0

El propósito del programa ( C++ ) es leer de teclado un array de 9x9 y decir si se trata de una tablero sudoku. Los requisitos para que un array sea sudoku son:

  • En la fila del tablero no puede haber números repetidos (sin contar el 0)
  • En cada columna no puede haber números repetidos (sin contar el 0)
  • Un tablero sudoku se divide en 9 regiones de 3x3. En una misma región no puede haber números repetidos. (adjunto ejemplo abajo).

Mi problema es que no se como hacer para comprobar esta ultima opción, ya que he tratado de hacerlo pero no funciona de la manera en que lo he hecho. (adjunto tb el código). Adjunto también la captura del error que me da.

#include <iostream>
#include <array>
#include <string>
using namespace std;

const int MAX=9;
typedef array <int,MAX> Filas;
typedef array <Filas,MAX> Sudoku;

void leersudoku (Sudoku& m)
{
    cout<<"Introduzca un tableto de sudoku de "<<MAX<<"x"<<MAX<<": "<<endl;
    for (int f=0; f<int(m.size()); ++f)
    {
        for (int c=0; c<int(m[f].size()); ++c)
        {
            cin>>m[f][c];
        }
    }
}

bool repeticionfila (const Sudoku& m, int f)
{
    bool control=true;

    for (int c=0; c<int(m[f].size())-1; ++c)
    {
         if ((m[f][c]==m[f][c+1])&&(m[f][c]!=0))
         {
             control=false;
         }
    }

    return control;
}

bool repeticioncolumna (const Sudoku& m, int c)
{
    bool control=true;

    for (int f=0; c<int(m[f].size())-1; ++f)
    {
         if ((m[f][c]==m[f+1][c])&&(m[f][c]!=0))
         {
             control=false;
         }
    }

    return control;
}

bool comprobarconresto (const Sudoku& m, int f, int c, int j)
{
    int cnt=m[f][c];
    bool control=true;

    for (int f=0; f<j; ++f)
    {
        for (int c=0; c<j; ++c)
        {
            if ((m[f][c]==cnt)&&(m[f][c]!=0))
            {
                control=false;
            }
        }
    }

    return control;
}

bool comprobarmatrizespecficica (const Sudoku& m, int f, int c, int j)
{
    bool control=true;

    for (int x=f; x<j; ++x)
    {
        for (int y=c; y<j; ++y)
        {
            if(comprobarconresto(m,x,y,j)==false)
            {
                control=false;
            }
        }
    }

    return control;
}


void comprobarvalido (const Sudoku& m)
{
    bool es_valido=true;
    //--------------------------------------
    for (int f=0; f<int(m.size()); ++f)
    {
        for (int c=0; c<int(m[f].size()); ++c)
        {
            if ((m[f][c]<0)||(m[f][c]>9))
            {
                es_valido=false;
            }
        }
    }
    //--------------------------------------
    for (int f=0; f<int(m.size()); ++f)
    {
        if (repeticionfila(m,f)==false)
        {
            es_valido=false;
        }
    }
    //--------------------------------------
    for (int c=0; c<int(m[0].size()); ++c)
    {
        if(repeticioncolumna(m,c)==false)
        {
            es_valido=false;
        }
    }
    //--------------------------------------
    for (int i=0; i<=2; ++i)
    {
        for (int f=3*i; f<int(m.size())-(6-3*i); ++f)
        {
            for (int c=3*i; c<int(m.size())-(6-3*i); ++c)
            {
                if(comprobarmatrizespecficica(m,f,c,int(m.size())-(6-3*i))==false)
                {
                    es_valido=false;
                }
            }
        }
    }
    //--------------------------------------

    if (es_valido==true)
    {
        cout<<"Este tablero de sudoku es valido"<<endl;
    }
    else
    {
        cout<<"Este tablero de sudoku no es valido"<<endl;
    }
}

int main ()
{
    Sudoku m;
    leersudoku(m);
    comprobarvalido(m);
}

introducir la descripción de la imagen aquí

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
j.j.3
  • 11
  • 1
  • No-respuesta: usar variables como `c`, `m` con nombres que no dicen nada hace mucho más difícil que modifiques, compartas o mantengas el código. Procura usar nombres más significativos. Lo otro, al validar cada fila estás preincrementando el valor de la variable en los ciclos; posiblemente ahí intentas leer la posición 9 que no existe (vas de 0 a 8) – Alfabravo Jan 31 '22 at 17:42

2 Answers2

0

Fíjate en la función repeticioncolumna:

bool repeticioncolumna(const Sudoku &m, int c) {
    bool control = true;

    for (int f = 0; c < int(m[f].size()) - 1; ++f) {
        if ((m[f][c] == m[f + 1][c]) && (m[f][c] != 0)) {
            control = false;
        }
    }

    return control;
}

Tienes la variable f, que incrementas cada vuelta. Hasta aquí bien. Pero, date cuenta de la condición que le colocaste al for:

    c < int(m[f].size()) - 1

Tu no cambias c en ningún momento. Entonces si inicialmente c es menor que 8, la condición del loop siempre sera verdadera, hasta que f llegue a 9, donde trataras de acceder a memoria fuera de m, que causara el error que tienes.

Notas

Estas no tienen que ver mucho con tu problema, pero si creo que te pueden ser de utilidad.

AdressSanitizer

Los problemas de memoria son difíciles de resolver, y puede tomar mucho tiempo. El origen de este lo encontré relativamente rápido porque use el ASAN, seria bueno que investigaras como utilizarlo en CodeBlocks (Que es lo que parece que usas).

En este caso el resultado de usar el ASAN es:

==1196==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x0038f4eff7e4 at pc 0x7ff6b9261438 bp 0x0038f4eff4e0 sp 0x0038f4eff528
READ of size 4 at 0x0038f4eff7e4 thread T0
    #0 0x7ff6b9261437 in repeticioncolumna(class std::array<class std::array<int, 9>, 9> const &, int) (C:\Users\pabsa\temp\a.exe+0x140001437)
    #1 0x7ff6b9261a6f in comprobarvalido(class std::array<class std::array<int, 9>, 9> const &) (C:\Users\pabsa\temp\a.exe+0x140001a6f)
    #2 0x7ff6b9261da8 in main (C:\Users\pabsa\temp\a.exe+0x140001da8)
    #3 0x7ff6b92dadbf in invoke_main d:\A01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
    #4 0x7ff6b92dadbf in __scrt_common_main_seh d:\A01\_work\6\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #5 0x7ffd8e8e7033  (C:\Windows\System32\KERNEL32.DLL+0x180017033)
    #6 0x7ffd8ea22650  (C:\Windows\SYSTEM32\ntdll.dll+0x180052650)

Address 0x0038f4eff7e4 is located in stack of thread T0 at offset 356 in frame
    #0 0x7ff6b9261c5f in main (C:\Users\pabsa\temp\a.exe+0x140001c5f)

  This frame has 1 object(s):
    [32, 356) 'm' <== Memory access at offset 356 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp, SEH and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (C:\Users\pabsa\temp\a.exe+0x140001437) in repeticioncolumna(class std::array<class std::array<int, 9>, 9> const &, int)

Nos dice que el error esta en repeticioncolumna, y que es en el acceso a la variable m.

for de rango

En C++11 tenemos los Range-based for loop. Estos pueden ayudarte a simplificar bastante tu código. Por ejemplo:

void leersudoku(Sudoku &m) {
    cout << "Introduzca un tableto de sudoku de " << MAX << "x" << MAX << ": "
         << endl;
    for (int f = 0; f < int(m.size()); ++f) {
        for (int c = 0; c < int(m[f].size()); ++c) {
            cin >> m[f][c];
        }
    }
}

Puede pasar a ser:

void leersudoku(Sudoku &m) {
    cout << "Introduzca un tableto de sudoku de " << MAX << "x" << MAX << ": "
         << endl;
    for (auto& fila : m) {
        for (auto& columna : fila) {
            cin >> columna;
        }
    }
}

Que es mucho mas limpio y fácil de leer. Ademas, con estos fijo que no te pasas del tamaño de m.

Convenciones

  • Nombres que sean todo mayúsculas tendrían que estar reservados para los macros. MAX tendría que ser en minúscula, y en vez de const tendría que ser constexpr.

  • Seria bueno que uses el snake_case consistentemente a lo largo de tu código, lo hará mas fácil de leer.

  • typedef es de C, si programas en C++ seria bueno que usaras using. En lugar de:

    typedef array<int, MAX>   Filas;
    typedef array<Filas, MAX> Sudoku;
    

    tendría que ser:

    using Filas = array<int, MAX>;
    using Sudoku = array<Filas, MAX>;
    

    Que es mas fácil de leer.

  • Usar using namespace std; no es la mejor idea. Mientras aprendes esta bien, pero no lo hagas una costumbre.

Pablochaches
  • 2,505
  • 1
  • 5
  • 21
  • @Pablochchaes Perfecto gracias. Ya lo he cambiado, sin embargo sigue sin funcionarme. Que podría hacer? – j.j.3 Jan 31 '22 at 23:08
0

Debes usar un desplazamiento (offset). Sabes que una sub-cuadrícula se desplazará (como mucho) tres posiciones en vertical y horizontal; así pues dado un Sudoku y unas coordenadas:

bool RepetidosEn3x3(const Sudoku &s, int fila, int columna)
{
    // Usaremos el std::set para buscar repetidos.
    std::set valores{};

    // Desplazamiento vertical
    for (int y = 0; y != 3; ++y)
    {
        // Desplazamiento horizontal
        for (int x = 0; x != 3; ++x)
        {
            auto insercion = valores.insert(s[fila + y][columna + x]);
            if (!insercion.second)
                return false;
        }
    }

    return true;
}
PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82