anki decks: add --skip-ones multiplication/division variant; switch shapes/clocks/currency images from SVG to PNG

--skip-ones drops every fact involving 1 (trivial, not worth drilling),
121 cards instead of 144 for both multiplication and division.

SVG images never displayed on AnkiDroid (mobile) despite rendering fine
on desktop Anki — a long-documented, still-open AnkiDroid limitation
(multiple open ankidroid/Anki-Android GitHub issues), not a bug in the
generated markup. Rewrote shape/clock/coin image generation to render
PNG via Pillow instead, preserving the same geometry math (regular
polygons, clock-hand angles including the fractional hour-hand
movement, coin layouts) — card counts and content are unchanged, only
the image format.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
This commit is contained in:
Claude
2026-09-10 12:28:28 +00:00
parent 8f83c96ca7
commit e4e32f20e7
3 changed files with 134 additions and 71 deletions
+23 -7
View File
@@ -13,10 +13,13 @@ and services/anki-progress.sh for the actual self-hosted sync backend and
progress dashboard this content is meant to be studied through.
Decks:
multiplication 1-12, all 144 ordered pairs (a x b), shuffled
multiplication 1-12, all 144 ordered pairs (a x b), shuffled
(not sequential — see the note near
random.shuffle(pairs) below for why)
division inverse of the multiplication deck (144 facts)
random.shuffle(pairs) below for why). Add
--skip-ones to drop every fact involving 1
(trivial, 121 facts instead of 144).
division inverse of the multiplication deck (144 facts,
or 121 with --skip-ones)
addsub --lo L --hi H addition + subtraction fact family for [L, H]
(subtraction facts derived from the addition
facts, e.g. 7+3=10 also gives 10-7=3 and
@@ -45,6 +48,7 @@ Setup (one time):
Usage (run with the venv activated):
python3 anki-deck-math.py --deck multiplication
python3 anki-deck-math.py --deck multiplication --skip-ones
python3 anki-deck-math.py --deck division
python3 anki-deck-math.py --deck addsub --lo 3 --hi 7
python3 anki-deck-math.py --deck addsub --lo 3 --hi 13
@@ -74,6 +78,10 @@ parser.add_argument("--deck", required=True,
choices=["multiplication", "division", "addsub", "fractions", "decimals"])
parser.add_argument("--lo", type=int, default=None, help="addsub only: low end of range")
parser.add_argument("--hi", type=int, default=None, help="addsub only: high end of range")
parser.add_argument("--skip-ones", action="store_true",
help="multiplication/division only: drop every fact where either"
" number is 1 (1x1..1x12, 2x1..12x1, and their division"
" inverses) — those are trivial and not worth drilling.")
parser.add_argument("--voice", default="en_US-lessac-medium")
parser.add_argument("--model-path", default=None)
parser.add_argument("--dry-run-tts", action="store_true",
@@ -269,11 +277,14 @@ def add_note(deck, model, top, bottom, answer, qtext, atext, media_files, tag):
# ─── Per-deck generators ─────────────────────────────────────────────────────
def gen_multiplication():
deck_key = "multiplication"
deck_key = "multiplication" + ("_no_ones" if args.skip_ones else "")
model_id, model = build_model(deck_key)
deck = build_deck(deck_key, "Multiplication Facts (1-12)")
title = "Multiplication Facts (1-12)" + (", no 1s" if args.skip_ones else "")
deck = build_deck(deck_key, title)
media_files = []
pairs = [(a, b) for a in range(1, 13) for b in range(1, 13)]
if args.skip_ones:
pairs = [(a, b) for a, b in pairs if a != 1 and b != 1]
random.seed(42)
random.shuffle(pairs)
for a, b in pairs:
@@ -285,13 +296,16 @@ def gen_multiplication():
def gen_division():
deck_key = "division"
deck_key = "division" + ("_no_ones" if args.skip_ones else "")
model_id, model = build_model(deck_key)
deck = build_deck(deck_key, "Division Facts (inverse of 1-12 times tables)")
title = "Division Facts (inverse of 1-12 times tables)" + (", no 1s" if args.skip_ones else "")
deck = build_deck(deck_key, title)
media_files = []
# Same (a, b) pairs as multiplication: product / a = b. This is the
# direct inverse of every multiplication card in that deck.
pairs = [(a, b) for a in range(1, 13) for b in range(1, 13)]
if args.skip_ones:
pairs = [(a, b) for a, b in pairs if a != 1 and b != 1]
random.seed(43)
random.shuffle(pairs)
for a, b in pairs:
@@ -393,6 +407,8 @@ def gen_decimals():
# ─── Dispatch ─────────────────────────────────────────────────────────────────
if args.deck == "addsub":
deck_key = f"addsub_{args.lo}_{args.hi}"
elif args.deck in ("multiplication", "division") and args.skip_ones:
deck_key = f"{args.deck}_no_ones"
else:
deck_key = args.deck