-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathnaughts_and_crosses.py
executable file
·114 lines (89 loc) · 3.39 KB
/
naughts_and_crosses.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env python3
"""
naughts_and_crosses.py
Play a game of naughts and crosses (also known as tic-tac-toe) against
the computer. To make a move choose a cell and type the number and
letter of that cell. Your input must be exactly two characters long;
do not include any spacing or punctionation.
"""
import enum
import random
from lenses import lens
class Outcome(enum.Enum):
"The possible outcomes of a game of naughts and crosses."
win_for_crosses = enum.auto()
win_for_naughts = enum.auto()
draw = enum.auto()
ongoing = enum.auto()
def __bool__(self):
"Returns whether the game has concluded."
return self is not Outcome.ongoing
def __str__(self):
return {
Outcome.win_for_crosses: "X is the winner!",
Outcome.win_for_naughts: "O is the winner!",
Outcome.draw: "The game is a draw!",
}.get(self, "The winner is unknown.")
class Board:
"A noughts and crosses board."
def __init__(self):
self.board = ((" ",) * 3,) * 3
def make_move(self, x, y):
"""Return a board with a cell filled in by the current player. If
the cell is already occupied then return the board unchanged."""
if self.board[y][x] == " ":
return self & lens.board[y][x].set(self.player)
return self
@property
def player(self):
"The player whose turn it currently is."
return "X" if self._count("X") <= self._count("O") else "O"
@property
def winner(self):
"The winner of this board if one exists."
for potential_win in self._potential_wins():
if potential_win == tuple("XXX"):
return Outcome.win_for_crosses
elif potential_win == tuple("OOO"):
return Outcome.win_for_naughts
if self._count(" ") == 0:
return Outcome.draw
return Outcome.ongoing
def _count(self, character):
"""Counts the number of cells in the board that contain a
particular character."""
return sum(cell == character for cell in self._all_cells())
def _potential_wins(self):
"""Generates all the combinations of board positions that need
to be checked for a win."""
yield from self.board
yield from zip(*self.board)
yield self.board[0][0], self.board[1][1], self.board[2][2]
yield self.board[0][2], self.board[1][1], self.board[2][0]
def __str__(self):
result = []
for letter, row in zip("abc", self.board):
result.append(letter + " " + (" │ ".join(row)))
return " 1 2 3\n" + ("\n ───┼───┼───\n".join(result))
_all_cells = lens.board.Each().Each().collect()
def player_move(board):
"""Shows the board to the player on the console and asks them to
make a move."""
print(board, end="\n\n")
x, y = input("Enter move (e.g. 2b): ")
print()
return int(x) - 1, ord(y) - ord("a")
def random_move(board):
"Makes a random move on the board."
return random.choice(range(3)), random.choice(range(3))
def play():
"Play a game of naughts and crosses against the computer."
ai = {"X": player_move, "O": random_move}
board = Board()
while not board.winner:
x, y = ai[board.player](board)
board = board.make_move(x, y)
print(board, end="\n\n")
print(board.winner)
if __name__ == "__main__":
play()