sudoku_solver.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # Sudoku: 9x9 Spielfeld. Auf jedem Feld ist eine Liste mit möglichen Werten.
  2. # Enthält diese Liste nur einen Wert, ist das der gesuchte Wert für das Feld.
  3. import logging
  4. logging.basicConfig(level=logging.INFO) # change to DEBUG if needed
  5. gamefield = [
  6. [[],[],[], [5],[],[7], [],[],[]],
  7. [[],[4],[], [2],[6],[3], [],[],[]],
  8. [[1],[],[7], [4],[],[], [],[],[]],
  9. [[3],[6],[], [],[],[], [],[4],[5]],
  10. [[],[],[2], [],[5],[], [7],[],[]],
  11. [[7],[9],[], [],[],[], [],[6],[2]],
  12. [[],[],[], [],[],[9], [4],[],[1]],
  13. [[],[],[], [1],[3],[4], [],[9],[]],
  14. [[],[],[], [6],[],[5], [],[],[]]
  15. ];
  16. def fill_empty_fields_with_possible_values():
  17. logging.debug("fill_empty_fields_with_possible_values")
  18. y = 0
  19. for row in gamefield:
  20. x = 0
  21. for field in row:
  22. if(len(field) == 0):
  23. field = [1,2,3,4,5,6,7,8,9]
  24. row[x] = field
  25. x += 1
  26. gamefield[y] = row
  27. y += 1
  28. def print_sudoku():
  29. print('')
  30. y = 0
  31. for row in gamefield:
  32. x = 0
  33. for field in row:
  34. if(len(field) == 1):
  35. print(field, end='')
  36. else:
  37. print('[ ]', end='')
  38. x += 1
  39. if(x > 0 and x < len(row) and x%3 == 0):
  40. print(" | ", end='')
  41. y += 1
  42. if(y > 0 and y < len(gamefield) and y%3 == 0):
  43. print('\n---------------------------------', end='')
  44. print('')
  45. print('')
  46. def solve_sudoku():
  47. logging.debug("solve_sudoku")
  48. # always repeat everything from the beginning if something is found
  49. while True:
  50. if not solve_by_cell_check():
  51. continue
  52. if not solve_by_unit_check():
  53. continue
  54. # break when every algorithm runs through without any findings
  55. break
  56. def solve_by_unit_check():
  57. '''checks each row/cell/3x3 if 1-9 is the only possible value for this unit'''
  58. logging.debug("solve_by_unit_check_inner")
  59. # check for each row. check for 1 to 9 if it is the only possible value for this unit. if yes, set. it and restart
  60. for row in gamefield:
  61. field_changed = False
  62. v = 1
  63. while True:
  64. count = 0
  65. foo_field = []
  66. for field in row:
  67. if v in field:
  68. count += 1
  69. foo_field = field
  70. if count == 1 and len(foo_field) > 1:
  71. foo_field.clear
  72. foo_field.append(v)
  73. field_changed = True
  74. break
  75. v += 1
  76. if v == 9:
  77. break
  78. # check for each column. check for 1 to 9 if it is the only possible value for this unit. if yes, set. it and restart
  79. # TODO
  80. # check for each 3x3 field. check for 1 to 9 if it is the only possible value for this unit. if yes, set. it and restart
  81. # TODO
  82. # return True when algorithm runs through without any findings
  83. # return False when algorithm found something
  84. return not field_changed
  85. def solve_by_cell_check():
  86. '''checks each cell to remove possible values'''
  87. logging.debug("solve_by_cell_check_inner")
  88. # for each cell
  89. y = 0
  90. for row in gamefield:
  91. x = 0
  92. logging.debug(f"check at {x}/{y}")
  93. for field in row:
  94. # if value is already defined, check next value
  95. if(len(field) == 1):
  96. x += 1
  97. continue
  98. # for each possible value
  99. field_changed = False
  100. for possible_value in field[:]:
  101. logging.debug(f"check value {possible_value} in {field}")
  102. # check if the value already exists in row/column/3x3 field
  103. if(check_row_for_value(y, possible_value) or
  104. check_column_for_value(x, possible_value) or
  105. check_3x3_for_value(x, y, possible_value)):
  106. # if yes, remove it from possible values
  107. remove_from_possible_values(x, y, possible_value)
  108. field_changed = True
  109. # if possible values for a field reaches 1 -> break
  110. if(len(field) == 1):
  111. break
  112. # return False because algorithm found something
  113. if field_changed:
  114. logging.debug(f"solve_by_cell_check_inner found something and changed field -> restart")
  115. return False
  116. x += 1
  117. y += 1
  118. # return True when algorithm runs through without any findings
  119. logging.debug(f"solve_by_cell_check_inner runs through without any findings")
  120. return True
  121. def remove_from_possible_values(x, y, value):
  122. logging.debug(f"remove {value} from possible values at {x}/{y}")
  123. gamefield[y][x].remove(value)
  124. # i do not explicitely skip the field itself because it is skipped by len check
  125. def check_row_for_value(rowIndex, value):
  126. logging.debug(f"check row {rowIndex} for {value}")
  127. for field in gamefield[rowIndex]:
  128. if len(field) == 1:
  129. if field[0] == value:
  130. return True
  131. return False
  132. def check_column_for_value(columnIndex, value):
  133. logging.debug(f"check row {columnIndex} for {value}")
  134. rowIndex = 0
  135. while rowIndex < len(gamefield):
  136. field = get_field_by_coord(columnIndex, rowIndex)
  137. if len(field) == 1:
  138. if field[0] == value:
  139. return True
  140. rowIndex += 1
  141. return False
  142. def check_3x3_for_value(x, y, value):
  143. logging.debug(f"check 3x3 at {x}/{y} for {value}")
  144. (block_x, block_y) = get_3x3_start_coords(x, y)
  145. block_x_end = block_x + 2
  146. block_y_end = block_y + 2
  147. while block_y <= block_y_end:
  148. while block_x <= block_x_end:
  149. # check for each slot in 3x3 if there is already a fixed number (len == 1)
  150. field = get_field_by_coord(block_x, block_y)
  151. if len(field) == 1:
  152. if field[0] == value:
  153. return True
  154. block_x += 1
  155. block_y += 1
  156. block_x -= 3
  157. return False
  158. def get_3x3_start_coords(x, y):
  159. while(x % 3 != 0):
  160. x -= 1
  161. while(y % 3 != 0):
  162. y -= 1
  163. return (x, y)
  164. def get_field_by_coord(x, y):
  165. return gamefield[y][x]
  166. def main():
  167. fill_empty_fields_with_possible_values()
  168. print_sudoku()
  169. solve_sudoku()
  170. print_sudoku()
  171. main()