Implementing Tic Tac Toe with 170mb of HTML – No JavaScript or CSS
portswigger.net
portswigger.net
Some code I wrote to generate a small table using BDDs is at https://codewords.recurse.com/issues/four/the-language-of-ch...
Also from there:
> In a 6th-grade science fair Steve Wozniak did [a tic-tac-toe machine] much better: “about 100 transistors and about 100 diodes.” How? Beats me!
https://gist.github.com/p4bl0-/abf4960f07045d7a443230a46e73b...
https://gist.github.com/p4bl0-/abf4960f07045d7a443230a46e73b...
170MB seems like an awefull lot.
I think this is going to lead to a lot of redundant states, isn't it, because there are multiple paths to get to a particular board layout?
See my script here: https://news.ycombinator.com/item?id=38266987
The demo indicates it only works on Chrome, however it worked fine on Firefox on iOS (so just Safari, presumably).
They could have used any language to do this really. I think the title means "the final game runs without JS or CSS"
So if these vectors/sploits were patched out, the game wouldn't work anymore?
(Impressive, thanks for sharing!)
In any case, this game doesn't involve any of that, it just uses popovers as fancy internal links.
Play it here: 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') 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:
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.3 characters per id in base36 should do, in reality even less on average, starting from #0. >100KB reduction?
74 + 74*74 = 5550 which is just above the 5478 game states that have to be represented.