Skip to content

Commit 20de39e

Browse files
Add EGTB support (#5)
python-tictactoe now supports creating and reading endgame tablebases (EGTB).
1 parent 57c20bb commit 20de39e

7 files changed

Lines changed: 294 additions & 96 deletions

File tree

.github/workflows/code-cov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Generate Report
2121
run: |
2222
pip install pytest pytest-cov numpy
23-
pytest test.py --cov=tictactoe --cov-report=xml
23+
pytest --cov=tictactoe --cov-report=xml
2424
coverage report --show-missing
2525
- name: Upload Coverage to Codecov
2626
uses: codecov/codecov-action@v2

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ jobs:
3737
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
3838
- name: Test with pytest
3939
run: |
40-
pytest test.py -s
40+
pytest

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@ from tictactoe import Board
3232

3333
board = Board(dimensions=(10, 10, 10), x_in_a_row=8)
3434
```
35+
* Generate endgame tablebases
36+
```python
37+
from tictactoe.egtb import Generator
38+
import functools, operator
39+
40+
dimensions = (4, 3)
41+
total_squares = functools.reduce(operator.mul, dimensions)
42+
for index in reversed(range(total_squares + 1)):
43+
Generator(dimensions, 3, index)
44+
```
45+
* Read endgame tablebases
46+
```python
47+
from tictactoe.egtb import Reader
48+
from tictactoe import Board
49+
50+
reader = Reader((3, 3), 3, 2)
51+
board = Board((3, 3), 3)
52+
board.push((0, 0))
53+
board.push((0, 1))
54+
print(reader.index(board))
55+
```
3556

3657
## License
3758
python-tictactoe is licensed under the MIT License. Check out LICENSE for the full text.

test_tictactoe/__init__.py

Whitespace-only changes.

test_tictactoe/test_egtb.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from tictactoe.egtb import Generator, Reader
2+
import tictactoe
3+
4+
5+
def test_egtb():
6+
# Generate EGTBs.
7+
two_piece_hash = None
8+
for index in reversed(range(10)):
9+
egtb_hash = Generator((3, 3), 3, index).correct_hash
10+
if index == 2:
11+
two_piece_hash = egtb_hash
12+
13+
reader = Reader((3, 3), 3, 2, two_piece_hash)
14+
board = tictactoe.Board((3, 3), 3)
15+
board.push((0, 0))
16+
board.push((0, 1))
17+
assert reader.index(board) == tictactoe.X
18+
19+
# Incorrect hash.
20+
with open("3_3-3-2.ttb", "rb") as file:
21+
file_bytes = file.read()
22+
incorrect_file_bytes = file_bytes[:-1] + b"\x81"
23+
with open("3_3-3-2.ttb", "wb") as file:
24+
file.write(incorrect_file_bytes)
25+
Reader((3, 3), 3, 2, two_piece_hash)
26+
with open("3_3-3-2.ttb", "wb") as file:
27+
file.write(file_bytes)
Lines changed: 94 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,94 @@
1-
from tictactoe import Board, Move
2-
import numpy
3-
4-
5-
def draw_board():
6-
board = Board()
7-
board.push((0, 0))
8-
board.push((0, 1))
9-
board.push((0, 2))
10-
board.push((1, 1))
11-
board.push((1, 0))
12-
board.push((2, 0))
13-
board.push((1, 2))
14-
board.push((2, 2))
15-
board.push((2, 1))
16-
return board
17-
18-
19-
def x_win_board():
20-
board = Board()
21-
board.push((0, 0))
22-
board.push((0, 1))
23-
board.push((0, 2))
24-
board.push((1, 1))
25-
board.push((1, 0))
26-
board.push((1, 2))
27-
board.push((2, 0))
28-
return board
29-
30-
31-
def o_win_board():
32-
board = Board()
33-
board.push((0, 0))
34-
board.push((0, 1))
35-
board.push((0, 2))
36-
board.push((1, 1))
37-
board.push((1, 0))
38-
board.push((2, 0))
39-
board.push((1, 2))
40-
board.push((2, 1))
41-
return board
42-
43-
44-
def unfinished_board():
45-
board = Board()
46-
board.push((0, 0))
47-
board.push((0, 1))
48-
board.push((0, 2))
49-
board.push((1, 1))
50-
board.push((1, 0))
51-
board.push((2, 0))
52-
board.push((1, 2))
53-
board.push((2, 2))
54-
return board
55-
56-
57-
def test_result():
58-
assert x_win_board().result() == 1
59-
assert o_win_board().result() == 2
60-
assert draw_board().result() == 0
61-
assert unfinished_board().result() is None
62-
63-
64-
def test_copy():
65-
board = unfinished_board()
66-
assert board.possible_moves().tolist() == board.copy().possible_moves().tolist()
67-
68-
69-
def test_inbound_outofbounds():
70-
board = unfinished_board()
71-
assert board.in_bounds(numpy.array([2, 2]))
72-
assert board.out_of_bounds(numpy.array([3, 2]))
73-
74-
75-
def test_move():
76-
assert Move((2, 2)).str_move == '2-2'
77-
assert Move(str_move='1-2').coordinate_move == (1, 2)
78-
79-
80-
def test_illegal_move():
81-
board = unfinished_board()
82-
try:
83-
board.push((0, 0))
84-
assert False
85-
except ValueError:
86-
assert True
87-
88-
89-
if __name__ == "__main__":
90-
test_result()
91-
test_copy()
92-
test_inbound_outofbounds()
93-
test_move()
94-
test_illegal_move()
1+
from tictactoe import Board, Move
2+
import numpy
3+
4+
5+
def draw_board():
6+
board = Board()
7+
board.push((0, 0))
8+
board.push((0, 1))
9+
board.push((0, 2))
10+
board.push((1, 1))
11+
board.push((1, 0))
12+
board.push((2, 0))
13+
board.push((1, 2))
14+
board.push((2, 2))
15+
board.push((2, 1))
16+
return board
17+
18+
19+
def x_win_board():
20+
board = Board()
21+
board.push((0, 0))
22+
board.push((0, 1))
23+
board.push((0, 2))
24+
board.push((1, 1))
25+
board.push((1, 0))
26+
board.push((1, 2))
27+
board.push((2, 0))
28+
return board
29+
30+
31+
def o_win_board():
32+
board = Board()
33+
board.push((0, 0))
34+
board.push((0, 1))
35+
board.push((0, 2))
36+
board.push((1, 1))
37+
board.push((1, 0))
38+
board.push((2, 0))
39+
board.push((1, 2))
40+
board.push((2, 1))
41+
return board
42+
43+
44+
def unfinished_board():
45+
board = Board()
46+
board.push((0, 0))
47+
board.push((0, 1))
48+
board.push((0, 2))
49+
board.push((1, 1))
50+
board.push((1, 0))
51+
board.push((2, 0))
52+
board.push((1, 2))
53+
board.push((2, 2))
54+
return board
55+
56+
57+
def test_result():
58+
assert x_win_board().result() == 1
59+
assert o_win_board().result() == 2
60+
assert draw_board().result() == 0
61+
assert unfinished_board().result() is None
62+
63+
64+
def test_copy():
65+
board = unfinished_board()
66+
assert board.possible_moves().tolist() == board.copy().possible_moves().tolist()
67+
68+
69+
def test_inbound_outofbounds():
70+
board = unfinished_board()
71+
assert board.in_bounds(numpy.array([2, 2]))
72+
assert board.out_of_bounds(numpy.array([3, 2]))
73+
74+
75+
def test_move():
76+
assert Move((2, 2)).str_move == "2-2"
77+
assert Move(str_move="1-2").coordinate_move == (1, 2)
78+
79+
80+
def test_illegal_move():
81+
board = unfinished_board()
82+
try:
83+
board.push((0, 0))
84+
assert False
85+
except ValueError:
86+
assert True
87+
88+
89+
if __name__ == "__main__":
90+
test_result()
91+
test_copy()
92+
test_inbound_outofbounds()
93+
test_move()
94+
test_illegal_move()

0 commit comments

Comments
 (0)