Files
ordinaceprojekt/SběrDatRůzné/DailyCalcudoku/cheatsheet_4x4.py
T
2026-05-12 16:15:15 +02:00

178 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Vygeneruje A4 cheat sheet pro Calcudoku 4x4 — kombinace sčítání, násobení, dělení.
"""
import os
import sys
from itertools import combinations
from math import prod
from pathlib import Path
sys.stdout.reconfigure(encoding="utf-8")
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen.canvas import Canvas
_fonts_dir = os.path.join(os.environ.get("WINDIR", r"C:\Windows"), "Fonts")
pdfmetrics.registerFont(TTFont("Arial", os.path.join(_fonts_dir, "arial.ttf")))
pdfmetrics.registerFont(TTFont("ArialBold", os.path.join(_fonts_dir, "arialbd.ttf")))
OUTPUT = Path(__file__).parent / "cheatsheet_4x4.pdf"
DIGITS = range(1, 5)
PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm
# Barvy sekcí
COL_HEADER_BG = colors.Color(0.20, 0.35, 0.55)
COL_HEADER_FG = colors.white
ROW_HEADER_BG = colors.Color(0.88, 0.92, 0.97)
ROW_ALT_BG = colors.Color(0.96, 0.97, 1.00)
ROW_WHITE_BG = colors.white
SECTION_COLORS = {
"+": colors.Color(0.18, 0.48, 0.30), # zelená
"*": colors.Color(0.55, 0.20, 0.20), # červená
"/": colors.Color(0.55, 0.40, 0.10), # oranžová
}
SECTION_LABELS = {"+": "SČÍTÁNÍ +", "*": "NÁSOBENÍ ×", "/": "DĚLENÍ ÷"}
def build_table(op: str) -> dict:
"""Vrátí {výsledek: {n_buněk: [combo, ...]}} pro daný operátor."""
results: dict = {}
for n in range(2, len(DIGITS) + 1):
for combo in combinations(DIGITS, n):
if op == "+":
val = sum(combo)
elif op == "*":
val = prod(combo)
elif op == "/":
# dělení: největší / zbytek (seřazeno desc)
s = sorted(combo, reverse=True)
val = s[0]
for x in s[1:]:
val //= x
# jen celočíselné výsledky odpovídající skutečnému dělení
check = s[0]
for x in s[1:]:
if check % x != 0:
val = None
break
check //= x
if val is None:
continue
results.setdefault(val, {}).setdefault(n, []).append(combo)
return results
def combo_str(combo: tuple, op: str) -> str:
sym = {"+" : "+", "*": "×", "/": "÷"}[op]
return sym.join(str(d) for d in combo)
def draw_section(c: Canvas, op: str, x: float, y: float, width: float) -> float:
"""Vykreslí sekci pro jeden operátor. Vrátí y-souřadnici konce sekce."""
table = build_table(op)
all_ns = sorted({n for sub in table.values() for n in sub})
col_label_w = 1.1 * cm
col_w = (width - col_label_w) / len(all_ns)
row_h = 0.72 * cm
header_h = 0.8 * cm
section_color = SECTION_COLORS[op]
# Nadpis sekce
c.setFillColor(section_color)
c.rect(x, y - 0.75 * cm, width, 0.75 * cm, fill=1, stroke=0)
c.setFillColor(colors.white)
c.setFont("ArialBold", 13)
c.drawString(x + 0.3 * cm, y - 0.55 * cm, SECTION_LABELS[op])
y -= 0.75 * cm
# Záhlaví sloupců (počet buněk)
c.setFillColor(COL_HEADER_BG)
c.rect(x, y - header_h, width, header_h, fill=1, stroke=0)
c.setFillColor(COL_HEADER_FG)
c.setFont("ArialBold", 9)
c.drawCentredString(x + col_label_w / 2, y - header_h + 0.22 * cm, "výsledek")
for i, n in enumerate(all_ns):
cx = x + col_label_w + i * col_w + col_w / 2
c.drawCentredString(cx, y - header_h + 0.22 * cm, f"{n} buňky" if n < 4 else "4 buňky")
y -= header_h
# Řádky
for ridx, result in enumerate(sorted(table.keys())):
bg = ROW_ALT_BG if ridx % 2 == 0 else ROW_WHITE_BG
c.setFillColor(bg)
c.rect(x, y - row_h, width, row_h, fill=1, stroke=0)
# Výsledek
c.setFillColor(section_color)
c.rect(x, y - row_h, col_label_w, row_h, fill=1, stroke=0)
c.setFillColor(colors.white)
c.setFont("ArialBold", 11)
c.drawCentredString(x + col_label_w / 2, y - row_h + 0.18 * cm, str(result))
# Kombinace
c.setFillColor(colors.Color(0.1, 0.1, 0.1))
for i, n in enumerate(all_ns):
cx = x + col_label_w + i * col_w
combos = table[result].get(n, [])
if not combos:
# šedá pomlčka
c.setFillColor(colors.Color(0.75, 0.75, 0.75))
c.setFont("Arial", 9)
c.drawCentredString(cx + col_w / 2, y - row_h + 0.18 * cm, "")
c.setFillColor(colors.Color(0.1, 0.1, 0.1))
else:
# více kombinací pod sebou
line_h = row_h / len(combos)
for ci, combo in enumerate(combos):
text = combo_str(combo, op)
ty = y - ci * line_h - line_h + 0.16 * cm
c.setFont("ArialBold" if len(combos) == 1 else "Arial", 9 if len(combos) > 1 else 10)
c.drawCentredString(cx + col_w / 2, ty, text)
y -= row_h
# Spodní linka sekce
c.setStrokeColor(section_color)
c.setLineWidth(1.2)
c.line(x, y, x + width, y)
c.setLineWidth(0.5)
return y
def main():
c = Canvas(str(OUTPUT), pagesize=A4)
width = PAGE_W - 2 * MARGIN
y = PAGE_H - 1.4 * cm
# Hlavní nadpis
c.setFillColor(colors.Color(0.12, 0.12, 0.30))
c.setFont("ArialBold", 18)
c.drawCentredString(PAGE_W / 2, y - 0.6 * cm, "Calcudoku 4×4 — Cheat Sheet")
c.setFont("Arial", 9)
c.setFillColor(colors.Color(0.4, 0.4, 0.4))
c.drawCentredString(PAGE_W / 2, y - 1.05 * cm, "Číslice 14, v každé kleci bez opakování")
y -= 1.5 * cm
gap = 0.45 * cm
for op in ("+", "*", "/"):
y = draw_section(c, op, MARGIN, y, width)
y -= gap
c.save()
print(f"PDF uloženo: {OUTPUT}")
if __name__ == "__main__":
main()