# Written by Claude (Anthropic) for Shintaro Mukai.
# CC0 1.0 Universal (public domain dedication)
# https://creativecommons.org/publicdomain/zero/1.0/
# https://mukai.systems/articles/i0skgk40f0nestefo7m4sv5y0ujt1mbm/
# Usage: python lattice-paths.py lattice-paths.svg

import sys
from collections import Counter

TOSSES = 10
PREFIX = [1, 1]


def valid_paths():
    def walk(path, height):
        if len(path) == TOSSES:
            if height == 0:
                yield path
            return
        if height == 0 and path:
            return
        for step in (1, -1):
            yield from walk(path + [step], height + step)
    return list(walk(PREFIX, sum(PREFIX)))


def points(path):
    heights = [0]
    for step in path:
        heights.append(heights[-1] + step)
    return list(enumerate(heights))


def render(paths):
    edges = Counter()
    nodes = set()
    for path in paths:
        pts = points(path)
        nodes.update(pts)
        edges.update(zip(pts, pts[1:]))
    ways = {(0, 0): 1}
    for x in range(1, TOSSES + 1):
        for a, b in edges:
            if b[0] == x:
                ways[b] = ways.get(b, 0) + ways[a]
    max_height = max(y for _, y in nodes)
    step, left, top, right, bottom = 56, 72, 28, 24, 64
    width = left + step * TOSSES + right
    height = top + step * max_height + bottom
    def px(x):
        return left + step * x
    def py(y):
        return top + step * (max_height - y)
    out = [f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}" width="{width}" height="{height}" font-family="Arial, \'Yu Gothic\', Meiryo, sans-serif">',
           f'<rect width="{width}" height="{height}" fill="#fcfcfb"/>']
    for x in range(TOSSES + 1):
        out.append(f'<line x1="{px(x)}" y1="{py(0)}" x2="{px(x)}" y2="{py(max_height)}" stroke="#e6e5e0" stroke-width="1"/>')
        out.append(f'<text x="{px(x)}" y="{py(0) + 26}" font-size="13" fill="#898781" text-anchor="middle">{x}</text>')
    for y in range(max_height + 1):
        color = "#52514e" if y == 0 else "#e6e5e0"
        out.append(f'<line x1="{px(0)}" y1="{py(y)}" x2="{px(TOSSES)}" y2="{py(y)}" stroke="{color}" stroke-width="1"/>')
        out.append(f'<text x="{px(0) - 22}" y="{py(y) + 4}" font-size="13" fill="#898781" text-anchor="end">{y}</text>')
    out.append(f'<text x="{px(TOSSES / 2)}" y="{height - 12}" font-size="14" fill="#52514e" text-anchor="middle">投げた回数</text>')
    out.append(f'<text transform="translate(20 {py(max_height / 2)}) rotate(-90)" font-size="14" fill="#52514e" text-anchor="middle">表の回数 − 裏の回数</text>')
    for (a, b), count in sorted(edges.items(), key=lambda e: e[1]):
        color = "#b4b2aa" if b[0] <= len(PREFIX) else "#2a78d6"
        out.append(f'<line x1="{px(a[0])}" y1="{py(a[1])}" x2="{px(b[0])}" y2="{py(b[1])}" stroke="{color}" stroke-width="{1.5 + count * 0.6:.1f}" stroke-linecap="round"/>')
    for x, y in sorted(nodes):
        out.append(f'<circle cx="{px(x)}" cy="{py(y)}" r="13" fill="#fcfcfb" stroke="#0b0b0b" stroke-width="1.5"/>')
        out.append(f'<text x="{px(x)}" y="{py(y) + 4.5}" font-size="13" font-weight="bold" fill="#0b0b0b" text-anchor="middle">{ways[(x, y)]}</text>')
    out.append('</svg>')
    return "\n".join(out) + "\n"


paths = valid_paths()
assert len(paths) == 14
with open(sys.argv[1], "w", encoding="utf-8", newline="\n") as f:
    f.write(render(paths))
