diff --git a/gui/romania_problem.py b/gui/romania_problem.py new file mode 100644 index 000000000..31a3d04c7 --- /dev/null +++ b/gui/romania_problem.py @@ -0,0 +1,518 @@ +from tkinter import * +import sys +import os.path +import math +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from search import * +from search import breadth_first_tree_search as bfts, depth_first_tree_search as dfts,depth_first_graph_search as dfgs +from utils import Stack, FIFOQueue, PriorityQueue +from copy import deepcopy +root = None +city_coord = {} +romania_problem = None +algo = None +start = None +goal = None +counter = -1 +city_map = None +frontier = None +front = None +node = None +next_button = None +explored=None + +def create_map(root): + ''' + This function draws out the required map. + ''' + global city_map, start, goal + romania_locations = romania_map.locations + width = 750 + height = 670 + margin = 5 + city_map = Canvas(root, width=width, height=height) + city_map.pack() + + # Since lines have to be drawn between particular points, we need to list + # them separately + make_line( + city_map, + romania_locations['Arad'][0], + height - + romania_locations['Arad'][1], + romania_locations['Sibiu'][0], + height - + romania_locations['Sibiu'][1], + romania_map.get('Arad', 'Sibiu')) + make_line( + city_map, + romania_locations['Arad'][0], + height - + romania_locations['Arad'][1], + romania_locations['Zerind'][0], + height - + romania_locations['Zerind'][1], + romania_map.get('Arad', 'Zerind')) + make_line( + city_map, + romania_locations['Arad'][0], + height - + romania_locations['Arad'][1], + romania_locations['Timisoara'][0], + height - + romania_locations['Timisoara'][1], + romania_map.get('Arad', 'Timisoara')) + make_line( + city_map, + romania_locations['Oradea'][0], + height - + romania_locations['Oradea'][1], + romania_locations['Zerind'][0], + height - + romania_locations['Zerind'][1], + romania_map.get('Oradea', 'Zerind')) + make_line( + city_map, + romania_locations['Oradea'][0], + height - + romania_locations['Oradea'][1], + romania_locations['Sibiu'][0], + height - + romania_locations['Sibiu'][1], + romania_map.get('Oradea', 'Sibiu')) + make_line( + city_map, + romania_locations['Lugoj'][0], + height - + romania_locations['Lugoj'][1], + romania_locations['Timisoara'][0], + height - + romania_locations['Timisoara'][1], + romania_map.get('Lugoj', 'Timisoara')) + make_line( + city_map, + romania_locations['Lugoj'][0], + height - + romania_locations['Lugoj'][1], + romania_locations['Mehadia'][0], + height - + romania_locations['Mehadia'][1], + romania_map.get('Lugoj', 'Mehandia')) + make_line( + city_map, + romania_locations['Drobeta'][0], + height - + romania_locations['Drobeta'][1], + romania_locations['Mehadia'][0], + height - + romania_locations['Mehadia'][1], + romania_map.get('Drobeta', 'Mehandia')) + make_line( + city_map, + romania_locations['Drobeta'][0], + height - + romania_locations['Drobeta'][1], + romania_locations['Craiova'][0], + height - + romania_locations['Craiova'][1], + romania_map.get('Drobeta', 'Craiova')) + make_line( + city_map, + romania_locations['Pitesti'][0], + height - + romania_locations['Pitesti'][1], + romania_locations['Craiova'][0], + height - + romania_locations['Craiova'][1], + romania_map.get('Pitesti', 'Craiova')) + make_line( + city_map, + romania_locations['Rimnicu'][0], + height - + romania_locations['Rimnicu'][1], + romania_locations['Craiova'][0], + height - + romania_locations['Craiova'][1], + romania_map.get('Rimnicu', 'Craiova')) + make_line( + city_map, + romania_locations['Rimnicu'][0], + height - + romania_locations['Rimnicu'][1], + romania_locations['Sibiu'][0], + height - + romania_locations['Sibiu'][1], + romania_map.get('Rimnicu', 'Sibiu')) + make_line( + city_map, + romania_locations['Rimnicu'][0], + height - + romania_locations['Rimnicu'][1], + romania_locations['Pitesti'][0], + height - + romania_locations['Pitesti'][1], + romania_map.get('Rimnicu', 'Pitesti')) + make_line( + city_map, + romania_locations['Bucharest'][0], + height - + romania_locations['Bucharest'][1], + romania_locations['Pitesti'][0], + height - + romania_locations['Pitesti'][1], + romania_map.get('Bucharest', 'Pitesti')) + make_line( + city_map, + romania_locations['Fagaras'][0], + height - + romania_locations['Fagaras'][1], + romania_locations['Sibiu'][0], + height - + romania_locations['Sibiu'][1], + romania_map.get('Fagaras', 'Sibiu')) + make_line( + city_map, + romania_locations['Fagaras'][0], + height - + romania_locations['Fagaras'][1], + romania_locations['Bucharest'][0], + height - + romania_locations['Bucharest'][1], + romania_map.get('Fagaras', 'Bucharest')) + make_line( + city_map, + romania_locations['Giurgiu'][0], + height - + romania_locations['Giurgiu'][1], + romania_locations['Bucharest'][0], + height - + romania_locations['Bucharest'][1], + romania_map.get('Giurgiu', 'Bucharest')) + make_line( + city_map, + romania_locations['Urziceni'][0], + height - + romania_locations['Urziceni'][1], + romania_locations['Bucharest'][0], + height - + romania_locations['Bucharest'][1], + romania_map.get('Urziceni', 'Bucharest')) + make_line( + city_map, + romania_locations['Urziceni'][0], + height - + romania_locations['Urziceni'][1], + romania_locations['Hirsova'][0], + height - + romania_locations['Hirsova'][1], + romania_map.get('Urziceni', 'Hirsova')) + make_line( + city_map, + romania_locations['Eforie'][0], + height - + romania_locations['Eforie'][1], + romania_locations['Hirsova'][0], + height - + romania_locations['Hirsova'][1], + romania_map.get('Eforie', 'Hirsova')) + make_line( + city_map, + romania_locations['Urziceni'][0], + height - + romania_locations['Urziceni'][1], + romania_locations['Vaslui'][0], + height - + romania_locations['Vaslui'][1], + romania_map.get('Urziceni', 'Vaslui')) + make_line( + city_map, + romania_locations['Iasi'][0], + height - + romania_locations['Iasi'][1], + romania_locations['Vaslui'][0], + height - + romania_locations['Vaslui'][1], + romania_map.get('Iasi', 'Vaslui')) + make_line( + city_map, + romania_locations['Iasi'][0], + height - + romania_locations['Iasi'][1], + romania_locations['Neamt'][0], + height - + romania_locations['Neamt'][1], + romania_map.get('Iasi', 'Neamt')) + + for city in romania_locations.keys(): + make_rectangle( + city_map, + romania_locations[city][0], + height - + romania_locations[city][1], + margin, + city) + + make_legend(city_map) + + +def make_line(map, x0, y0, x1, y1, distance): + ''' + This function draws out the lines joining various points. + ''' + map.create_line(x0, y0, x1, y1) + map.create_text((x0 + x1) / 2, (y0 + y1) / 2, text=distance) + + +def make_rectangle(map, x0, y0, margin, city_name): + ''' + This function draws out rectangles for various points. + ''' + global city_coord + rect = map.create_rectangle( + x0 - margin, + y0 - margin, + x0 + margin, + y0 + margin, + fill="white") + map.create_text( + x0 - 2 * margin, + y0 - 2 * margin, + text=city_name, + anchor=SE) + city_coord.update({city_name: rect}) + + +def make_legend(map): + + rect1 = map.create_rectangle(600, 100, 610, 110, fill="white") + text1 = map.create_text(615, 105, anchor=W, text="Un-explored") + + rect2 = map.create_rectangle(600, 115, 610, 125, fill="orange") + text2 = map.create_text(615, 120, anchor=W, text="Frontier") + + rect3 = map.create_rectangle(600, 130, 610, 140, fill="red") + text3 = map.create_text(615, 135, anchor=W, text="Currently Exploring") + + rect4 = map.create_rectangle(600, 145, 610, 155, fill="grey") + text4 = map.create_text(615, 150, anchor=W, text="Explored") + + rect5 = map.create_rectangle(600, 160, 610, 170, fill="dark green") + text5 = map.create_text(615, 165, anchor=W, text="Final Solution") + + +def tree_search(problem): + ''' + earch through the successors of a problem to find a goal. + The argument frontier should be an empty queue. + Don't worry about repeated paths to a state. [Figure 3.7] + This function has been changed to make it suitable for the Tkinter GUI. + ''' + global counter, frontier, node + # print(counter) + if counter == -1: + frontier.append(Node(problem.initial)) + # print(frontier) + display_frontier(frontier) + if counter % 3 == 0 and counter >= 0: + node = frontier.pop() + # print(node) + display_current(node) + if counter % 3 == 1 and counter >= 0: + if problem.goal_test(node.state): + # print(node) + return node + frontier.extend(node.expand(problem)) + # print(frontier) + display_frontier(frontier) + if counter % 3 == 2 and counter >= 0: + # print(node) + display_explored(node) + return None + +def graph_search(problem): + ''' + Search through the successors of a problem to find a goal. + The argument frontier should be an empty queue. + If two paths reach a state, only use the first one. [Figure 3.7] + This function has been changed to make it suitable for the Tkinter GUI. + ''' + global counter,frontier,node,explored + if counter == -1: + frontier.append(Node(problem.initial)) + explored=set() + display_frontier(frontier) + if counter % 3 ==0 and counter >=0: + node = frontier.pop() + display_current(node) + if counter % 3 == 1 and counter >= 0: + if problem.goal_test(node.state): + return node + explored.add(node.state) + frontier.extend(child for child in node.expand(problem) + if child.state not in explored and + child not in frontier) + display_frontier(frontier) + if counter % 3 == 2 and counter >= 0: + display_explored(node) + return None + + + +def display_frontier(queue): + ''' + This function marks the frontier nodes (orange) on the map. + ''' + global city_map, city_coord + qu = deepcopy(queue) + while qu: + node = qu.pop() + for city in city_coord.keys(): + if node.state == city: + city_map.itemconfig(city_coord[city], fill="orange") + +def display_current(node): + ''' + This function marks the currently exploring node (red) on the map. + ''' + global city_map, city_coord + city = node.state + city_map.itemconfig(city_coord[city], fill="red") + +def display_explored(node): + ''' + This function marks the already explored node (gray) on the map. + ''' + global city_map, city_coord + city = node.state + city_map.itemconfig(city_coord[city], fill="gray") + +def display_final(cities): + ''' + This function marks the final solution nodes (green) on the map. + ''' + global city_map, city_coord + for city in cities: + city_map.itemconfig(city_coord[city], fill="green") + +def breadth_first_tree_search(problem): + """Search the shallowest nodes in the search tree first.""" + global frontier, counter + if counter == -1: + frontier = FIFOQueue() + return tree_search(problem) + + +def depth_first_tree_search(problem): + """Search the deepest nodes in the search tree first.""" + # This search algorithm might not work in case of repeated paths. + global frontier,counter + if counter == -1: + frontier=Stack() + return tree_search(problem) + +# TODO: Check if the solution given by this function is consistent with the original function. +def depth_first_graph_search(problem): + """Search the deepest nodes in the search tree first.""" + global frontier, counter + if counter == -1: + frontier = Stack() + return graph_search(problem) + +# TODO: +# Remove redundant code. +# Make the interchangbility work between various algorithms at each step. +def on_click(): + ''' + This function defines the action of the 'Next' button. + ''' + global algo, counter, next_button, romania_problem, start, goal + romania_problem = GraphProblem(start.get(), goal.get(), romania_map) + if "Breadth-First Tree Search" == algo.get(): + node = breadth_first_tree_search(romania_problem) + if node is not None: + final_path = bfts(romania_problem).solution() + final_path.append(start.get()) + display_final(final_path) + next_button.config(state="disabled") + counter += 1 + elif "Depth-First Tree Search" == algo.get(): + node = depth_first_tree_search(romania_problem) + if node is not None: + final_path = dfts(romania_problem).solution() + final_path.append(start.get()) + display_final(final_path) + next_button.config(state="disabled") + counter += 1 + elif "Depth-First Graph Search" == algo.get(): + node = depth_first_graph_search(romania_problem) + if node is not None: + print(node) + final_path = dfgs(romania_problem).solution() + print(final_path) + final_path.append(start.get()) + display_final(final_path) + next_button.config(state="disabled") + counter += 1 + + + +def reset_map(): + global counter, city_coord, city_map, next_button + counter = -1 + for city in city_coord.keys(): + city_map.itemconfig(city_coord[city], fill="white") + next_button.config(state="normal") + +# TODO: Add more search algorithms in the OptionMenu + + +def main(): + global algo, start, goal, next_button + root = Tk() + root.title("Road Map of Romania") + root.geometry("950x1150") + algo = StringVar(root) + start = StringVar(root) + goal = StringVar(root) + algo.set("Breadth-First Tree Search") + start.set('Arad') + goal.set('Bucharest') + cities = sorted(romania_map.locations.keys()) + algorithm_menu = OptionMenu( + root, algo, "Breadth-First Tree Search", "Depth-First Tree Search","Depth-First Graph Search") + Label(root, text="\n Search Algorithm").pack() + algorithm_menu.pack() + Label(root, text="\n Start City").pack() + start_menu = OptionMenu(root, start, *cities) + start_menu.pack() + Label(root, text="\n Goal City").pack() + goal_menu = OptionMenu(root, goal, *cities) + goal_menu.pack() + frame1 = Frame(root) + next_button = Button( + frame1, + width=6, + height=2, + text="Next", + command=on_click, + padx=2, + pady=2, + relief=GROOVE) + next_button.pack(side=RIGHT) + reset_button = Button( + frame1, + width=6, + height=2, + text="Reset", + command=reset_map, + padx=2, + pady=2, + relief=GROOVE) + reset_button.pack(side=RIGHT) + frame1.pack(side=BOTTOM) + create_map(root) + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/gui/tic-tac-toe.py b/gui/tic-tac-toe.py new file mode 100644 index 000000000..c2781255f --- /dev/null +++ b/gui/tic-tac-toe.py @@ -0,0 +1,236 @@ +from tkinter import * +import sys +import os.path +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from games import minimax_decision, alphabeta_player, random_player, TicTacToe +# "gen_state" can be used to generate a game state to apply the algorithm +from tests.test_games import gen_state + +ttt = TicTacToe() +root = None +buttons = [] +frames = [] +x_pos = [] +o_pos = [] +count = 0 +sym = "" +result = None +choices = None + + +def create_frames(root): + """ + This function creates the necessary structure of the game. + """ + frame1 = Frame(root) + frame2 = Frame(root) + frame3 = Frame(root) + frame4 = Frame(root) + create_buttons(frame1) + create_buttons(frame2) + create_buttons(frame3) + buttonExit = Button( + frame4, height=1, width=2, + text="Exit", + command=lambda: exit_game(root)) + buttonExit.pack(side=LEFT) + frame4.pack(side=BOTTOM) + frame3.pack(side=BOTTOM) + frame2.pack(side=BOTTOM) + frame1.pack(side=BOTTOM) + frames.append(frame1) + frames.append(frame2) + frames.append(frame3) + for x in frames: + buttons_in_frame = [] + for y in x.winfo_children(): + buttons_in_frame.append(y) + buttons.append(buttons_in_frame) + buttonReset = Button(frame4, height=1, width=2, + text="Reset", command=lambda: reset_game()) + buttonReset.pack(side=LEFT) + + +def create_buttons(frame): + """ + This function creates the buttons to be pressed/clicked during the game. + """ + button0 = Button(frame, height=2, width=2, text=" ", + command=lambda: on_click(button0)) + button0.pack(side=LEFT) + button1 = Button(frame, height=2, width=2, text=" ", + command=lambda: on_click(button1)) + button1.pack(side=LEFT) + button2 = Button(frame, height=2, width=2, text=" ", + command=lambda: on_click(button2)) + button2.pack(side=LEFT) + + +# TODO: Add a choice option for the user. +def on_click(button): + """ + This function determines the action of any button. + """ + global ttt, choices, count, sym, result, x_pos, o_pos + + if count % 2 == 0: + sym = "X" + else: + sym = "O" + count += 1 + + button.config( + text=sym, + state='disabled', + disabledforeground="red") # For cross + + x, y = get_coordinates(button) + x += 1 + y += 1 + x_pos.append((x, y)) + state = gen_state(to_move='O', x_positions=x_pos, + o_positions=o_pos) + try: + choice = choices.get() + if "Random" in choice: + a, b = random_player(ttt, state) + elif "Pro" in choice: + a, b = minimax_decision(state, ttt) + else: + a, b = alphabeta_player(ttt, state) + except (ValueError, IndexError, TypeError) as e: + disable_game() + result.set("It's a draw :|") + return + if 1 <= a <= 3 and 1 <= b <= 3: + o_pos.append((a, b)) + button_to_change = get_button(a - 1, b - 1) + if count % 2 == 0: # Used again, will become handy when user is given the choice of turn. + sym = "X" + else: + sym = "O" + count += 1 + + if check_victory(button): + result.set("You win :)") + disable_game() + else: + button_to_change.config(text=sym, state='disabled', + disabledforeground="black") + if check_victory(button_to_change): + result.set("You lose :(") + disable_game() + + +# TODO: Replace "check_victory" by "k_in_row" function. +def check_victory(button): + """ + This function checks various winning conditions of the game. + """ + # check if previous move caused a win on vertical line + global buttons + x, y = get_coordinates(button) + tt = button['text'] + if buttons[0][y]['text'] == buttons[1][y]['text'] == buttons[2][y]['text'] != " ": + buttons[0][y].config(text="|" + tt + "|") + buttons[1][y].config(text="|" + tt + "|") + buttons[2][y].config(text="|" + tt + "|") + return True + + # check if previous move caused a win on horizontal line + if buttons[x][0]['text'] == buttons[x][1]['text'] == buttons[x][2]['text'] != " ": + buttons[x][0].config(text="--" + tt + "--") + buttons[x][1].config(text="--" + tt + "--") + buttons[x][2].config(text="--" + tt + "--") + return True + + # check if previous move was on the main diagonal and caused a win + if x == y and buttons[0][0]['text'] == buttons[1][1]['text'] == buttons[2][2]['text'] != " ": + buttons[0][0].config(text="\\" + tt + "\\") + buttons[1][1].config(text="\\" + tt + "\\") + buttons[2][2].config(text="\\" + tt + "\\") + return True + + # check if previous move was on the secondary diagonal and caused a win + if x + \ + y == 2 and buttons[0][2]['text'] == buttons[1][1]['text'] == buttons[2][0]['text'] != " ": + buttons[0][2].config(text="/" + tt + "/") + buttons[1][1].config(text="/" + tt + "/") + buttons[2][0].config(text="/" + tt + "/") + return True + + return False + + +def get_coordinates(button): + """ + This function returns the coordinates of the button clicked. + """ + global buttons + for x in range(len(buttons)): + for y in range(len(buttons[x])): + if buttons[x][y] == button: + return x, y + + +def get_button(x, y): + """ + This function returns the button memory location corresponding to a coordinate. + """ + global buttons + return buttons[x][y] + + +def reset_game(): + """ + This function will reset all the tiles to the initial null value. + """ + global x_pos, o_pos, frames, count + + count = 0 + x_pos = [] + o_pos = [] + result.set("Your Turn!") + for x in frames: + for y in x.winfo_children(): + y.config(text=" ", state='normal') + + +def disable_game(): + """ + This function deactivates the game after a win, loss or draw. + """ + global frames + for x in frames: + for y in x.winfo_children(): + y.config(state='disabled') + + +def exit_game(root): + """ + This function will exit the game by killing the root. + """ + root.destroy() + + +def main(): + global result, choices + + root = Tk() + root.title("TicTacToe") + root.resizable(0, 0) # To remove the maximize window option + result = StringVar() + result.set("Your Turn!") + w = Label(root, textvariable=result) + w.pack(side=BOTTOM) + create_frames(root) + choices = StringVar(root) + choices.set("Vs Pro") + menu = OptionMenu(root, choices, "Vs Random", "Vs Pro", "Vs Legend") + menu.pack() + root.mainloop() + + +if __name__ == "__main__": + main() +
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: