Advertisement
Guest User

Untitled

a guest
Jul 21st, 2017
280
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.48 KB | None | 0 0
  1. rows = 'ABCDEFGHI'
  2. cols = '123456789'
  3.  
  4. assignments = []
  5. row_units = [cross(r, cols) for r in rows]
  6. column_units = [cross(rows, c) for c in cols]
  7. square_units = [cross(rs, cs) for rs in ('ABC', 'DEF', 'GHI') for cs in ('123', '456', '789')]
  8. unitlist = row_units + column_units + square_units
  9. units = dict((s, [u for u in unitlist if s in u]) for s in boxes)
  10. peers = dict((s, set(sum(units[s], [])) - set([s])) for s in boxes)
  11. boxes = cross(rows, cols)
  12. diagonal_units_one = ['A1', 'B2', 'C3', 'D4', 'E5', 'F6', 'G7', 'H8', 'I9']
  13. diagonal_units_two = ['A9', 'B8', 'C7', 'D6', 'E5', 'F4', 'G3', 'H2', 'I1']
  14.  
  15.  
  16. def cross(a, b):
  17. "Cross product of elements in A and elements in B."
  18. return [s + t for s in a for t in b]
  19.  
  20.  
  21. def assign_value(values, box, value):
  22. """
  23. Please use this function to update your values dictionary!
  24. Assigns a value to a given box. If it updates the board record it.
  25. """
  26. # Don't waste memory appending actions that don't actually change any values
  27. if values[box] == value:
  28. return values
  29.  
  30. values[box] = value
  31. if len(value) == 1:
  32. assignments.append(values.copy())
  33. return values
  34.  
  35.  
  36. def assign_value_wrapper(values, box, value):
  37. if box in diagonal_units_one:
  38. diag_values = [diag for diag in diagonal_units_one if diagonal_units_one[]]
  39. return values
  40.  
  41.  
  42. def grid_values(grid):
  43. """
  44. Convert grid into a dict of {square: char} with '123456789' for empties.
  45. Args:
  46. grid(string) - A grid in string form.
  47. Returns:
  48. A grid in dictionary form
  49. Keys: The boxes, e.g., 'A1'
  50. Values: The value in each box, e.g., '8'. If the box has no value, then the value will be '123456789'.
  51. """
  52. chars = []
  53. digits = '123456789'
  54. for c in grid:
  55. if c in digits:
  56. chars.append(c)
  57. if c == '.':
  58. chars.append(digits)
  59. assert len(chars) == 81
  60. return dict(zip(boxes, chars))
  61.  
  62.  
  63. def display(values):
  64. """
  65. Display the values as a 2-D grid.
  66. Args:
  67. values(dict): The sudoku in dictionary form
  68. """
  69. width = 1 + max(len(values[s]) for s in boxes)
  70. line = '+'.join(['-' * (width * 3)] * 3)
  71. for r in rows:
  72. print(''.join(values[r + c].center(width) + ('|' if c in '36' else '')
  73. for c in cols))
  74. if r in 'CF': print(line)
  75. return
  76.  
  77.  
  78. def eliminate(values):
  79. solved_values = [box for box in values.keys() if len(values[box]) == 1]
  80. for box in solved_values:
  81. digit = values[box]
  82. for peer in peers[box]:
  83. #values[peer] = values[peer].replace(digit, '')
  84. assign_value(values, peer, values[peer].replace(digit, ''))
  85. return values
  86.  
  87.  
  88. def only_choice(values):
  89. for unit in unitlist:
  90. for digit in '123456789':
  91. dplaces = [box for box in unit if digit in values[box]]
  92. if len(dplaces) == 1:
  93. #values[dplaces[0]] = digit
  94. assign_value(values, dplaces[0], digit)
  95. return values
  96.  
  97.  
  98. def naked_twins(values):
  99. """Eliminate values using the naked twins strategy.
  100. Args:
  101. values(dict): a dictionary of the form {'box_name': '123456789', ...}
  102.  
  103. Returns:
  104. the values dictionary with the naked twins eliminated from peers.
  105. """
  106. # Find all instances of naked twins
  107. # Eliminate the naked twins as possibilities for their peers
  108. row_index_map = dict(zip(rows, '012345678'))
  109. for box in boxes:
  110. update_twins(values, box, [i for i in column_units[int(box[1]) - 1]])
  111. update_twins(values, box, [i for i in row_units[int(row_index_map[box[0]])]])
  112. return values
  113.  
  114.  
  115. def twin_present(values, source_twin, targets):
  116. twin_totals = {}
  117. for possible_twin in targets:
  118. if values[possible_twin] == values[source_twin]:
  119. twin_totals[possible_twin] = 1 if possible_twin not in twin_totals else 1 + twin_totals[possible_twin]
  120.  
  121. for twin in twin_totals:
  122. if len(values[twin]) == twin_totals[twin] + 1:
  123. return True
  124. return False
  125.  
  126.  
  127. def update_twins(values, source_twin, possible_twins):
  128. if source_twin in possible_twins:
  129. possible_twins.remove(source_twin)
  130.  
  131. if twin_present(values, source_twin, possible_twins):
  132. remove_twin_from_targets(values, source_twin, possible_twins)
  133.  
  134.  
  135. def remove_twin_from_targets(values, twin, targets):
  136. for target in targets:
  137. if values[twin] != values[target]:
  138. for digit in values[twin]:
  139. assign_value(values, target, values[target].replace(digit, ''))
  140.  
  141.  
  142. def reduce_puzzle(values):
  143. solved_values = [box for box in values.keys() if len(values[box]) == 1]
  144. stalled = False
  145. while not stalled:
  146. solved_values_before = len([box for box in values.keys() if len(values[box]) == 1])
  147. values = eliminate(values)
  148. values = only_choice(values)
  149. values = naked_twins(values)
  150. solved_values_after = len([box for box in values.keys() if len(values[box]) == 1])
  151. stalled = solved_values_before == solved_values_after
  152. if len([box for box in values.keys() if len(values[box]) == 0]):
  153. return False
  154. return values
  155.  
  156.  
  157. def search(values):
  158. "Using depth-first search and propagation, try all possible values."
  159. # First, reduce the puzzle using the previous function
  160. values = reduce_puzzle(values)
  161. if values is False:
  162. return False ## Failed earlier
  163. if all(len(values[s]) == 1 for s in boxes):
  164. return values ## Solved!
  165. # Choose one of the unfilled squares with the fewest possibilities
  166. n, s = min((len(values[s]), s) for s in boxes if len(values[s]) > 1)
  167. # Now use recurrence to solve each one of the resulting sudokus, and
  168. for value in values[s]:
  169. new_sudoku = values.copy()
  170. new_sudoku[s] = value
  171. attempt = search(new_sudoku)
  172. if attempt:
  173. return attempt
  174.  
  175.  
  176. def solve(grid):
  177. """
  178. Find the solution to a Sudoku grid.
  179. Args:
  180. grid(string): a string representing a sudoku grid.
  181. Example: '2.............62....1....7...6..8...3...9...7...6..4...4....8....52.............3'
  182. Returns:
  183. The dictionary representation of the final sudoku grid. False if no solution exists.
  184. """
  185. return search(grid_values(grid))
  186.  
  187. if __name__ == '__main__':
  188. diag_sudoku_grid = '2.............62....1....7...6..8...3...9...7...6..4...4....8....52.............3'
  189. display(solve(diag_sudoku_grid))
  190.  
  191. try:
  192. from visualize import visualize_assignments
  193.  
  194. visualize_assignments(assignments)
  195.  
  196. except SystemExit:
  197. pass
  198. except:
  199. print('We could not visualize your board due to a pygame issue. Not a problem! It is not a requirement.')
  200.  
  201.  
  202.  
  203.  
  204.  
  205.  
  206.  
  207.  
  208.  
  209. ****************************************
  210. {
  211. "tests": [
  212. {
  213. "description": "the naked_twins method applies the 'naked twins' heuristic.",
  214. "traceback": "AssertionError: False is not true : Your naked_twins function missed a set of naked_twins.\n",
  215. "message": "Your naked_twins function missed a set of naked_twins.",
  216. "advice": "Try the following example and see what happens:\n{\"G7\": \"1234568\", \"G6\": \"9\", \"G5\": \"35678\", \"G4\": \"23678\", \"G3\": \"245678\", \"G2\": \"123568\", \"G1\": \"1234678\", \"G9\": \"12345678\", \"G8\": \"1234567\", \"C9\": \"13456\", \"C8\": \"13456\", \"C3\": \"4678\", \"C2\": \"68\", \"C1\": \"4678\", \"C7\": \"13456\", \"C6\": \"368\", \"C5\": \"2\", \"A4\": \"5\", \"A9\": \"2346\", \"A8\": \"2346\", \"F1\": \"123689\", \"F2\": \"7\", \"F3\": \"25689\", \"F4\": \"23468\", \"F5\": \"1345689\", \"F6\": \"23568\", \"F7\": \"1234568\", \"F8\": \"1234569\", \"F9\": \"1234568\", \"B4\": \"46\", \"B5\": \"46\", \"B6\": \"1\", \"B7\": \"7\", \"E9\": \"12345678\", \"B1\": \"5\", \"B2\": \"2\", \"B3\": \"3\", \"C4\": \"9\", \"B8\": \"8\", \"B9\": \"9\", \"I9\": \"1235678\", \"I8\": \"123567\", \"I1\": \"123678\", \"I3\": \"25678\", \"I2\": \"123568\", \"I5\": \"35678\", \"I4\": \"23678\", \"I7\": \"9\", \"I6\": \"4\", \"A1\": \"2468\", \"A3\": \"1\", \"A2\": \"9\", \"A5\": \"3468\", \"E8\": \"12345679\", \"A7\": \"2346\", \"A6\": \"7\", \"E5\": \"13456789\", \"E4\": \"234678\", \"E7\": \"1234568\", \"E6\": \"23568\", \"E1\": \"123689\", \"E3\": \"25689\", \"E2\": \"123568\", \"H8\": \"234567\", \"H9\": \"2345678\", \"H2\": \"23568\", \"H3\": \"2456789\", \"H1\": \"2346789\", \"H6\": \"23568\", \"H7\": \"234568\", \"H4\": \"1\", \"H5\": \"35678\", \"D8\": \"1235679\", \"D9\": \"1235678\", \"D6\": \"23568\", \"D7\": \"123568\", \"D4\": \"23678\", \"D5\": \"1356789\", \"D2\": \"4\", \"D3\": \"25689\", \"D1\": \"123689\"}",
  217. "result": "F",
  218. "rubric_item_id": "5531"
  219. },
  220. {
  221. "description": "the solve method produces a valid solution.",
  222. "traceback": "AssertionError: False is not true : The solution was not valid for diagonal sudoku.\n",
  223. "message": "The solution was not valid for diagonal sudoku.",
  224. "advice": "Try your code on the following example:\n9.1....8.8.5.7..4.2.4....6...7......5..............83.3..6......9................\n",
  225. "result": "F",
  226. "rubric_item_id": "5530"
  227. },
  228. {
  229. "description": "the README has been modified.",
  230. "traceback": "",
  231. "message": null,
  232. "advice": null,
  233. "result": ".",
  234. "rubric_item_id": null
  235. }
  236. ]
  237. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement