Skip to content

Commit 3067c27

Browse files
committedApr 1, 2017
PR7
1 parent 4cacc3d commit 3067c27

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
 

‎12/tictactoe-atakume.py

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
from operator import itemgetter
2+
import os
3+
import random
4+
5+
DEFAULT = '_'
6+
VALID_POSITIONS = list(range(1, 10))
7+
WINNING_COMBINATIONS = (
8+
(7, 8, 9), (4, 5, 6), (1, 2, 3),
9+
(7, 4, 1), (8, 5, 2), (9, 6, 3),
10+
(1, 5, 9), (7, 5, 3),
11+
)
12+
PLAYER_X = "X"
13+
PLAYER_O = "O"
14+
AUTO_PLAYER = PLAYER_O
15+
16+
DRAW_GAME = 20
17+
18+
class TicTacToe:
19+
20+
def __init__(self, mode='multiplayer'):
21+
self.board = [None] + len(VALID_POSITIONS) * [DEFAULT] # skip index 0
22+
self.turns = 0
23+
self.player = PLAYER_X
24+
self.mode = mode
25+
26+
def __str__(self):
27+
os.system('clear')
28+
result = ""
29+
for i in range(1, len(self.board), 3):
30+
result += " ".join(self.board[i: i + 3]) + "\n"
31+
return result
32+
33+
def is_win(self):
34+
return self.player if TicTacToe.player_wins(self.player, self.board) else None
35+
36+
def is_end(self):
37+
return TicTacToe.is_draw(self.board)
38+
39+
def take_turn(self):
40+
if self.mode == 's' and self.player == AUTO_PLAYER:
41+
move = self.compute_move()
42+
else:
43+
move = input("Take turn: ")
44+
while not self.proper_move(move):
45+
move = input("Wrong move try again: ")
46+
self.make_move(move)
47+
48+
def proper_move(self, move):
49+
return move.isdigit() and int(move) in TicTacToe.available_steps(self.board)
50+
51+
def make_move(self, move):
52+
self.board[int(move)] = self.player
53+
self.turns += 1
54+
55+
def compute_move(self):
56+
return next(TicTacToe.available_steps(self.board), None)
57+
58+
def next_turn(self):
59+
self.player = PLAYER_O if self.player == PLAYER_X else PLAYER_X
60+
61+
@staticmethod
62+
def available_steps(board):
63+
return [i for i in VALID_POSITIONS if board[i] == DEFAULT]
64+
65+
@staticmethod
66+
def player_wins(player, board):
67+
return any(all(player == i for i in itemgetter(a, b, c)(board)) for a, b, c in WINNING_COMBINATIONS)
68+
69+
@staticmethod
70+
def is_draw(board):
71+
return len(TicTacToe.available_steps(board)) == 0
72+
73+
74+
class RandomAITicTacToe(TicTacToe):
75+
def compute_move(self):
76+
return random.choice(TicTacToe.available_steps(self.board))
77+
78+
79+
class MinMaxAITicTacToe(TicTacToe):
80+
def __init__(self, mode='multiplayer'):
81+
super(MinMaxAITicTacToe, self).__init__(mode)
82+
self.choice = 0
83+
84+
def compute_move(self):
85+
self.minmax(self.board, 0)
86+
return self.choice
87+
88+
def minmax(self, board, depth):
89+
self.choice
90+
91+
depth += 1
92+
scores = []
93+
steps = []
94+
95+
def update_state(board, step, depth):
96+
board = list(board)
97+
board[step] = PLAYER_X if depth % 2 else PLAYER_O
98+
return board
99+
100+
def unit_score(winner, depth):
101+
if winner == DRAW_GAME:
102+
return 0
103+
else:
104+
return 10 - depth if winner == PLAYER_X else depth - 10
105+
106+
def check_win_game(board):
107+
for player in [PLAYER_X, PLAYER_O]:
108+
if TicTacToe.player_wins(player, board):
109+
return player
110+
return DRAW_GAME if TicTacToe.is_draw(board) else 10
111+
112+
result = check_win_game(board)
113+
if result != 10:
114+
return unit_score(result, depth)
115+
116+
for step in TicTacToe.available_steps(board):
117+
score = self.minmax(update_state(board, step, depth), depth)
118+
scores.append(score)
119+
steps.append(step)
120+
121+
if depth % 2 == 1:
122+
max_value_index = scores.index(max(scores))
123+
self.choice = steps[max_value_index]
124+
return max(scores)
125+
else:
126+
min_value_index = scores.index(min(scores))
127+
self.choice = steps[min_value_index]
128+
return min(scores)
129+
130+
if __name__ == "__main__":
131+
another = "y"
132+
while True:
133+
player_mode = input("For singleplayer mode type 's': ")
134+
game = MinMaxAITicTacToe(player_mode)
135+
print(game)
136+
while not game.is_end():
137+
game.take_turn()
138+
print(game)
139+
if game.is_win():
140+
break
141+
game.next_turn()
142+
143+
another = input("Game over. Play once again?")
144+
if another.lower() not in ["y", "yes"]:
145+
break

0 commit comments

Comments
 (0)
Please sign in to comment.