Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aimacode
GitHub Repository: aimacode/aima-python
Path: blob/master/gui/tic-tac-toe.py
621 views
1
import os.path
2
from tkinter import *
3
4
from games import minmax_decision, alpha_beta_player, random_player, TicTacToe
5
# "gen_state" can be used to generate a game state to apply the algorithm
6
from tests.test_games import gen_state
7
8
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
9
10
ttt = TicTacToe()
11
root = None
12
buttons = []
13
frames = []
14
x_pos = []
15
o_pos = []
16
count = 0
17
sym = ""
18
result = None
19
choices = None
20
21
22
def create_frames(root):
23
"""
24
This function creates the necessary structure of the game.
25
"""
26
frame1 = Frame(root)
27
frame2 = Frame(root)
28
frame3 = Frame(root)
29
frame4 = Frame(root)
30
create_buttons(frame1)
31
create_buttons(frame2)
32
create_buttons(frame3)
33
buttonExit = Button(
34
frame4, height=1, width=2,
35
text="Exit",
36
command=lambda: exit_game(root))
37
buttonExit.pack(side=LEFT)
38
frame4.pack(side=BOTTOM)
39
frame3.pack(side=BOTTOM)
40
frame2.pack(side=BOTTOM)
41
frame1.pack(side=BOTTOM)
42
frames.append(frame1)
43
frames.append(frame2)
44
frames.append(frame3)
45
for x in frames:
46
buttons_in_frame = []
47
for y in x.winfo_children():
48
buttons_in_frame.append(y)
49
buttons.append(buttons_in_frame)
50
buttonReset = Button(frame4, height=1, width=2,
51
text="Reset", command=lambda: reset_game())
52
buttonReset.pack(side=LEFT)
53
54
55
def create_buttons(frame):
56
"""
57
This function creates the buttons to be pressed/clicked during the game.
58
"""
59
button0 = Button(frame, height=2, width=2, text=" ",
60
command=lambda: on_click(button0))
61
button0.pack(side=LEFT)
62
button1 = Button(frame, height=2, width=2, text=" ",
63
command=lambda: on_click(button1))
64
button1.pack(side=LEFT)
65
button2 = Button(frame, height=2, width=2, text=" ",
66
command=lambda: on_click(button2))
67
button2.pack(side=LEFT)
68
69
70
# TODO: Add a choice option for the user.
71
def on_click(button):
72
"""
73
This function determines the action of any button.
74
"""
75
global ttt, choices, count, sym, result, x_pos, o_pos
76
77
if count % 2 == 0:
78
sym = "X"
79
else:
80
sym = "O"
81
count += 1
82
83
button.config(
84
text=sym,
85
state='disabled',
86
disabledforeground="red") # For cross
87
88
x, y = get_coordinates(button)
89
x += 1
90
y += 1
91
x_pos.append((x, y))
92
state = gen_state(to_move='O', x_positions=x_pos,
93
o_positions=o_pos)
94
try:
95
choice = choices.get()
96
if "Random" in choice:
97
a, b = random_player(ttt, state)
98
elif "Pro" in choice:
99
a, b = minmax_decision(state, ttt)
100
else:
101
a, b = alpha_beta_player(ttt, state)
102
except (ValueError, IndexError, TypeError) as e:
103
disable_game()
104
result.set("It's a draw :|")
105
return
106
if 1 <= a <= 3 and 1 <= b <= 3:
107
o_pos.append((a, b))
108
button_to_change = get_button(a - 1, b - 1)
109
if count % 2 == 0: # Used again, will become handy when user is given the choice of turn.
110
sym = "X"
111
else:
112
sym = "O"
113
count += 1
114
115
if check_victory(button):
116
result.set("You win :)")
117
disable_game()
118
else:
119
button_to_change.config(text=sym, state='disabled',
120
disabledforeground="black")
121
if check_victory(button_to_change):
122
result.set("You lose :(")
123
disable_game()
124
125
126
# TODO: Replace "check_victory" by "k_in_row" function.
127
def check_victory(button):
128
"""
129
This function checks various winning conditions of the game.
130
"""
131
# check if previous move caused a win on vertical line
132
global buttons
133
x, y = get_coordinates(button)
134
tt = button['text']
135
if buttons[0][y]['text'] == buttons[1][y]['text'] == buttons[2][y]['text'] != " ":
136
buttons[0][y].config(text="|" + tt + "|")
137
buttons[1][y].config(text="|" + tt + "|")
138
buttons[2][y].config(text="|" + tt + "|")
139
return True
140
141
# check if previous move caused a win on horizontal line
142
if buttons[x][0]['text'] == buttons[x][1]['text'] == buttons[x][2]['text'] != " ":
143
buttons[x][0].config(text="--" + tt + "--")
144
buttons[x][1].config(text="--" + tt + "--")
145
buttons[x][2].config(text="--" + tt + "--")
146
return True
147
148
# check if previous move was on the main diagonal and caused a win
149
if x == y and buttons[0][0]['text'] == buttons[1][1]['text'] == buttons[2][2]['text'] != " ":
150
buttons[0][0].config(text="\\" + tt + "\\")
151
buttons[1][1].config(text="\\" + tt + "\\")
152
buttons[2][2].config(text="\\" + tt + "\\")
153
return True
154
155
# check if previous move was on the secondary diagonal and caused a win
156
if x + y == 2 and buttons[0][2]['text'] == buttons[1][1]['text'] == buttons[2][0]['text'] != " ":
157
buttons[0][2].config(text="/" + tt + "/")
158
buttons[1][1].config(text="/" + tt + "/")
159
buttons[2][0].config(text="/" + tt + "/")
160
return True
161
162
return False
163
164
165
def get_coordinates(button):
166
"""
167
This function returns the coordinates of the button clicked.
168
"""
169
global buttons
170
for x in range(len(buttons)):
171
for y in range(len(buttons[x])):
172
if buttons[x][y] == button:
173
return x, y
174
175
176
def get_button(x, y):
177
"""
178
This function returns the button memory location corresponding to a coordinate.
179
"""
180
global buttons
181
return buttons[x][y]
182
183
184
def reset_game():
185
"""
186
This function will reset all the tiles to the initial null value.
187
"""
188
global x_pos, o_pos, frames, count
189
190
count = 0
191
x_pos = []
192
o_pos = []
193
result.set("Your Turn!")
194
for x in frames:
195
for y in x.winfo_children():
196
y.config(text=" ", state='normal')
197
198
199
def disable_game():
200
"""
201
This function deactivates the game after a win, loss or draw.
202
"""
203
global frames
204
for x in frames:
205
for y in x.winfo_children():
206
y.config(state='disabled')
207
208
209
def exit_game(root):
210
"""
211
This function will exit the game by killing the root.
212
"""
213
root.destroy()
214
215
216
if __name__ == "__main__":
217
global result, choices
218
219
root = Tk()
220
root.title("TicTacToe")
221
root.geometry("150x200") # Improved the window geometry
222
root.resizable(0, 0) # To remove the maximize window option
223
result = StringVar()
224
result.set("Your Turn!")
225
w = Label(root, textvariable=result)
226
w.pack(side=BOTTOM)
227
create_frames(root)
228
choices = StringVar(root)
229
choices.set("Vs Pro")
230
menu = OptionMenu(root, choices, "Vs Random", "Vs Pro", "Vs Legend")
231
menu.pack()
232
root.mainloop()
233
234