Initial Commit
This commit is contained in:
+188
@@ -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)
|
||||
Reference in New Issue
Block a user