|
| 1 | +# author ad71 |
| 2 | +from tkinter import * |
| 3 | +from functools import partial |
| 4 | + |
| 5 | +import time |
| 6 | +import random |
| 7 | +import numpy as np |
| 8 | + |
| 9 | +import sys |
| 10 | +import os.path |
| 11 | +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
| 12 | + |
| 13 | +from search import astar_search, EightPuzzle |
| 14 | +import utils |
| 15 | + |
| 16 | +root = Tk() |
| 17 | + |
| 18 | +state = [1, 2, 3, 4, 5, 6, 7, 8, 0] |
| 19 | +puzzle = EightPuzzle(tuple(state)) |
| 20 | +solution = None |
| 21 | + |
| 22 | +b = [None]*9 |
| 23 | + |
| 24 | +# TODO: refactor into OOP, remove global variables |
| 25 | + |
| 26 | +def scramble(): |
| 27 | + """ Scrambles the puzzle starting from the goal state """ |
| 28 | + |
| 29 | + global state |
| 30 | + global puzzle |
| 31 | + possible_actions = ['UP', 'DOWN', 'LEFT', 'RIGHT'] |
| 32 | + scramble = [] |
| 33 | + for _ in range(60): |
| 34 | + scramble.append(random.choice(possible_actions)) |
| 35 | + |
| 36 | + for move in scramble: |
| 37 | + if move in puzzle.actions(state): |
| 38 | + state = list(puzzle.result(state, move)) |
| 39 | + puzzle = EightPuzzle(tuple(state)) |
| 40 | + create_buttons() |
| 41 | + |
| 42 | +def solve(): |
| 43 | + """ Solves the puzzle using astar_search """ |
| 44 | + |
| 45 | + return astar_search(puzzle).solution() |
| 46 | + |
| 47 | +def solve_steps(): |
| 48 | + """ Solves the puzzle step by step """ |
| 49 | + |
| 50 | + global puzzle |
| 51 | + global solution |
| 52 | + global state |
| 53 | + solution = solve() |
| 54 | + print(solution) |
| 55 | + |
| 56 | + for move in solution: |
| 57 | + state = puzzle.result(state, move) |
| 58 | + create_buttons() |
| 59 | + root.update() |
| 60 | + root.after(1, time.sleep(0.75)) |
| 61 | + |
| 62 | +def exchange(index): |
| 63 | + """ Interchanges the position of the selected tile with the zero tile under certain conditions """ |
| 64 | + |
| 65 | + global state |
| 66 | + global solution |
| 67 | + global puzzle |
| 68 | + zero_ix = list(state).index(0) |
| 69 | + actions = puzzle.actions(state) |
| 70 | + current_action = '' |
| 71 | + i_diff = index//3 - zero_ix//3 |
| 72 | + j_diff = index%3 - zero_ix%3 |
| 73 | + if i_diff == 1: |
| 74 | + current_action += 'DOWN' |
| 75 | + elif i_diff == -1: |
| 76 | + current_action += 'UP' |
| 77 | + |
| 78 | + if j_diff == 1: |
| 79 | + current_action += 'RIGHT' |
| 80 | + elif j_diff == -1: |
| 81 | + current_action += 'LEFT' |
| 82 | + |
| 83 | + if abs(i_diff) + abs(j_diff) != 1: |
| 84 | + current_action = '' |
| 85 | + |
| 86 | + if current_action in actions: |
| 87 | + b[zero_ix].grid_forget() |
| 88 | + b[zero_ix] = Button(root, text=f'{state[index]}', width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, zero_ix)) |
| 89 | + b[zero_ix].grid(row=zero_ix//3, column=zero_ix%3, ipady=40) |
| 90 | + b[index].grid_forget() |
| 91 | + b[index] = Button(root, text=None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, index)) |
| 92 | + b[index].grid(row=index//3, column=index%3, ipady=40) |
| 93 | + state[zero_ix], state[index] = state[index], state[zero_ix] |
| 94 | + puzzle = EightPuzzle(tuple(state)) |
| 95 | + |
| 96 | +def create_buttons(): |
| 97 | + """ Creates dynamic buttons """ |
| 98 | + |
| 99 | + # TODO: Find a way to use grid_forget() with a for loop for initialization |
| 100 | + b[0] = Button(root, text=f'{state[0]}' if state[0] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 0)) |
| 101 | + b[0].grid(row=0, column=0, ipady=40) |
| 102 | + b[1] = Button(root, text=f'{state[1]}' if state[1] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 1)) |
| 103 | + b[1].grid(row=0, column=1, ipady=40) |
| 104 | + b[2] = Button(root, text=f'{state[2]}' if state[2] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 2)) |
| 105 | + b[2].grid(row=0, column=2, ipady=40) |
| 106 | + b[3] = Button(root, text=f'{state[3]}' if state[3] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 3)) |
| 107 | + b[3].grid(row=1, column=0, ipady=40) |
| 108 | + b[4] = Button(root, text=f'{state[4]}' if state[4] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 4)) |
| 109 | + b[4].grid(row=1, column=1, ipady=40) |
| 110 | + b[5] = Button(root, text=f'{state[5]}' if state[5] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 5)) |
| 111 | + b[5].grid(row=1, column=2, ipady=40) |
| 112 | + b[6] = Button(root, text=f'{state[6]}' if state[6] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 6)) |
| 113 | + b[6].grid(row=2, column=0, ipady=40) |
| 114 | + b[7] = Button(root, text=f'{state[7]}' if state[7] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 7)) |
| 115 | + b[7].grid(row=2, column=1, ipady=40) |
| 116 | + b[8] = Button(root, text=f'{state[8]}' if state[8] != 0 else None, width=6, font=('Helvetica', 40, 'bold'), command=partial(exchange, 8)) |
| 117 | + b[8].grid(row=2, column=2, ipady=40) |
| 118 | + |
| 119 | +def create_static_buttons(): |
| 120 | + """ Creates scramble and solve buttons """ |
| 121 | + |
| 122 | + scramble_btn = Button(root, text='Scramble', font=('Helvetica', 30, 'bold'), width=8, command=partial(init)) |
| 123 | + scramble_btn.grid(row=3, column=0, ipady=10) |
| 124 | + solve_btn = Button(root, text='Solve', font=('Helvetica', 30, 'bold'), width=8, command=partial(solve_steps)) |
| 125 | + solve_btn.grid(row=3, column=2, ipady=10) |
| 126 | + |
| 127 | +def init(): |
| 128 | + """ Calls necessary functions """ |
| 129 | + |
| 130 | + global state |
| 131 | + global solution |
| 132 | + state = [1, 2, 3, 4, 5, 6, 7, 8, 0] |
| 133 | + scramble() |
| 134 | + create_buttons() |
| 135 | + create_static_buttons() |
| 136 | + |
| 137 | +init() |
| 138 | +root.mainloop() |
0 commit comments