commit ee60562527a1b8d710fac00530e5d2e9ce7227dd Author: rto Date: Sat May 9 20:51:45 2026 +0200 Initial Commit diff --git a/su1.json b/su1.json new file mode 100644 index 0000000..2b067d0 --- /dev/null +++ b/su1.json @@ -0,0 +1,17 @@ +{ + "field" : [ + [0, 0, 7, 0, 8, 0, 0, 3, 0], + [0, 0, 0, 0, 0, 0, 2, 6, 0], + [0, 6, 0, 0, 1, 9, 0, 0, 7], + [0, 9, 2, 0, 4, 0, 0, 0, 0], + [8, 0, 0, 3, 0, 5, 0, 0, 4], + [0, 0, 0, 0, 9, 0, 3, 1, 0], + [6, 0, 0, 7, 5, 0, 0, 8, 0], + [0, 8, 9, 0, 0, 0, 0, 0, 0], + [0, 3, 0, 0, 6, 0, 5, 0, 0] + ], + "num_fields" : 9, + "row_block" : 3, + "col_block" : 3 +} + diff --git a/su_solver.py b/su_solver.py new file mode 100644 index 0000000..ce559b1 --- /dev/null +++ b/su_solver.py @@ -0,0 +1,188 @@ +import sys +import json +from pydantic import BaseModel +from itertools import count + +class Sudoku(BaseModel): + field: list[list[int]] + num_fields: int + row_block: int = 0 + col_block: int = 0 + +def inCol(array:list[list[int]], col: int, num: int) -> bool: + """Docstring for inRow. + :array: Representation of the sudoku array + :col: number of the column + :num: number searched in column + :returns: True if column contains num, otherwise False + """ + iter = (row[col] for row in array) + return (num in iter) + + +def inRow(array: list[list[int]], row: int, num: int) -> bool: + """Docstring for inRow. + :array: Representation of the sudoku array + :row: number of the row + :num: number searched in row + :returns: True if row contains num, otherwise False + """ + assert(len(array)> row) + return (num in array[row]) + +def inBlock( + array: list[list[int]], row: int, col: int, rb: int, cb: int, num: int +) -> bool: + """Docstring for inRow. + :array: Representation of the sudoku array + :row: vertical dimension of the block + :col: horizontal dimension of the block + :rb: row dimension of a block + :cb: col dimension of a block + :num: number searched in row + :returns: True if block contains num, otherwise False + """ + iter = (array[x][y] for x in range(rb*row, rb*(row+1)) for y in + range(cb*col, cb*(col+1))) + return (num in iter) + + +def readSudoku(filename: str) -> Sudoku | None: + """Docstring for readSudoku. + + :filename: filename of the soduko in json + :returns: list of list arrays + + """ + with open(filename, "r+") as fd: + try: + obj = json.load(fd) + except ValueError: + return None + a = Sudoku(**obj) + sq = ((x,x*x) for x in count()) + for x,y in sq: + if y == a.num_fields: + a.row_block = x + a.col_block = x + break + if y > a.num_fields: + break + rows = a.row_block + cols = a.col_block + assert (rows*cols == a.num_fields) + return a + +def find_positions(array: list[list[int]]) -> list[tuple[int,int]]: + """TODO: Docstring for find_positions. + + :array: TODO + :returns: TODO + + """ + return [(x,y) for x in range(len(array)) for y in range(len(array[x])) + if array[x][y] == 0 + ] +def fits(S: Sudoku, x, y): + """TODO: Docstring for fits. + + :Sudoku: TODO + :returns: TODO + + """ + b = S.field[x][y] + S.field[x][y] = 0 + cond = inRow(S.field, x, b) + cond = cond or inCol(S.field, y, b) + cond = cond or inBlock( + S.field, x // S.row_block, y // S.col_block, S.row_block, S.col_block, b + ) + # Reset variable + S.field[x][y] = b + return not cond + +def solve(Sudoku) -> bool: + num_pos = 0 + poss: list[tuple[int,int]] = find_positions(Sudoku.field) + while num_pos < len(poss) and num_pos >= 0: + x = poss[num_pos][0] + y = poss[num_pos][1] + Sudoku.field[x][y]+=1 + if Sudoku.field[x][y] > Sudoku.num_fields: + num_pos -= 1 + Sudoku.field[x][y] = 0 + continue + elif fits(Sudoku, x, y) : + num_pos+= 1 + else: + continue + + return num_pos == len(poss) + +def check_array(Sudoku : Sudoku) -> bool: + num_rows = len(Sudoku.field) + if num_rows != Sudoku.num_fields: + return False + for row in range(num_rows): + num_cols = len(Sudoku.field[row]) + if num_cols != Sudoku.num_fields: + return False + return True + + +def main(argv: list[str]) -> None: + if len(argv) < 1 : + return + filename = argv[1] + a = readSudoku(filename) + if a is None: + print("Read failed") + return + s : Sudoku = a + if not check_array(s) : + sys.exit(-1) + if not solve(s): + print("Not solvable") + else: + for row in s.field: + for col in row: + print(col, end=" ") + print() + return + + +def initSudoku() -> Sudoku: + field = [ + [1, 2, 3, 4, 5, 0, 7, 8, 9], + [4, 5, 0, 7, 8, 9, 1, 2, 3], + [7, 8, 9, 1, 2, 3, 4, 5, 6], + [0] * 9, + [0] * 9, + [0] * 9, + [1, 2, 3, 4, 5, 0, 7, 8, 9], + [4, 5, 0, 7, 8, 9, 1, 2, 3], + [7, 8, 9, 1, 2, 3, 4, 5, 6], + ] + return Sudoku(field=field, num_fields=9, row_block=3, col_block=3) +def test(): + s = initSudoku() + assert inBlock(s.field, 0, 1, 3, 3, 6) == False + assert inBlock(s.field, 0, 2, 3, 3, 6) == True + assert inBlock(s.field, 0, 0, 3, 3, 6) == False + assert inBlock(s.field, 2, 0, 3, 3, 6) == False + assert inBlock(s.field, 2, 1, 3, 3, 6) == False + assert inBlock(s.field, 2, 2, 3, 3, 6) == True + assert inRow(s.field, 0 ,6) == False + assert inRow(s.field, 1 ,6) == False + assert inRow(s.field, 2 ,6) == True + assert inRow(s.field, 6 ,6) == False + assert inRow(s.field, 7 ,6) == False + assert inRow(s.field, 8 ,6) == True + assert inCol(s.field, 0, 6) == False + assert inCol(s.field, 1, 6) == False + assert inCol(s.field, 2, 6) == False + assert inCol(s.field, 7, 6) == False + assert inCol(s.field, 8, 6) == True + +if __name__ == "__main__": + main(sys.argv)