Skip to content

Commit 1c9531a

Browse files
authored
Merge pull request #47 from Naman9639/master
Add tic-tac-toe
2 parents d1dd992 + c0e0507 commit 1c9531a

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

Games/Tic-tac-toe/tic_tac_toe.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import random
2+
import sys
3+
4+
print "Welcome to Tic Tac Toe!"
5+
print "The board is set up like this: "
6+
print "0|1|2"
7+
print "3|4|5"
8+
print "6|7|8"
9+
print "Enter your first move, 0 through 8. (You are O's, the AI is X's)"
10+
11+
# The 'pairs' list is used to check and see if anyone has won the game.
12+
# -------| Verticals | Horizontals | Diagonals |
13+
pairs = ([0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 4, 8], [2, 4, 6])
14+
15+
# 'Corners' is used for the AI to let it pick random corners.
16+
corners = [0, 2, 6, 8]
17+
board = ["_", "_", "_", "_", "_", "_", " ", " ", " "]
18+
19+
# 'Turn' keeps track of whose turn it is. During the game it is either "AI" or "PLAYER".
20+
turn = "PLAYER"
21+
22+
# 'Aiturn' lets the AI know how many turns it has taken.
23+
aiturn = 0
24+
25+
26+
# Just prints the board and starts the next turn
27+
def printboard(turn, board, aiturn):
28+
print board[0] + "|" + board[1] + "|" + board[2]
29+
print board[3] + "|" + board[4] + "|" + board[5]
30+
print board[6] + "|" + board[7] + "|" + board[8]
31+
print "Turn: " + str(turn)
32+
if turn == 0:
33+
playermove(turn, board, aiturn)
34+
if turn == "AI":
35+
aiturn += 1
36+
aimove(turn, board, aiturn, corners)
37+
if turn == "PLAYER":
38+
playermove(turn, board, aiturn)
39+
40+
41+
# Prompts the player to move
42+
# I added some stuff that makes sure the input is legit. Thanks to Github user Kaligule for opening an issue.
43+
def playermove(turn, board, aiturn):
44+
choice = raw_input("Enter a number 0-8: ")
45+
if choice.isdigit() == False:
46+
print "Keep it an integer, buddy."
47+
playermove(turn, board, aiturn)
48+
if int(choice) > 8 or int(choice) < 0:
49+
print "Make that a number between 0 and 8."
50+
playermove(turn, board, aiturn)
51+
if board[int(choice)] == 'X' or board[int(choice)] == 'O':
52+
print "That's already taken! Try again."
53+
playermove(turn, board, aiturn)
54+
else:
55+
board[int(choice)] = "O"
56+
turn = "AI"
57+
checkforwin(turn, board, aiturn)
58+
59+
60+
# Makes the AI move.
61+
def aimove(turn, board, aiturn, corners):
62+
alreadymoved = False
63+
completes = ([0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 4, 8], [2, 4, 6])
64+
65+
# Pretty self-explanatory, just chooses a random empty corner.
66+
def cornerchoice(corners, board, alreadymoved):
67+
goodchoices = []
68+
if not alreadymoved:
69+
for i in corners:
70+
if board[i] == " " or board[i] == "_":
71+
goodchoices.append(i)
72+
board[random.choice(goodchoices)] = "X"
73+
74+
# If the player didn't take the center on their first move, this makes the AI take the center.
75+
if aiturn == 1:
76+
if board[4] != "O":
77+
board[4] = "X"
78+
alreadymoved = True
79+
else:
80+
cornerchoice(corners, board, alreadymoved)
81+
alreadymoved = True
82+
83+
# Checks to see if there are any possible ways for the game to end next turn, and takes proper action.
84+
else:
85+
for x in completes:
86+
# Offensive
87+
if board[x[0]] == "X" and board[x[1]] == "X" and board[x[2]] != "O":
88+
board[x[2]] = "X"
89+
alreadymoved = True
90+
break
91+
if board[x[1]] == "X" and board[x[2]] == "X" and board[x[0]] != "O":
92+
board[x[0]] = "X"
93+
alreadymoved = True
94+
break
95+
if board[x[0]] == "X" and board[x[2]] == "X" and board[x[1]] != "O":
96+
board[x[1]] = "X"
97+
alreadymoved = True
98+
break
99+
# Tweaked it here a little bit, thanks to reddit user mdond for letting me know. It defending items closer to the
100+
# start of the list 'pairs' before it would play offensive with items later in 'pairs'.
101+
for x in completes:
102+
if alreadymoved == False:
103+
# Defensive
104+
if board[x[0]] == "O" and board[x[1]] == "O" and board[x[2]] != "X":
105+
board[x[2]] = "X"
106+
alreadymoved = True
107+
break
108+
if board[x[1]] == "O" and board[x[2]] == "O" and board[x[0]] != "X":
109+
board[x[0]] = "X"
110+
alreadymoved = True
111+
break
112+
if board[x[0]] == "O" and board[x[2]] == "O" and board[x[1]] != "X":
113+
board[x[1]] = "X"
114+
alreadymoved = True
115+
break
116+
117+
# If none of the above has worked and it isn't during the first few turns, it chooses a random 'side' space to
118+
# fill (this rarely happens). If it is during the AI's 2nd turn, it chooses a corner piece because of this flaw:
119+
# Turn 1
120+
# _|_|_
121+
# _|O|_
122+
# X| |
123+
#
124+
# Turn 2
125+
# _|_|O
126+
# _|O|X <--- Random side piece
127+
# X| |
128+
#
129+
# Turn 3
130+
# O|!|O The two exclamation points represent where an O could be placed to win, but if the AI chooses a corner
131+
# _|O|X in turn 2, this could be prevented.
132+
# X| |!
133+
#
134+
# Also, the code below avoids situations where the player is able to have two possible winning moves next turn.
135+
# For example:
136+
#
137+
# O|_|X <--- Random corner choice
138+
# _|X|_ If the AI were to choose a corner, the player could win.
139+
# O| |O
140+
#
141+
# But when the AI chooses a side piece the below happens.
142+
#
143+
# O|_|_
144+
# ?|X|X This forces the player to place an O at the square with the '?', so the two possible solutions don't occur
145+
# | |O
146+
#
147+
148+
if not alreadymoved:
149+
# The 'and board[4] == "O"' part was added because of another exploit similar to the last one mentioned above
150+
if aiturn == 2 and board[4] == "O":
151+
cornerchoice(corners, board, alreadymoved)
152+
else:
153+
sides = [1, 3, 5, 7]
154+
humansides = 0
155+
for i in sides:
156+
if board[i] == "O":
157+
humansides += 1
158+
if humansides >= 1:
159+
cornerchoice(corners, board, alreadymoved)
160+
else:
161+
162+
goodchoices = []
163+
for i in sides:
164+
if board[i] == " " or board[i] == "_":
165+
goodchoices.append(i)
166+
if goodchoices == []:
167+
cornerchoice(corners, board, alreadymoved)
168+
else:
169+
board[random.choice(goodchoices)] = "X"
170+
171+
turn = "PLAYER"
172+
checkforwin(turn, board, aiturn)
173+
174+
175+
def checkforwin(turn, board, aiturn):
176+
for x in pairs:
177+
zero = board[x[0]]
178+
one = board[x[1]]
179+
two = board[x[2]]
180+
if zero == one and one == two:
181+
if zero == "X":
182+
print "AI wins."
183+
end()
184+
if zero == "O":
185+
print "Human wins. Did you cheat?"
186+
end()
187+
else:
188+
filledspaces = 0
189+
for i in range(8):
190+
if board[i] != " " and board[i] != "_":
191+
filledspaces += 1
192+
if filledspaces == 8:
193+
print "A draw! You will never win!"
194+
end()
195+
196+
printboard(turn, board, aiturn)
197+
198+
# Displays the final board #
199+
200+
def end():
201+
print "Here is the final board."
202+
print board[0] + "|" + board[1] + "|" + board[2]
203+
print board[3] + "|" + board[4] + "|" + board[5]
204+
print board[6] + "|" + board[7] + "|" + board[8]
205+
sys.exit(0)
206+
207+
208+
printboard(turn, board, aiturn)

0 commit comments

Comments
 (0)