""" 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 1–4, 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()