Sudoku solver StackOverflowError

0

Witajcie!

Chciałem sobie napisać własny sudoku solver.
I tu na początku ważna uwaga - nie chcę korzystać z gotowych rozwiązań. Nie zrozumcie mnie źle, po prostu chciałbym trochę podszkolić z wprowadzania algorytmów w życie.

Korzystam z Wikipediowego algorytmu backtrack

 Initialize 2D array with 81 empty grids (nx = 9, ny = 9)
 Fill in some empty grid with the known values
 Make an original copy of the array
 Start from top left grid (nx = 0, ny = 0), check if grid is empty
 if (grid is empty) {
   assign the empty grid with values (i)
   if (no numbers exists in same rows & same columns same as (i) & 3x3 square (i) is currently in)
     fill in the number
   if (numbers exists in same rows | same columns same as (i) | 3x3 square (i) is currently in)
     discard (i) and repick other values (i++)
 }
 else {
   while (nx < 9) {
     Proceed to next row grid(nx++, ny)
     if (nx equals 9) {
       reset nx = 1
       proceed to next column grid(nx,ny++)
       if (ny equals 9) {
         print solution
       }
     }
   }
 }

Pisząc kod staram się zrozumieć jak działa owy algorytm. Oto kod mojej klasy:

package com.burdzi0.solver.sudoku;

public class Solve {
	
	int sudoku[][] = new int[9][9];
	int current[][] = sudoku;
	
	// Example for debugging
	void fill() {
		sudoku[0][0] = 5;
    	sudoku[0][1] = 3;
    	sudoku[0][4] = 7;
    	
    	sudoku[1][0] = 6;
    	sudoku[1][3] = 1;
    	sudoku[1][4] = 9;
    	sudoku[1][5] = 5;
    	
    	sudoku[2][1] = 9;
    	sudoku[2][2] = 8;
    	sudoku[2][7] = 6;
    	sudoku[2][7] = 6;
    	
    	sudoku[3][0] = 8;
    	sudoku[3][4] = 6;
    	sudoku[3][8] = 3;
    	
    	sudoku[4][0] = 4;
    	sudoku[4][3] = 8;
    	sudoku[4][5] = 3;
    	sudoku[4][8] = 1;
    	
    	sudoku[5][0] = 7;
    	sudoku[5][4] = 2;
    	sudoku[5][8] = 6;
    	
    	sudoku[6][1] = 6;
    	sudoku[6][6] = 2;
    	sudoku[6][7] = 8;

    	sudoku[7][3] = 4;
    	sudoku[7][4] = 1;
    	sudoku[7][5] = 9;
    	sudoku[7][8] = 5;
    	
    	sudoku[8][4] = 8;
    	sudoku[8][7] = 7;
    	sudoku[8][8] = 9;
	}
	
	// Checks if the number is already used in the given row
	boolean checkIfNumberIsInRow(int number, int rowIndex) {
		for (int i = 0; i < 9; i++) {
			if (current[rowIndex][i] == number) return true;
		}
		return false;
	}
	
	// Checks if the number is already used in the given column
	boolean checkIfNumberIsInColumn(int number, int j) {
		for (int i = 0; i < 9; i++) {
			if (current[i][j] == number) return true;
		}
		return false;
	}
	
	// Defines the square that is needed to be searched in
	boolean checkSquare(int number, int rowIndex, int columnIndex) {
		int x,y;
		if (rowIndex < 2) {
			x = 0;
		} else if (rowIndex < 5) {
			x = 1;
		} else {
			x = 2;
		}
		
		if (columnIndex < 2) {
			y = 0;
		} else if (columnIndex < 5) {
			y = 1;
		} else {
			y = 2;
		}
		
		for (byte i = 0; i < 3; i++) {
			for (byte j = 0; j < 3; j++) {
				if (current[3*x + i][3*y + j] == number) return true;
			}
		}
		return false;
	}
	
	void printTable() {
		for (int i = 0; i < 9; i++) {
			for (int j = 0; j < 9; j++) {
				System.out.print(sudoku[i][j] + " ");
			}
			System.out.println();
		}
	}
	
	void mainSearch(int row, int column, int number) {
		if (current[row][column] == 0) {
			if (!checkIfNumberIsInRow(number, row) && !checkIfNumberIsInColumn(number, column) && !checkSquare(number, row, column)) {
				current[row][column] = number;
			} else {
				mainSearch(row, column, number + 1);
			}
		} else {
			while (row < 9) {
				mainSearch(row, column, number);
				if (row == 8) {
					row = 0;
					mainSearch(row, column++, number);
					if (column == 8) {
						printTable();
					}
				}
			}
		}
	}
}

Widziałem już ten temat i wiele linków w necie, lecz uparłem się żeby zrobić to bez korzystania z gotowego kodu, więc zapytam na forum.
Gdzie w mojej metodzie mainSearch popełniłem błąd (stawiam, że stąd pojawia się StackOverflowError)?
Jak powinna wyglądać i dlaczego?
Moglibyście mnie nakierunkować, proszę?

0

Na pierwszy rzut oka wydaje mi się że pominąłeś inkrementację.


void mainSearch(int row, int column, int number) {
        if (current[row][column] == 0) {
            if (!checkIfNumberIsInRow(number, row) && !checkIfNumberIsInColumn(number, column) && !checkSquare(number, row, column)) {
                current[row][column] = number;
            } else {
                mainSearch(row, column, number + 1);
            }
        } else {
            while (row < 9) {
                mainSearch(row, column, number);   <-------    chyba powinno być row++   -----------------------
                if (row == 8) {
                    row = 0;
                    mainSearch(row, column++, number);
                    if (column == 8) {
                        printTable();
                    }
                }
            }
        }
    }
}

0

Nawet po zaproponowanej przez Ciebie zmianie @Darth Bane dostaję StackOverflowError.
Jakieś inne pomysły? :)

0

Zmieniłem kod na taki o:

package com.burdzi0.solver.sudoku;

public class Solve {
	
	int sudoku[][];
	int cell[] = new int[2];
	int current[][];
	
	public Solve() {
		fill();
		current = sudoku;
	}
	
	// Example for debugging
	void fill() {
		sudoku = new int[][] { { 5, 3, 0, 0, 7, 0, 0, 0, 0 },
			{ 6, 0, 0, 1, 9, 5, 0, 0, 0 }, { 0, 9, 8, 0, 0, 0, 0, 6, 0 },
			{ 8, 0, 0, 0, 6, 0, 0, 0, 3 }, { 4, 0, 0, 8, 0, 3, 0, 0, 1 },
			{ 7, 0, 0, 0, 2, 0, 0, 0, 6 }, { 0, 6, 0, 0, 0, 0, 2, 8, 0 },
{ 0, 0, 0, 4, 1, 9, 0, 0, 5 }, { 0, 0, 0, 0, 8, 0, 0, 7, 9 } };
	}
	
	// Checks if the number is already used in the given row
	boolean checkIfNumberIsInRow(int number, int rowIndex) {
		for (int i = 0; i < 9; i++) {
			if (current[rowIndex][i] == number) return true;
		}
		return false;
	}
	
	// Checks if the number is already used in the given column
	boolean checkIfNumberIsInColumn(int number, int j) {
		for (int i = 0; i < 9; i++) {
			if (current[i][j] == number) return true;
		}
		return false;
	}
	
	// Defines the square that is needed to be searched in
	boolean checkSquare(int number, int rowIndex, int columnIndex) {
		int x,y;
		if (rowIndex < 2) {
			x = 0;
		} else if (rowIndex < 5) {
			x = 1;
		} else {
			x = 2;
		}
		
		if (columnIndex < 2) {
			y = 0;
		} else if (columnIndex < 5) {
			y = 1;
		} else {
			y = 2;
		}
		
		for (byte i = 0; i < 3; i++) {
			for (byte j = 0; j < 3; j++) {
				if (current[3*x + i][3*y + j] == number) return true;
			}
		}
		return false;
	}
	
	// Checks if given number can be placed in given location
	boolean isSafe(int number, int row, int column) {
		return (!checkIfNumberIsInRow(number, row) && !checkIfNumberIsInColumn(number, column) && !checkSquare(number, row, column));
	}
	
	// Prints the table
	void printTable() {
		for (int i = 0; i < 9; i++) {
			for (int j = 0; j < 9; j++) {
				System.out.print(current[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println("----------------------------");
	}
	
	int[] findBlankCell() {
		for (int i = 0; i < 9; i++) {
			for (int j = 0; j < 9; j++) {
				if (current[i][j] == 0) {
					cell[0] = i;
					cell[1] = j;
					return cell;
				}
			}
		}
		cell[0] = -1;
		cell[1] = -1;
		return cell;
	}
	
	boolean mainSearch() {
		printTable();
		int row, column;
		int[] blankCell = findBlankCell();
		row = blankCell[0];
		column = blankCell[1];
		
		if (row == -1) {
			return true;
		}
		
		for (int i = 1; i <= 9; i++) {
			if (isSafe(i, row, column)) {
				current[row][column] = i;
				
				if (mainSearch()) {
					return true;
				}
				
				current[row][column] = 0;
			}
		}
		return false;
	}
}

I coś ruszyło. Jednak ciągle nie rozwiązuje sudoku tak jak powinno, przy sprawdzonym przykładzie nie znajduje rozwiązania.
Wzorowałem się tym kodem (chęć ogarnięcia algorytmu szlag trafił). Jest ktoś w stanie mi pomóc? :|

0

Brzydki ten wasz sudoku solver.
Ładny powinien wyglądać tak:

 
static boolean solve(int i, int j, int[][] cells) {
        if (i == 9) {
            i = 0;
            if (++j == 9)
                return true;
        }
        if (cells[i][j] != 0)  // skip filled cells
            return solve(i+1,j,cells);

        for (int val = 1; val <= 9; ++val) {
            if (legal(i,j,val,cells)) {
                **cells[i][j] = val;**
                if (solve(i+1,j,cells))
                    return true;
            }
        }
        **cells[i][j] = 0; // reset on backtrack**
        return false;
    }

Cały kod:
https://bob-carpenter.github.io/games/sudoku/java_sudoku.html

1

Metoda checkSquare niepoprawnie wydziela pola 3x3.

0

Kolejne filmiki na temat działania stosu wywołań:


1 użytkowników online, w tym zalogowanych: 0, gości: 1