Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ccce457dfd | |||
| 6cc2457fc5 | |||
| acc78aec77 | |||
| 68c928bd66 | |||
| 61e9274fc1 | |||
| cbface83ee | |||
| 340b627df3 | |||
| 1361237168 | |||
| f807011f79 |
5 changed files with 95 additions and 79 deletions
18
.github/workflows/release.yaml
vendored
Normal file
18
.github/workflows/release.yaml
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
- run: ./blind_chess.py
|
||||
- env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
run: |
|
||||
date=$(date +%y-%m-%d)
|
||||
gh release delete --yes $date || :
|
||||
gh release create --notes "" $date blind_chess.apkg
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.apkg
|
||||
76
blind_chess.py
Executable file
76
blind_chess.py
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env -S uv run --script
|
||||
#
|
||||
# Generates the blind_chess.apkg Anki Deck
|
||||
#
|
||||
# /// script
|
||||
# requires-python = ">=3.13"
|
||||
# dependencies = [
|
||||
# "genanki",
|
||||
# "python-chess",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import random
|
||||
import genanki
|
||||
import chess
|
||||
import chess.svg
|
||||
|
||||
deck = genanki.Deck(2059400110, 'Blind Chess')
|
||||
|
||||
model = genanki.Model(1120858226, 'Chess Model',
|
||||
fields=[
|
||||
{'name': 'guid'},
|
||||
{'name': 'Question'},
|
||||
{'name': 'Answer'},
|
||||
{'name': 'Board'},
|
||||
],
|
||||
templates=[
|
||||
{
|
||||
'name': 'Base Card with SVG board in answer',
|
||||
'qfmt': '{{Question}}',
|
||||
'afmt': '{{FrontSide}}<hr id="answer">{{Answer}} {{Board}}',
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
class ChessNote(genanki.Note):
|
||||
@property
|
||||
def guid(self):
|
||||
return genanki.guid_for(self.fields[0])
|
||||
|
||||
def square_color(sq_name):
|
||||
sq_id = chess.parse_square(sq_name)
|
||||
sq_file = chess.square_file(sq_id)
|
||||
sq_rank = chess.square_rank(sq_id)
|
||||
|
||||
guid = f'{sq_name}-color'
|
||||
question = f'What is the color of square {sq_name}?'
|
||||
answer = 'black' if (sq_file+sq_rank) % 2 == 0 else 'white'
|
||||
board_svg = chess.svg.board(None, squares=sq_id)
|
||||
return ChessNote(model, [guid, question, answer, board_svg])
|
||||
|
||||
def knight_moves(sq_name):
|
||||
board = chess.Board().empty()
|
||||
sq_id = chess.parse_square(sq_name)
|
||||
board.set_piece_at(sq_id, chess.Piece(chess.KNIGHT, chess.WHITE))
|
||||
attacks = board.attacks(sq_id)
|
||||
|
||||
guid = f'{sq_name}-knight-moves'
|
||||
question = f'Where can the knight move from square {sq_name}?'
|
||||
answer = ', '.join(chess.SQUARE_NAMES[sq] for sq in list(attacks))
|
||||
board_svg = chess.svg.board(board, fill=dict.fromkeys(attacks, "#cc0000cc"))
|
||||
return ChessNote(model, [guid, question, answer, board_svg])
|
||||
|
||||
def notes():
|
||||
for sq_name in chess.SQUARE_NAMES:
|
||||
yield square_color(sq_name)
|
||||
yield knight_moves(sq_name)
|
||||
|
||||
notes = list(notes())
|
||||
random.shuffle(notes)
|
||||
for note in notes:
|
||||
deck.add_note(note)
|
||||
|
||||
out = 'blind_chess.apkg'
|
||||
genanki.Package(deck).write_to_file(out)
|
||||
print(f'Created deck {out} with {len(deck.notes)} notes')
|
||||
BIN
chess.apkg
BIN
chess.apkg
Binary file not shown.
|
|
@ -1,79 +0,0 @@
|
|||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.13"
|
||||
# dependencies = [
|
||||
# "genanki",
|
||||
# "python-chess",
|
||||
# ]
|
||||
# ///
|
||||
|
||||
import random
|
||||
import genanki
|
||||
import chess
|
||||
import chess.svg
|
||||
|
||||
deck = genanki.Deck(
|
||||
2059400110,
|
||||
'Blind Chess'
|
||||
)
|
||||
|
||||
model= genanki.Model(
|
||||
614661503,
|
||||
'Simple Model',
|
||||
fields=[
|
||||
{'name': 'Slug'},
|
||||
{'name': 'Square'},
|
||||
{'name': 'Piece'},
|
||||
{'name': 'Question'},
|
||||
{'name': 'Answer'},
|
||||
{'name': 'Board'},
|
||||
],
|
||||
templates=[
|
||||
{
|
||||
'name': 'Card 1',
|
||||
'qfmt': '{{Question}} {{Square}}',
|
||||
'afmt': '{{FrontSide}}<hr id="answer">{{Answer}} <div id="answer">{{Board}}</div>',
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
class ChessNote(genanki.Note):
|
||||
@property
|
||||
def guid(self):
|
||||
# slug-square-piece
|
||||
return genanki.guid_for(self.fields[0], self.fields[1], self.fields[2])
|
||||
|
||||
board = chess.BaseBoard()
|
||||
board.clear_board()
|
||||
|
||||
notes = []
|
||||
for sq_name in chess.SQUARE_NAMES:
|
||||
# square colors
|
||||
sq_id = chess.parse_square(sq_name)
|
||||
sq_file = chess.square_file(sq_id)
|
||||
sq_rank = chess.square_rank(sq_id)
|
||||
sq_color = 'black' if (sq_file+sq_rank) % 2 == 0 else 'white'
|
||||
board_svg = chess.svg.board(
|
||||
board,
|
||||
squares=chess.SquareSet([sq_id]),
|
||||
)
|
||||
note = ChessNote(
|
||||
model=model,
|
||||
fields=[
|
||||
'color',
|
||||
sq_name,
|
||||
'',
|
||||
'What is the color of square',
|
||||
sq_color,
|
||||
board_svg,
|
||||
],
|
||||
)
|
||||
notes.append(note)
|
||||
|
||||
# randomize the order of the notes
|
||||
random.shuffle(notes)
|
||||
for note in notes:
|
||||
deck.add_note(note)
|
||||
|
||||
genanki.Package(deck).write_to_file('chess.apkg')
|
||||
print('Deck generated: chess.apkg')
|
||||
Loading…
Reference in a new issue