Created
August 19, 2015 06:25
-
-
Save itarato/aca4b3105f5c317d48a2 to your computer and use it in GitHub Desktop.
2 player (human + AI) Tic Tac Toe using Minimax
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Player(): | |
| def __init__(self): pass | |
| def move(self, player_bit, game_map, evaluator): pass | |
| class STDIOPlayer(Player): | |
| def move(self, player_bit, game_map, evaluator): | |
| idx = input('Your step (0-9): ') | |
| game_map[int(idx)] = player_bit | |
| class MinimaxPlayer(Player): | |
| def move(self, player_bit, game_map, evaluator): | |
| availables = self.get_availables(game_map) | |
| max_score = -2 | |
| max_index = -1 | |
| for idx in availables: | |
| new_game_map = game_map[:] | |
| new_game_map[idx] = player_bit | |
| score = self.get_score(player_bit, not player_bit, new_game_map, evaluator) | |
| if score > max_score: | |
| max_score = score | |
| max_index = idx | |
| game_map[max_index] = player_bit | |
| def get_availables(self, game_map): | |
| return filter(lambda x: x is not None, [idx if game_map[idx] is None else None for idx in range(len(game_map))]) | |
| def get_score(self, player_me_bit, player_next_bit, game_map, evaluator): | |
| leaf_score = evaluator.check_win(game_map) | |
| if leaf_score.left: | |
| return 1 if player_me_bit else -1 | |
| if leaf_score.right: | |
| return -1 if player_me_bit else 1 | |
| if leaf_score.drawn: | |
| return 0 | |
| availables = self.get_availables(game_map) | |
| if player_next_bit == player_me_bit: # Max. | |
| curr_score = -2 | |
| for idx in availables: | |
| new_game_map = game_map[:] | |
| new_game_map[idx] = player_next_bit | |
| score = self.get_score(player_me_bit, not player_next_bit, new_game_map, evaluator) | |
| if curr_score < score: | |
| curr_score = score | |
| else: # Min. | |
| curr_score = 2 | |
| for idx in availables: | |
| new_game_map = game_map[:] | |
| new_game_map[idx] = player_next_bit | |
| score = self.get_score(player_me_bit, not player_next_bit, new_game_map, evaluator) | |
| if curr_score > score: | |
| curr_score = score | |
| return curr_score | |
| class GameFlow(): | |
| def __init__(self, game_map, player_left, player_right, evaluator): | |
| self.evaluator = evaluator | |
| self.player_left = player_left | |
| self.player_right = player_right | |
| self.map = game_map | |
| self.current_player_left = True | |
| self.result = WinResult() | |
| def move(self): | |
| if self.current_player_left: | |
| self.player_left.move(self.current_player_left, self.map, self.evaluator) | |
| else: | |
| self.player_right.move(self.current_player_left, self.map, self.evaluator) | |
| self.current_player_left = not self.current_player_left | |
| self.result = self.evaluator.check_win(self.map) | |
| print_map(self.map) | |
| class Evaluator(): | |
| def __init__(self): | |
| pass | |
| def check_win(self, game_map): | |
| for i in range(3): | |
| if self.check_sequence(game_map, i * 3, 1) is not None: | |
| return WinResult(winner=game_map[i * 3]) | |
| if self.check_sequence(game_map, i, 3) is not None: | |
| return WinResult(winner=game_map[i]) | |
| if self.check_sequence(game_map, 0, 4) is not None: | |
| return WinResult(winner=game_map[0]) | |
| if self.check_sequence(game_map, 2, 2) is not None: | |
| return WinResult(winner=game_map[2]) | |
| if len(filter(lambda x: x is None, game_map)) == 0: | |
| return WinResult(drawn=True) | |
| return WinResult() | |
| def check_sequence(self, game_map, from_idx=0, offset=1): | |
| if game_map[from_idx] == game_map[from_idx + offset] == game_map[from_idx + offset * 2]: | |
| return game_map[from_idx] | |
| return None | |
| class WinResult(): | |
| def __init__(self, winner=None, drawn=False): | |
| self.left = winner is True | |
| self.right = winner is False | |
| self.drawn = drawn | |
| def is_over(self): | |
| return self.left or self.right or self.drawn | |
| def print_map(map): | |
| for j in range(3): | |
| for i in range(3): | |
| cell = map[j * 3 + i] | |
| if cell is True: | |
| print('O'), | |
| elif cell is False: | |
| print('X'), | |
| else: | |
| print('_'), | |
| print('') | |
| print('\n\n') | |
| if __name__ == '__main__': | |
| pl = MinimaxPlayer() | |
| pr = STDIOPlayer() | |
| m = [None] * 9 | |
| ev = Evaluator() | |
| gf = GameFlow(m, pl, pr, ev) | |
| while not gf.result.is_over(): | |
| gf.move() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment