-
Notifications
You must be signed in to change notification settings - Fork 3
/
tictactoeexample.py
136 lines (125 loc) · 4.53 KB
/
tictactoeexample.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import sys
from curtsies.fmtfuncs import *
from curtsies.window import Window
from curtsies.terminal import Terminal
from curtsies.fsarray import fsarray
class Board(object):
"""
>>> Board().rows
((' ', ' ', ' '), (' ', ' ', ' '), (' ', ' ', ' '))
>>> Board().columns
((' ', ' ', ' '), (' ', ' ', ' '), (' ', ' ', ' '))
>>> Board().turn
0
>>> Board().whose_turn
'x'
>>> b = Board().move(2); print(b)
| |x
-----
| |
-----
| |
>>> b.possible()
[< Board |o.x......| >, < Board |.ox......| >, < Board |..xo.....| >, < Board |..x.o....| >, < Board |..x..o...| >, < Board |..x...o..| >, < Board |..x....o.| >, < Board |..x.....o| >]
"""
def __init__(self, width=3, height=3):
self._rows = [[' ' for _ in range(width)] for _ in range(height)]
rows = property(lambda self: tuple(tuple(row) for row in self._rows))
columns = property(lambda self: tuple(zip(*self._rows)))
spots = property(lambda self: tuple(c for row in self._rows for c in row))
def __str__(self):
return ('\n'+'-'*(len(self.columns)*2-1) + '\n').join(['|'.join(row) for row in self._rows])
def __repr__(self): return '< Board |'+''.join(self.spots).replace(' ','.')+'| >'
@property
def turn(self):
return 9 - self.spots.count(' ')
@property
def whose_turn(self):
return 'xo'[self.turn % 2]
def winner(self):
"""Returns either x or o if one of them won, otherwise None"""
for c in 'xo':
for comb in [(0,3,6), (1,4,7), (2,5,8), (0,1,2), (3,4,5), (6,7,8), (0,4,8), (2,4,6)]:
if all(self.spots[p] == c for p in comb):
return c
return None
def move(self, pos):
if not self.spots[pos] == ' ': raise ValueError('That spot it taken')
new = Board(len(self.rows), len(self.columns))
new._rows = list(list(row) for row in self.rows)
new._rows[pos // 3][pos % 3] = self.whose_turn
return new
def possible(self):
return [self.move(p) for p in range(len(self.spots)) if self.spots[p] == ' ']
def display(self):
colored = {' ':' ', 'x':blue(bold('x')), 'o':red(bold('o'))}
s = ('\n'+green('-')*(len(self.columns)*2-1) + '\n').join([green('|').join(colored[mark] for mark in row) for row in self._rows])
a = fsarray([bold(green('enter a number, 0-8' if self.whose_turn == 'x' else 'wait for computer...'))] + s.split('\n'))
return a
def opp(c):
"""
>>> opp('x'), opp('o')
('o', 'x')
"""
return 'x' if c == 'o' else 'o'
def value(board, who='x'):
"""Returns the value of a board
>>> b = Board(); b._rows = [['x', 'x', 'x'], ['x', 'x', 'x'], ['x', 'x', 'x']]
>>> value(b)
1
>>> b = Board(); b._rows = [['o', 'o', 'o'], ['o', 'o', 'o'], ['o', 'o', 'o']]
>>> value(b)
-1
>>> b = Board(); b._rows = [['x', 'o', ' '], ['x', 'o', ' '], [' ', ' ', ' ']]
>>> value(b)
1
>>> b._rows[0][2] = 'x'
>>> value(b)
-1
"""
w = board.winner()
if w == who:
return 1
if w == opp(who):
return -1
if board.turn == 9:
return 0
if who == board.whose_turn:
return max([value(b, who) for b in board.possible()])
else:
return min([value(b, who) for b in board.possible()])
def ai(board, who='x'):
"""
Returns best next board
>>> b = Board(); b._rows = [['x', 'o', ' '], ['x', 'o', ' '], [' ', ' ', ' ']]
>>> ai(b)
< Board |xo.xo.x..| >
"""
return sorted(board.possible(), key=lambda b: value(b, who))[-1]
def main():
with Terminal(sys.stdin, sys.stdout) as tc:
with Window(tc) as t:
b = Board()
window_change_event = t.tc.get_event() # always the first event get_event() returns
while True:
t.render_to_terminal(b.display())
if b.turn == 9 or b.winner():
c = t.tc.get_event() # hit any key
sys.exit()
while True:
c = t.tc.get_event()
if c == '':
sys.exit()
try:
if int(c) in range(9):
b = b.move(int(c))
except ValueError:
continue
t.render_to_terminal(b.display())
break
if b.turn == 9 or b.winner():
c = t.tc.get_event() # hit any key
sys.exit()
b = ai(b, 'o')
if __name__ == '__main__':
main()