-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtictacreact.py
121 lines (94 loc) · 3.45 KB
/
tictacreact.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
from pyreact import render, useState, component, createContext, useContext
from pyreact import Button, Div, Li, Ol
Ctx = createContext()
CtxProvider = component(Ctx.Provider)
@component
def Square(props):
idx = props['idx']
ctx = useContext(Ctx)
squares = ctx['squares']
onClick = ctx['onClick']
return Button({'className': 'square',
'onClick': lambda: onClick(idx)
}, squares[idx])
@component
def Row(props):
rowNum = props['rowNum']
row = [Square({'idx': (rowNum * 3) + col_num}) for col_num in range(3)]
return Div({'className': 'board-row'}, row)
@component
def Board():
rows = [Row({'rowNum': row_num}) for row_num in range(3)]
return Div(None, rows)
@component
def Moves(props):
numMoves = props['numMoves']
setStepNumber = props['setStepNumber']
@component
def MoveButton(_props):
move = _props['move']
desc = f"Go to move #{move}" if move > 0 else "Go to game start"
return Li({'key': move},
Button({'className': 'move-history',
'onClick': lambda: setStepNumber(move)
}, desc)
)
return [MoveButton({'move': move}) for move in range(numMoves)]
@component
def Game():
history, setHistory = useState([{'squares': [None for _ in range(9)]}])
stepNumber, setStepNumber = useState(0)
board = history[stepNumber]
xIsNext = (stepNumber % 2) == 0
winner = calculate_winner(board['squares'])
if winner is not None:
status = f"Winner: {winner}"
elif stepNumber == 9:
status = "No Winner"
else:
status = f"Next player: {'X' if xIsNext else 'O'}"
def handle_click(i):
new_squares = list(board['squares'])
if winner or new_squares[i]: # Already winner or square not empty
return # Nothing to do
new_squares[i] = 'X' if xIsNext else 'O'
tmp_history = history[:stepNumber + 1] # Slice in case step changed
new_history = [{'squares': move['squares']} for move in tmp_history]
new_history.append({'squares': new_squares})
setHistory(new_history)
setStepNumber(len(new_history) - 1)
return CtxProvider({'value': {'squares': board['squares'],
'onClick': handle_click}
},
Div({'className': 'game'},
Div({'className': 'game-board'},
Board(None),
Div({'className': 'game-status'}, status),
),
Div({'className': 'game-info'}, 'Move History',
Ol(None,
Moves({'numMoves': len(history),
'setStepNumber': setStepNumber}
)
)
)
)
)
# Render the component in a 'container' div
render(Game, None, 'root')
def calculate_winner(squares):
lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
for line in lines:
a, b, c = line
if squares[a] and (squares[a] == squares[b]) and (squares[a] == squares[c]):
return squares[a]
return None