3 ms·
This Python scripts generates a playable HTML-only Tic Tac Toe game. The generated file is 560KB, that is more than 300 times smaller. Also it works in any brow
by p4bl0 3y ago
This Python scripts generates a playable HTML-only Tic Tac Toe game. The generated file is 560KB, that is more than 300 times smaller. Also it works in any browser not just Chrome, probably even TUI browsers like w3m or links for that matter ^^. It generates all and only the valid game states, that is 5478 boards. It may be optimized further.
Play it here: https://pablo.rauzy.name/dev/ttt.html https://pablo.rauzy.name/dev/ttt.html
from copy import deepcopy
opponent = {'X':'O','O':'X'}
generated_boards = []
def board_id (board):
return ''.join([''.join(row) for row in board])
def winning (board, player):
return player == board[0][0] == board[0][1] == board[0][2] \
or player == board[1][0] == board[1][1] == board[1][2] \
or player == board[2][0] == board[2][1] == board[2][2] \
or player == board[0][0] == board[1][0] == board[2][0] \
or player == board[0][1] == board[1][1] == board[2][1] \
or player == board[0][2] == board[1][2] == board[2][2] \
or player == board[0][0] == board[1][1] == board[2][2] \
or player == board[0][2] == board[1][1] == board[2][0]
def full (board):
return '_' not in [cell for row in board for cell in row]
def print_board (board, player):
print(f'<hr id="{board_id(board)}">', end="")
links = True
if winning(board, opponent[player]):
links = False
print("WIN:")
elif full(board):
links = False
print("DRAW:")
for row in range(0, 3):
for col in range(0, 3):
if links and board[row][col] == '_':
board[row][col] = player
print(f'<a href="#{board_id(board)}">_</a>', end="")
board[row][col] = '_'
else:
print(f'{board[row][col]}', end="")
if row != 2: print('')
return links
def next_boards (board, player):
boards = []
for row in range(0, 3):
for col in range(0, 3):
if board[row][col] == '_':
board[row][col] = player
bid = player + board_id(board)
if bid not in generated_boards:
boards.append(deepcopy(board))
generated_boards.append(bid)
board[row][col] = '_'
return boards
def print_boards (board, player):
if not print_board(board, player): return
for board in next_boards(board, player):
print_boards(board, opponent[player])
print("<pre>")
print_boards([['_' for _ in range(0, 3)] for _ in range(0, 3)], 'X')
- xnx 3y agoVery cool! I bet another few KB of CSS could make it indistinguishable from a full "app" implementation.
- p4bl0 3y agoNot even a few KB of CSS. A single small line is sufficient: pre{font-size:20pt;display:none}:target{display:block} However this CSS needs each board state to be in its own <pre> element, which makes the file a bit larger in total (598KB) Here is the result: https://pablo.rauzy.name/dev/prettty.html https://pablo.rauzy.name/dev/prettty.html
- p4bl0 3y agoFunny things: there are 4578 valid boards. Of those, 16 are draw positions, and 942 are winning positions. Among the winning positions, 626 are winning position for the player who starts, and 316 for the second player.
- deaddodo 3y agoThe only way to win tic-tac-toe consistently is with a double gambit (which the starting player gets an advantage on). E.G. something like: X_X _O_ X_O Problem is, as soon as the other player knows this, every game will end in a draw as they will play to block versus win.
- nodoodles 3y agoClever! With bit of more complexity in generation, you can shave off good number of bytes by using shorter id's, I think? 3 characters per id in base36 should do, in reality even less on average, starting from #0. >100KB reduction?
- p4bl0 3y agoI just saw your comment! I was given the same idea on Mastodon. I did it and got down to 412KB. I'm using a custom base74 so that IDs are max 2 chars. 74 + 74*74 = 5550 which is just above the 5478 game states that have to be represented. See https://news.ycombinator.com/item?id=38275082 https://news.ycombinator.com/item?id=38275082