Compare commits
5 Commits
a391633172
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 33d503105b | |||
| 0a586a33c1 | |||
| e846259282 | |||
| 9f75af0f47 | |||
| 99dfd7fc82 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
|
__pycache__
|
||||||
tictactoecss.sublime-project
|
tictactoecss.sublime-project
|
||||||
tictactoecss.sublime-workspace
|
tictactoecss.sublime-workspace
|
||||||
|
|||||||
1183
readme.html
Normal file
1183
readme.html
Normal file
File diff suppressed because one or more lines are too long
151
readme.md
151
readme.md
@@ -1,7 +1,150 @@
|
|||||||
# Tic Tac Toe CSS
|
# Pure CSS Tic-tac-toe Bot
|
||||||
|
|
||||||
https://eddyerburgh.me/make-responsive-tic-tac-toe-board
|
Tic-tac-toe CSS is a static Tic-tac-toe *bot* in pure CSS *without* any JavaScript.
|
||||||
|
I had the idea for this project when I learned the basics of CSS many years ago.
|
||||||
|
For me the hard part of this project was to figure out a way to respond to different board constellations in CSS.
|
||||||
|
Once I had understood how to make rule based decisions it was easy to generate the necessary HTML and CSS for the bot in Python using the [Jinja template engine](http://jinja.pocoo.org).
|
||||||
|
|
||||||
https://css-tricks.com/game-life/
|
## HTML and CSS Generation
|
||||||
|
|
||||||
https://codepen.io/ziga-miklic/post/pure-css-tic-tac-toe
|
Luckily, [Žiga Miklič](https://codepen.io/ziga-miklic/)
|
||||||
|
has created a dual player
|
||||||
|
[Tic-tac-toe](https://codepen.io/ziga-miklic/post/pure-css-tic-tac-toe)
|
||||||
|
version in pure CSS.
|
||||||
|
Without his example it would have been hard for me to come up with a solution as elegant as his (or possibly a solution at all).
|
||||||
|
The idea is to generate a complete Tic-tac-toe board for each turn.
|
||||||
|
Each field of the board consists of an `<input>` tag to mark the field as checked and a `<label>` tag to visualize it.
|
||||||
|
This Jinja template generates nine boards - one for each turn.
|
||||||
|
The classes `.field-n` and `.turn-n` help to control the game flow
|
||||||
|
while `.row-n` and `.col-n` place the field on the board.
|
||||||
|
|
||||||
|
~~~html
|
||||||
|
{% for turn in range(9) %}
|
||||||
|
<!-- turn-{{turn}} -->
|
||||||
|
{% for row in range(3) %}
|
||||||
|
{% for col in range(3) %}
|
||||||
|
<input class="field-{{row * 3 + col}} row-{{row}} col-{{col}} turn-{{turn}}"
|
||||||
|
id="block-{{turn}}-{{row}}-{{col}}" type="radio">
|
||||||
|
<label class="turn-{{turn}}" for="block-{{turn}}-{{row}}-{{col}}"></label>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
At the start of the game the CSS rules hide all fields but `.turn-0`.
|
||||||
|
For each turn the checked field moves to the front and the next board becomes visible.
|
||||||
|
In fact, only every second board moves to the front because the other move is done by the bot (note that the z-index is incremented by two).
|
||||||
|
|
||||||
|
~~~css
|
||||||
|
{% for turn in turns_player %}
|
||||||
|
.tic-tac-toe input.turn-{{turn}}:checked + label {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
z-index: 10 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.turn-{{turn}}:checked ~ .turn-{{turn + 2}} + label {
|
||||||
|
z-index: {{turn + 2}};
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Using the `.turn` and `.field` classes we can now tell the bot to make certain moves.
|
||||||
|
For example, let's say the player marks the first field `.field-0` in turn `.turn-0` and the second field `.field-1` in turn `.turn-2`.
|
||||||
|
We do not want the player to win by marking all fields in the first row and tell the bot to mark the third field (`.field-2`) of that row.
|
||||||
|
|
||||||
|
~~~css
|
||||||
|
.tic-tac-toe
|
||||||
|
input.turn-0.field-0:checked ~ input.turn-2.field-1:checked ~
|
||||||
|
input.turn-3.field-2 + label {
|
||||||
|
display: block;
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
z-index: 10 !important;
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
We do not care which field has been checked in `.turn-1` because the bot rule generation algorithm keeps track of the bot's moves.
|
||||||
|
The CSS file contains a couple of other rules to create the Tic-tac-toe grid, pretty check marks, and a message for when the game has ended.
|
||||||
|
Credits to this [JS fiddle](https://jsfiddle.net/stackmanoz/r6E9p/) for teaching me how to make crosses in CSS.
|
||||||
|
I was not aware of the rotate property.
|
||||||
|
The next step is to generate the CSS rules for the bot.
|
||||||
|
|
||||||
|
|
||||||
|
## Bot Rules Generation
|
||||||
|
|
||||||
|
There has to be a CSS rule for every possible sequence of moves by the human player.
|
||||||
|
We define a `Move` as an integer two-tuple.
|
||||||
|
The first element represents the turn and the second element the index of the field that is selected by the move.
|
||||||
|
|
||||||
|
~~~python
|
||||||
|
Move = namedtuple("Move", ["turn", "field"])
|
||||||
|
~~~
|
||||||
|
|
||||||
|
We define a `Rule` as a two-tuple where the first element is a sequence of moves and the second element is the next move, i.e. the response to the player moves by the bot.
|
||||||
|
|
||||||
|
~~~python
|
||||||
|
Rule = namedtuple("Rule", ["moves", "next_move"])
|
||||||
|
~~~
|
||||||
|
|
||||||
|
One might think that there are too many rules to cover all potential player moves statically.
|
||||||
|
However, it can be shown that this is not an issue.
|
||||||
|
Tic-tac-toe is a simple game with only *9!* possible sequences of moves.
|
||||||
|
In the first round there are nine free fields, then eight, then seven and so on.
|
||||||
|
The bot can only select one of its available options per turn.
|
||||||
|
Hence, the number of possible sequences further reduces to *9 * 7 * 5 * 3 * 1 = 945*.
|
||||||
|
|
||||||
|
To compute the rules we implement a recursive procedure `get_rules`.
|
||||||
|
The procedure takes a board, the current turn, a partial rule (a rule including all moves till this point), and a flag whether indicating if it is the bot's or the player's turn.
|
||||||
|
The procedure terminates when either of the players has won or when the board is full.
|
||||||
|
In either case we create a rule which we append to `draw_rules` and `win_rules` which we use to display a nice message at the end of the game.
|
||||||
|
|
||||||
|
If the game is still open and it is not the bot's turn we iterate over all potential moves by the player.
|
||||||
|
For each move we make another call to `get_rules` with the new board and partial rule.
|
||||||
|
|
||||||
|
If it is the bot's turn we also iterate over all potential moves and call a procedure `get_equity` for every new potential board.
|
||||||
|
We choose the move with the highest equity for us and the lowest equity for our opponent.
|
||||||
|
For this move we then create a new final rule and call `get_rules` recursively with the updated board and the current rule (not the new rule!).
|
||||||
|
|
||||||
|
The procedure `get_equity` takes a board and a player and returns the expected value for the constellation.
|
||||||
|
If the player wins the game the equity is one, if they lose it is minus one, and a draw means zero.
|
||||||
|
If the game is undecided `get_equity` calls itself recursively with every potential move and the opposite player.
|
||||||
|
The procedure assumes that the other player always selects the move that maximizes their equity.
|
||||||
|
By choosing the move with the minimal equity for the other player the current player can maximize their own equity:
|
||||||
|
|
||||||
|
~~~python
|
||||||
|
def get_equity(board, player):
|
||||||
|
if player_won(board, player):
|
||||||
|
return 1
|
||||||
|
elif player_lost(board, player):
|
||||||
|
return -1
|
||||||
|
elif game_over(board):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
|
||||||
|
equities = []
|
||||||
|
for field_index in get_unchecked_fields(board):
|
||||||
|
new_board = get_new_board(board, field_index, player)
|
||||||
|
equity = get_equity(new_board, other_player)
|
||||||
|
equities.append(equity)
|
||||||
|
return min(equities) * -1
|
||||||
|
~~~
|
||||||
|
|
||||||
|
To avoid recomputing of equities we use `lru_cache` from `functools` to cache existing equities.
|
||||||
|
Once we have computed all rules we can feed them to Jinja to generate the respective CSS code and thus have generated a static Tic-tac-toe bot in pure CSS.
|
||||||
|
|
||||||
|
~~~css
|
||||||
|
{% for rule in bot_move_rules %}
|
||||||
|
.tic-tac-toe
|
||||||
|
{% for move in rule.moves -%}
|
||||||
|
input.turn-{{move.turn}}.field-{{move.field}}:checked ~
|
||||||
|
{% endfor -%}
|
||||||
|
input.turn-{{rule.next_move.turn}}.field-{{rule.next_move.field}} + label {
|
||||||
|
display: block;
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
z-index: 10 !important;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
~~~
|
||||||
|
|||||||
120
rules.py
Normal file
120
rules.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
from functools import lru_cache
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
def get_tictactoe_rules():
|
||||||
|
PLAYER_1 = "X"
|
||||||
|
PLAYER_2 = "O"
|
||||||
|
UNCHECKED = "_"
|
||||||
|
Rule = namedtuple("Rule", ["moves", "next_move"])
|
||||||
|
Move = namedtuple("Move", ["turn", "field"])
|
||||||
|
|
||||||
|
def all_rows(board):
|
||||||
|
""" Returns all rows of a Sudoku field. """
|
||||||
|
f = board
|
||||||
|
return [
|
||||||
|
f[0:3], f[3:6], f[6:9], # horizontals
|
||||||
|
f[0:7:3], f[1:8:3], f[2:9:3], # verticals
|
||||||
|
f[0:9:4], f[2:7:2] # diagonals
|
||||||
|
]
|
||||||
|
|
||||||
|
assert(all_rows(list(range(9))) == [
|
||||||
|
[0, 1, 2], [3, 4, 5], [6, 7, 8],
|
||||||
|
[0, 3, 6], [1, 4, 7], [2, 5, 8],
|
||||||
|
[0, 4, 8], [2, 4, 6]])
|
||||||
|
|
||||||
|
def all_equal_to(row, value):
|
||||||
|
""" Returns True if all elements in row are
|
||||||
|
equal to value. Returns False otherwise. """
|
||||||
|
for elem in row:
|
||||||
|
if elem != value:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def player_won(board, player):
|
||||||
|
for row in all_rows(board):
|
||||||
|
if all_equal_to(row, player):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def player_lost(board, player):
|
||||||
|
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
|
||||||
|
return player_won(board, other_player)
|
||||||
|
|
||||||
|
def game_over(board):
|
||||||
|
for field in board:
|
||||||
|
if field == UNCHECKED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_unchecked_fields(board):
|
||||||
|
return [field_index for field_index in range(len(board))
|
||||||
|
if board[field_index] == UNCHECKED]
|
||||||
|
|
||||||
|
def get_new_board(board, field_index, player):
|
||||||
|
new_board = list(board)
|
||||||
|
new_board[field_index] = player
|
||||||
|
return tuple(new_board)
|
||||||
|
|
||||||
|
def get_empty_board():
|
||||||
|
return tuple([UNCHECKED for _ in range(9)])
|
||||||
|
|
||||||
|
@lru_cache(maxsize=2**16)
|
||||||
|
def get_equity(board, player):
|
||||||
|
if player_won(board, player):
|
||||||
|
return 1
|
||||||
|
elif player_lost(board, player):
|
||||||
|
return -1
|
||||||
|
elif game_over(board):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
other_player = PLAYER_2 if player == PLAYER_1 else PLAYER_1
|
||||||
|
equities = []
|
||||||
|
for field_index in get_unchecked_fields(board):
|
||||||
|
new_board = get_new_board(board, field_index, player)
|
||||||
|
equity = get_equity(new_board, other_player)
|
||||||
|
equities.append(equity)
|
||||||
|
return min(equities) * -1
|
||||||
|
|
||||||
|
bot_move_rules = []
|
||||||
|
draw_rules = []
|
||||||
|
win_rules = []
|
||||||
|
|
||||||
|
def get_rules(current_board, turn, current_rule, bots_turn):
|
||||||
|
|
||||||
|
if player_won(current_board, PLAYER_1):
|
||||||
|
raise Exception("If we got here our bot screwed up.")
|
||||||
|
|
||||||
|
if player_won(current_board, PLAYER_2):
|
||||||
|
rule = Rule(current_rule.moves, Move(turn, None))
|
||||||
|
win_rules.append(rule)
|
||||||
|
return
|
||||||
|
|
||||||
|
if game_over(current_board):
|
||||||
|
rule = Rule(current_rule.moves, Move(turn, None))
|
||||||
|
draw_rules.append(rule)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not bots_turn:
|
||||||
|
for field_index in get_unchecked_fields(current_board):
|
||||||
|
new_board = get_new_board(current_board, field_index, PLAYER_1)
|
||||||
|
new_moves = current_rule.moves + [Move(turn, field_index)]
|
||||||
|
new_current_rule = Rule(new_moves, None)
|
||||||
|
get_rules(new_board, turn + 1, new_current_rule, True)
|
||||||
|
else:
|
||||||
|
equities = []
|
||||||
|
for field_index in get_unchecked_fields(current_board):
|
||||||
|
new_board = get_new_board(current_board, field_index, PLAYER_2)
|
||||||
|
equity = get_equity(new_board, PLAYER_1)
|
||||||
|
equities.append((equity, field_index))
|
||||||
|
new_index = min(equities)[1]
|
||||||
|
new_board = get_new_board(current_board, new_index, PLAYER_2)
|
||||||
|
|
||||||
|
bot_rule = Rule(current_rule.moves, Move(turn, new_index))
|
||||||
|
bot_move_rules.append(bot_rule)
|
||||||
|
get_rules(new_board, turn + 1, current_rule, False)
|
||||||
|
|
||||||
|
EMPTY_BOARD = get_empty_board()
|
||||||
|
get_rules(EMPTY_BOARD, 0, Rule([], None), False)
|
||||||
|
|
||||||
|
return (bot_move_rules, draw_rules, win_rules)
|
||||||
248
template.tictactoe.css
Normal file
248
template.tictactoe.css
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
body {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe {
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
height: 440px;
|
||||||
|
width: 440px;
|
||||||
|
margin: 50px auto 30px auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .grid {
|
||||||
|
background-color: grey;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .horizontal-bar-1 {
|
||||||
|
top: 140px;
|
||||||
|
bottom: 290px;
|
||||||
|
height: 10px;
|
||||||
|
width: 440px
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .horizontal-bar-2 {
|
||||||
|
top: 290px;
|
||||||
|
bottom: 140px;
|
||||||
|
height: 10px;
|
||||||
|
width: 440px
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .vertical-bar-1 {
|
||||||
|
left: 140px;
|
||||||
|
right: 290px;
|
||||||
|
height: 440px;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .vertical-bar-2 {
|
||||||
|
left: 290px;
|
||||||
|
right: 140px;
|
||||||
|
height: 440px;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe label {
|
||||||
|
height: 140px;
|
||||||
|
width: 140px;
|
||||||
|
display: none;
|
||||||
|
opacity: 0.0;
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
-moz-transition: background-color 0.3s;
|
||||||
|
-o-transition: background-color 0.3s;
|
||||||
|
-webkit-transition: background-color 0.3s;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe label:hover {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.turn-0 + label {
|
||||||
|
z-index: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.col-0 + label {
|
||||||
|
left: 0px;
|
||||||
|
right: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.col-1 + label {
|
||||||
|
left: 150px;
|
||||||
|
right: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.col-2 + label {
|
||||||
|
left: 300px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.row-0 + label {
|
||||||
|
top: 0px;
|
||||||
|
bottom: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.row-1 + label {
|
||||||
|
top: 150px;
|
||||||
|
bottom: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.row-2 + label {
|
||||||
|
top: 300px;
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* CSS for creating the O and X check marks on the field. */
|
||||||
|
.tic-tac-toe .circle {
|
||||||
|
position: absolute;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
margin: 10px;
|
||||||
|
border: 10px solid #dc685a;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .cross {
|
||||||
|
position: absolute;
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
display: block;
|
||||||
|
transform:rotate(45deg);
|
||||||
|
-ms-transform:rotate(45deg);
|
||||||
|
-webkit-transform:rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .cross:before, .cross:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
background: #78bec5;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .cross:before {
|
||||||
|
top: 65px;
|
||||||
|
bottom: 65px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
height: 10px;
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .cross:after {
|
||||||
|
left: 65px;
|
||||||
|
right: 65px;
|
||||||
|
height: 140px;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .end {
|
||||||
|
width: 440px;
|
||||||
|
height: 440px;
|
||||||
|
padding-top: 125px;
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
background-color: #ecaf4f;
|
||||||
|
color: #3d4250;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 11;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .end h1 {
|
||||||
|
margin: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe .end a {
|
||||||
|
background-color: #3d4250;
|
||||||
|
border-radius: 3px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Rules for player moves. Make selected field permanently visible and make next
|
||||||
|
board visible. The next player board is the current turn + 2 because every
|
||||||
|
second board is for the bot.
|
||||||
|
*/
|
||||||
|
{%- for turn in turns_player %}
|
||||||
|
.tic-tac-toe input.turn-{{turn}}:checked + label {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
z-index: 10 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe input.turn-{{turn}}:checked ~ .turn-{{turn + 2}} + label {
|
||||||
|
z-index: {{turn + 2}};
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
/* BEGIN: bot_move_rules */
|
||||||
|
{% for rule in bot_move_rules %}
|
||||||
|
.tic-tac-toe
|
||||||
|
{% for move in rule.moves -%}
|
||||||
|
input.turn-{{move.turn}}.field-{{move.field}}:checked ~
|
||||||
|
{% endfor -%}
|
||||||
|
input.turn-{{rule.next_move.turn}}.field-{{rule.next_move.field}} + label {
|
||||||
|
display: block;
|
||||||
|
cursor: default;
|
||||||
|
opacity: 1.0;
|
||||||
|
z-index: 10 !important;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
/* END: bot_move_rules */
|
||||||
|
|
||||||
|
|
||||||
|
/* BEGING: Rules for when the bot has won. */
|
||||||
|
{% for rule in win_rules %}
|
||||||
|
.tic-tac-toe
|
||||||
|
{% for move in rule.moves -%}
|
||||||
|
input.turn-{{move.turn}}.field-{{move.field}}:checked ~
|
||||||
|
{% endfor -%}
|
||||||
|
.end {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe
|
||||||
|
{% for move in rule.moves -%}
|
||||||
|
input.turn-{{move.turn}}.field-{{move.field}}:checked ~
|
||||||
|
{% endfor -%}
|
||||||
|
.end > h1:before {
|
||||||
|
content: "Bot wins!" !important;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
/* END: Rules for when the bot has won. */
|
||||||
|
|
||||||
|
|
||||||
|
/* BEGING: draws */
|
||||||
|
{% for rule in draw_rules %}
|
||||||
|
.tic-tac-toe
|
||||||
|
{% for move in rule.moves -%}
|
||||||
|
input.turn-{{move.turn}}.field-{{move.field}}:checked ~
|
||||||
|
{% endfor -%}
|
||||||
|
.end {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tic-tac-toe
|
||||||
|
{% for move in rule.moves -%}
|
||||||
|
input.turn-{{move.turn}}.field-{{move.field}}:checked ~
|
||||||
|
{% endfor -%}
|
||||||
|
.end > h1:before {
|
||||||
|
content: "Tied!" !important;
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
/* END: draws */
|
||||||
35
template.tictactoe.html
Normal file
35
template.tictactoe.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Tic Tac Toe CSS</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="tictactoe.css">
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic' rel='stylesheet'>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="tic-tac-toe">
|
||||||
|
<div class="grid horizontal-bar-1"></div>
|
||||||
|
<div class="grid horizontal-bar-2"></div>
|
||||||
|
<div class="grid vertical-bar-1"></div>
|
||||||
|
<div class="grid vertical-bar-2"></div>
|
||||||
|
|
||||||
|
{% for turn in range(9) %}
|
||||||
|
<!-- turn-{{turn}} -->
|
||||||
|
{% for row in range(3) %}
|
||||||
|
{% for col in range(3) %}
|
||||||
|
<input class="field-{{row * 3 + col}} row-{{row}} col-{{col}} turn-{{turn}}" id="block-{{turn}}-{{row}}-{{col}}" type="radio">
|
||||||
|
<label class="turn-{{turn}}" for="block-{{turn}}-{{row}}-{{col}}">
|
||||||
|
{% if (turn % 2 == 0) %}
|
||||||
|
<div class="circle"></div>
|
||||||
|
{% else %}
|
||||||
|
<div class="cross"></div>
|
||||||
|
{% endif %}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
<div class="end">
|
||||||
|
<h1></h1>
|
||||||
|
<a href="">Play again.</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
23405
tictactoe.css
23405
tictactoe.css
File diff suppressed because it is too large
Load Diff
659
tictactoe.html
659
tictactoe.html
@@ -2,189 +2,668 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Tic Tac Toe CSS</title>
|
<title>Tic Tac Toe CSS</title>
|
||||||
<link rel="stylesheet" type="text/css" href="tictactoe.css">
|
<link rel="stylesheet" type="text/css" href="tictactoe.css">
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic' rel='stylesheet'>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="tic-tac-toe">
|
<div class="tic-tac-toe">
|
||||||
|
<div class="grid horizontal-bar-1"></div>
|
||||||
|
<div class="grid horizontal-bar-2"></div>
|
||||||
|
<div class="grid vertical-bar-1"></div>
|
||||||
|
<div class="grid vertical-bar-2"></div>
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-0 -->
|
<!-- turn-0 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-0" id="block-0-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-0" id="block-0-0-0" type="radio">
|
||||||
<label class="turn-0" for="block-0-0-0"></label>
|
<label class="turn-0" for="block-0-0-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-0" id="block-0-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-0" id="block-0-0-1" type="radio">
|
||||||
<label class="turn-0" for="block-0-0-1"></label>
|
<label class="turn-0" for="block-0-0-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-0" id="block-0-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-0" id="block-0-0-2" type="radio">
|
||||||
<label class="turn-0" for="block-0-0-2"></label>
|
<label class="turn-0" for="block-0-0-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-0" id="block-0-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-0" id="block-0-1-0" type="radio">
|
||||||
<label class="turn-0" for="block-0-1-0"></label>
|
<label class="turn-0" for="block-0-1-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-0" id="block-0-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-0" id="block-0-1-1" type="radio">
|
||||||
<label class="turn-0" for="block-0-1-1"></label>
|
<label class="turn-0" for="block-0-1-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-0" id="block-0-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-0" id="block-0-1-2" type="radio">
|
||||||
<label class="turn-0" for="block-0-1-2"></label>
|
<label class="turn-0" for="block-0-1-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-0" id="block-0-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-0" id="block-0-2-0" type="radio">
|
||||||
<label class="turn-0" for="block-0-2-0"></label>
|
<label class="turn-0" for="block-0-2-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-0" id="block-0-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-0" id="block-0-2-1" type="radio">
|
||||||
<label class="turn-0" for="block-0-2-1"></label>
|
<label class="turn-0" for="block-0-2-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-0" id="block-0-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-0" id="block-0-2-2" type="radio">
|
||||||
<label class="turn-0" for="block-0-2-2"></label>
|
<label class="turn-0" for="block-0-2-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-1 -->
|
<!-- turn-1 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-1" id="block-1-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-1" id="block-1-0-0" type="radio">
|
||||||
<label class="turn-1" for="block-1-0-0"></label>
|
<label class="turn-1" for="block-1-0-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-1" id="block-1-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-1" id="block-1-0-1" type="radio">
|
||||||
<label class="turn-1" for="block-1-0-1"></label>
|
<label class="turn-1" for="block-1-0-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-1" id="block-1-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-1" id="block-1-0-2" type="radio">
|
||||||
<label class="turn-1" for="block-1-0-2"></label>
|
<label class="turn-1" for="block-1-0-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-1" id="block-1-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-1" id="block-1-1-0" type="radio">
|
||||||
<label class="turn-1" for="block-1-1-0"></label>
|
<label class="turn-1" for="block-1-1-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-1" id="block-1-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-1" id="block-1-1-1" type="radio">
|
||||||
<label class="turn-1" for="block-1-1-1"></label>
|
<label class="turn-1" for="block-1-1-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-1" id="block-1-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-1" id="block-1-1-2" type="radio">
|
||||||
<label class="turn-1" for="block-1-1-2"></label>
|
<label class="turn-1" for="block-1-1-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-1" id="block-1-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-1" id="block-1-2-0" type="radio">
|
||||||
<label class="turn-1" for="block-1-2-0"></label>
|
<label class="turn-1" for="block-1-2-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-1" id="block-1-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-1" id="block-1-2-1" type="radio">
|
||||||
<label class="turn-1" for="block-1-2-1"></label>
|
<label class="turn-1" for="block-1-2-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-1" id="block-1-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-1" id="block-1-2-2" type="radio">
|
||||||
<label class="turn-1" for="block-1-2-2"></label>
|
<label class="turn-1" for="block-1-2-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-2 -->
|
<!-- turn-2 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-2" id="block-2-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-2" id="block-2-0-0" type="radio">
|
||||||
<label class="turn-2" for="block-2-0-0"></label>
|
<label class="turn-2" for="block-2-0-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-2" id="block-2-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-2" id="block-2-0-1" type="radio">
|
||||||
<label class="turn-2" for="block-2-0-1"></label>
|
<label class="turn-2" for="block-2-0-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-2" id="block-2-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-2" id="block-2-0-2" type="radio">
|
||||||
<label class="turn-2" for="block-2-0-2"></label>
|
<label class="turn-2" for="block-2-0-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-2" id="block-2-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-2" id="block-2-1-0" type="radio">
|
||||||
<label class="turn-2" for="block-2-1-0"></label>
|
<label class="turn-2" for="block-2-1-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-2" id="block-2-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-2" id="block-2-1-1" type="radio">
|
||||||
<label class="turn-2" for="block-2-1-1"></label>
|
<label class="turn-2" for="block-2-1-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-2" id="block-2-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-2" id="block-2-1-2" type="radio">
|
||||||
<label class="turn-2" for="block-2-1-2"></label>
|
<label class="turn-2" for="block-2-1-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-2" id="block-2-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-2" id="block-2-2-0" type="radio">
|
||||||
<label class="turn-2" for="block-2-2-0"></label>
|
<label class="turn-2" for="block-2-2-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-2" id="block-2-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-2" id="block-2-2-1" type="radio">
|
||||||
<label class="turn-2" for="block-2-2-1"></label>
|
<label class="turn-2" for="block-2-2-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-2" id="block-2-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-2" id="block-2-2-2" type="radio">
|
||||||
<label class="turn-2" for="block-2-2-2"></label>
|
<label class="turn-2" for="block-2-2-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-3 -->
|
<!-- turn-3 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-3" id="block-3-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-3" id="block-3-0-0" type="radio">
|
||||||
<label class="turn-3" for="block-3-0-0"></label>
|
<label class="turn-3" for="block-3-0-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-3" id="block-3-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-3" id="block-3-0-1" type="radio">
|
||||||
<label class="turn-3" for="block-3-0-1"></label>
|
<label class="turn-3" for="block-3-0-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-3" id="block-3-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-3" id="block-3-0-2" type="radio">
|
||||||
<label class="turn-3" for="block-3-0-2"></label>
|
<label class="turn-3" for="block-3-0-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-3" id="block-3-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-3" id="block-3-1-0" type="radio">
|
||||||
<label class="turn-3" for="block-3-1-0"></label>
|
<label class="turn-3" for="block-3-1-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-3" id="block-3-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-3" id="block-3-1-1" type="radio">
|
||||||
<label class="turn-3" for="block-3-1-1"></label>
|
<label class="turn-3" for="block-3-1-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-3" id="block-3-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-3" id="block-3-1-2" type="radio">
|
||||||
<label class="turn-3" for="block-3-1-2"></label>
|
<label class="turn-3" for="block-3-1-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-3" id="block-3-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-3" id="block-3-2-0" type="radio">
|
||||||
<label class="turn-3" for="block-3-2-0"></label>
|
<label class="turn-3" for="block-3-2-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-3" id="block-3-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-3" id="block-3-2-1" type="radio">
|
||||||
<label class="turn-3" for="block-3-2-1"></label>
|
<label class="turn-3" for="block-3-2-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-3" id="block-3-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-3" id="block-3-2-2" type="radio">
|
||||||
<label class="turn-3" for="block-3-2-2"></label>
|
<label class="turn-3" for="block-3-2-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-4 -->
|
<!-- turn-4 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-4" id="block-4-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-4" id="block-4-0-0" type="radio">
|
||||||
<label class="turn-4" for="block-4-0-0"></label>
|
<label class="turn-4" for="block-4-0-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-4" id="block-4-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-4" id="block-4-0-1" type="radio">
|
||||||
<label class="turn-4" for="block-4-0-1"></label>
|
<label class="turn-4" for="block-4-0-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-4" id="block-4-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-4" id="block-4-0-2" type="radio">
|
||||||
<label class="turn-4" for="block-4-0-2"></label>
|
<label class="turn-4" for="block-4-0-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-4" id="block-4-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-4" id="block-4-1-0" type="radio">
|
||||||
<label class="turn-4" for="block-4-1-0"></label>
|
<label class="turn-4" for="block-4-1-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-4" id="block-4-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-4" id="block-4-1-1" type="radio">
|
||||||
<label class="turn-4" for="block-4-1-1"></label>
|
<label class="turn-4" for="block-4-1-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-4" id="block-4-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-4" id="block-4-1-2" type="radio">
|
||||||
<label class="turn-4" for="block-4-1-2"></label>
|
<label class="turn-4" for="block-4-1-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-4" id="block-4-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-4" id="block-4-2-0" type="radio">
|
||||||
<label class="turn-4" for="block-4-2-0"></label>
|
<label class="turn-4" for="block-4-2-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-4" id="block-4-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-4" id="block-4-2-1" type="radio">
|
||||||
<label class="turn-4" for="block-4-2-1"></label>
|
<label class="turn-4" for="block-4-2-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-4" id="block-4-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-4" id="block-4-2-2" type="radio">
|
||||||
<label class="turn-4" for="block-4-2-2"></label>
|
<label class="turn-4" for="block-4-2-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-5 -->
|
<!-- turn-5 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-5" id="block-5-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-5" id="block-5-0-0" type="radio">
|
||||||
<label class="turn-5" for="block-5-0-0"></label>
|
<label class="turn-5" for="block-5-0-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-5" id="block-5-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-5" id="block-5-0-1" type="radio">
|
||||||
<label class="turn-5" for="block-5-0-1"></label>
|
<label class="turn-5" for="block-5-0-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-5" id="block-5-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-5" id="block-5-0-2" type="radio">
|
||||||
<label class="turn-5" for="block-5-0-2"></label>
|
<label class="turn-5" for="block-5-0-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-5" id="block-5-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-5" id="block-5-1-0" type="radio">
|
||||||
<label class="turn-5" for="block-5-1-0"></label>
|
<label class="turn-5" for="block-5-1-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-5" id="block-5-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-5" id="block-5-1-1" type="radio">
|
||||||
<label class="turn-5" for="block-5-1-1"></label>
|
<label class="turn-5" for="block-5-1-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-5" id="block-5-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-5" id="block-5-1-2" type="radio">
|
||||||
<label class="turn-5" for="block-5-1-2"></label>
|
<label class="turn-5" for="block-5-1-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-5" id="block-5-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-5" id="block-5-2-0" type="radio">
|
||||||
<label class="turn-5" for="block-5-2-0"></label>
|
<label class="turn-5" for="block-5-2-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-5" id="block-5-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-5" id="block-5-2-1" type="radio">
|
||||||
<label class="turn-5" for="block-5-2-1"></label>
|
<label class="turn-5" for="block-5-2-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-5" id="block-5-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-5" id="block-5-2-2" type="radio">
|
||||||
<label class="turn-5" for="block-5-2-2"></label>
|
<label class="turn-5" for="block-5-2-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-6 -->
|
<!-- turn-6 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-6" id="block-6-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-6" id="block-6-0-0" type="radio">
|
||||||
<label class="turn-6" for="block-6-0-0"></label>
|
<label class="turn-6" for="block-6-0-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-6" id="block-6-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-6" id="block-6-0-1" type="radio">
|
||||||
<label class="turn-6" for="block-6-0-1"></label>
|
<label class="turn-6" for="block-6-0-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-6" id="block-6-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-6" id="block-6-0-2" type="radio">
|
||||||
<label class="turn-6" for="block-6-0-2"></label>
|
<label class="turn-6" for="block-6-0-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-6" id="block-6-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-6" id="block-6-1-0" type="radio">
|
||||||
<label class="turn-6" for="block-6-1-0"></label>
|
<label class="turn-6" for="block-6-1-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-6" id="block-6-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-6" id="block-6-1-1" type="radio">
|
||||||
<label class="turn-6" for="block-6-1-1"></label>
|
<label class="turn-6" for="block-6-1-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-6" id="block-6-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-6" id="block-6-1-2" type="radio">
|
||||||
<label class="turn-6" for="block-6-1-2"></label>
|
<label class="turn-6" for="block-6-1-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-6" id="block-6-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-6" id="block-6-2-0" type="radio">
|
||||||
<label class="turn-6" for="block-6-2-0"></label>
|
<label class="turn-6" for="block-6-2-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-6" id="block-6-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-6" id="block-6-2-1" type="radio">
|
||||||
<label class="turn-6" for="block-6-2-1"></label>
|
<label class="turn-6" for="block-6-2-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-6" id="block-6-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-6" id="block-6-2-2" type="radio">
|
||||||
<label class="turn-6" for="block-6-2-2"></label>
|
<label class="turn-6" for="block-6-2-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-7 -->
|
<!-- turn-7 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-7" id="block-7-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-7" id="block-7-0-0" type="radio">
|
||||||
<label class="turn-7" for="block-7-0-0"></label>
|
<label class="turn-7" for="block-7-0-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-7" id="block-7-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-7" id="block-7-0-1" type="radio">
|
||||||
<label class="turn-7" for="block-7-0-1"></label>
|
<label class="turn-7" for="block-7-0-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-7" id="block-7-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-7" id="block-7-0-2" type="radio">
|
||||||
<label class="turn-7" for="block-7-0-2"></label>
|
<label class="turn-7" for="block-7-0-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-7" id="block-7-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-7" id="block-7-1-0" type="radio">
|
||||||
<label class="turn-7" for="block-7-1-0"></label>
|
<label class="turn-7" for="block-7-1-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-7" id="block-7-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-7" id="block-7-1-1" type="radio">
|
||||||
<label class="turn-7" for="block-7-1-1"></label>
|
<label class="turn-7" for="block-7-1-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-7" id="block-7-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-7" id="block-7-1-2" type="radio">
|
||||||
<label class="turn-7" for="block-7-1-2"></label>
|
<label class="turn-7" for="block-7-1-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-7" id="block-7-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-7" id="block-7-2-0" type="radio">
|
||||||
<label class="turn-7" for="block-7-2-0"></label>
|
<label class="turn-7" for="block-7-2-0">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-7" id="block-7-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-7" id="block-7-2-1" type="radio">
|
||||||
<label class="turn-7" for="block-7-2-1"></label>
|
<label class="turn-7" for="block-7-2-1">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-7" id="block-7-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-7" id="block-7-2-2" type="radio">
|
||||||
<label class="turn-7" for="block-7-2-2"></label>
|
<label class="turn-7" for="block-7-2-2">
|
||||||
|
|
||||||
|
<div class="cross"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- turn-8 -->
|
<!-- turn-8 -->
|
||||||
|
|
||||||
|
|
||||||
<input class="field-0 row-0 col-0 turn-8" id="block-8-0-0" type="radio">
|
<input class="field-0 row-0 col-0 turn-8" id="block-8-0-0" type="radio">
|
||||||
<label class="turn-8" for="block-8-0-0"></label>
|
<label class="turn-8" for="block-8-0-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-1 row-0 col-1 turn-8" id="block-8-0-1" type="radio">
|
<input class="field-1 row-0 col-1 turn-8" id="block-8-0-1" type="radio">
|
||||||
<label class="turn-8" for="block-8-0-1"></label>
|
<label class="turn-8" for="block-8-0-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-2 row-0 col-2 turn-8" id="block-8-0-2" type="radio">
|
<input class="field-2 row-0 col-2 turn-8" id="block-8-0-2" type="radio">
|
||||||
<label class="turn-8" for="block-8-0-2"></label>
|
<label class="turn-8" for="block-8-0-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-3 row-1 col-0 turn-8" id="block-8-1-0" type="radio">
|
<input class="field-3 row-1 col-0 turn-8" id="block-8-1-0" type="radio">
|
||||||
<label class="turn-8" for="block-8-1-0"></label>
|
<label class="turn-8" for="block-8-1-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-4 row-1 col-1 turn-8" id="block-8-1-1" type="radio">
|
<input class="field-4 row-1 col-1 turn-8" id="block-8-1-1" type="radio">
|
||||||
<label class="turn-8" for="block-8-1-1"></label>
|
<label class="turn-8" for="block-8-1-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-5 row-1 col-2 turn-8" id="block-8-1-2" type="radio">
|
<input class="field-5 row-1 col-2 turn-8" id="block-8-1-2" type="radio">
|
||||||
<label class="turn-8" for="block-8-1-2"></label>
|
<label class="turn-8" for="block-8-1-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<input class="field-6 row-2 col-0 turn-8" id="block-8-2-0" type="radio">
|
<input class="field-6 row-2 col-0 turn-8" id="block-8-2-0" type="radio">
|
||||||
<label class="turn-8" for="block-8-2-0"></label>
|
<label class="turn-8" for="block-8-2-0">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-7 row-2 col-1 turn-8" id="block-8-2-1" type="radio">
|
<input class="field-7 row-2 col-1 turn-8" id="block-8-2-1" type="radio">
|
||||||
<label class="turn-8" for="block-8-2-1"></label>
|
<label class="turn-8" for="block-8-2-1">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
<input class="field-8 row-2 col-2 turn-8" id="block-8-2-2" type="radio">
|
<input class="field-8 row-2 col-2 turn-8" id="block-8-2-2" type="radio">
|
||||||
<label class="turn-8" for="block-8-2-2"></label>
|
<label class="turn-8" for="block-8-2-2">
|
||||||
|
|
||||||
|
<div class="circle"></div>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="end">
|
||||||
|
<h1></h1>
|
||||||
|
<a href="">Play again.</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
58
tictactoe.py
58
tictactoe.py
@@ -1,49 +1,27 @@
|
|||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from rules import get_tictactoe_rules
|
||||||
|
|
||||||
|
|
||||||
def write_html(html_file):
|
def write_html(html_file):
|
||||||
html_top = [
|
env = Environment(loader=FileSystemLoader("."))
|
||||||
'<html>',
|
template = env.get_template('template.' + html_file)
|
||||||
'<head>',
|
|
||||||
' <title>Tic Tac Toe CSS</title>',
|
|
||||||
' <link rel="stylesheet" type="text/css" href="tictactoe.css">',
|
|
||||||
'</head>',
|
|
||||||
'<body>',
|
|
||||||
' <div class="tic-tac-toe">']
|
|
||||||
|
|
||||||
html_bottom = [
|
|
||||||
' </div>',
|
|
||||||
'</body>',
|
|
||||||
'</html>']
|
|
||||||
tepl_input = ' <input class="field-{field} ' \
|
|
||||||
'row-{row} col-{col} turn-{turn}" ' \
|
|
||||||
'id="block-{turn}-{row}-{col}" type="radio">'
|
|
||||||
|
|
||||||
tepl_label = ' <label class="turn-{turn}" ' \
|
|
||||||
'for="block-{turn}-{row}-{col}"></label>'
|
|
||||||
html_main = []
|
|
||||||
|
|
||||||
for turn in range(9):
|
|
||||||
c = " <!-- turn-{} -->".format(turn)
|
|
||||||
html_main.append(c)
|
|
||||||
for row in range(3):
|
|
||||||
for col in range(3):
|
|
||||||
d = {
|
|
||||||
"field": row * 3 + col,
|
|
||||||
"turn": turn,
|
|
||||||
"row": row,
|
|
||||||
"col": col}
|
|
||||||
input_ = tepl_input.format(**d)
|
|
||||||
label_ = tepl_label.format(**d)
|
|
||||||
html_main.append(input_)
|
|
||||||
html_main.append(label_)
|
|
||||||
html_main.append("")
|
|
||||||
|
|
||||||
html = html_top + html_main + html_bottom
|
|
||||||
with open(html_file, 'w') as f:
|
with open(html_file, 'w') as f:
|
||||||
f.write("\n".join(html))
|
f.write(template.render())
|
||||||
|
|
||||||
|
|
||||||
def write_css(css_file):
|
def write_css(css_file):
|
||||||
pass
|
bot_move_rules, draw_rules, win_rules = get_tictactoe_rules()
|
||||||
|
kwargs = {
|
||||||
|
"turns_player": [0, 2, 4, 6, 8],
|
||||||
|
"turns_bot": [1, 3, 5, 7],
|
||||||
|
"bot_move_rules": bot_move_rules,
|
||||||
|
"draw_rules": draw_rules,
|
||||||
|
"win_rules": win_rules,
|
||||||
|
}
|
||||||
|
env = Environment(loader=FileSystemLoader("."))
|
||||||
|
template = env.get_template('template.' + css_file)
|
||||||
|
with open(css_file, 'w') as f:
|
||||||
|
f.write(template.render(**kwargs))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user