codex/north-east-path-positions-and-counts.py

north-east-path-positions-and-counts.py
"""Draw a path-position bijection and its exact recursive arrival counts.

Tested with Python 3.14, matplotlib 3.10.7; run from _media.
"""

from pathlib import Path

import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle


def main():
    plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 12})
    fig, axes = plt.subplots(1, 2, figsize=(10.5, 6), dpi=120, facecolor="white")
    fig.subplots_adjust(left=0.07, right=0.97, bottom=0.35, top=0.84, wspace=0.33)
    blue, orange, purple = "#2477a2", "#c76316", "#603fa0"
    for ax in axes:
        ax.set_facecolor("white")
        for x in range(5):
            ax.plot([x, x], [0, 3], color="#dfe5eb", lw=1, zorder=0)
        for y in range(4):
            ax.plot([0, 4], [y, y], color="#dfe5eb", lw=1, zorder=0)
        ax.set(xlim=(-0.35, 4.35), ylim=(-0.35, 3.35), aspect="equal",
               xticks=range(5), yticks=range(4))
        ax.set_xlabel("x  (east →)")
        ax.set_ylabel("y  (north ↑)")
        for spine in ax.spines.values():
            spine.set_visible(False)
        ax.tick_params(length=0)
    word = "ENENEEN"
    x, y = 0, 0
    axes[0].scatter([0], [0], s=70, color="#222936", zorder=4)
    for step in word:
        nx, ny = x + (step == "E"), y + (step == "N")
        color = orange if step == "N" else blue
        axes[0].annotate("", xy=(nx, ny), xytext=(x, y),
                         arrowprops={"arrowstyle": "->", "lw": 3,
                                     "color": color, "shrinkA": 1, "shrinkB": 1})
        x, y = nx, ny
    axes[0].scatter([4], [3], s=75, color=purple, zorder=4)
    axes[0].set_title("One path: ENENEEN", fontsize=14, pad=14)
    counts = {}
    for y in range(4):
        for x in range(5):
            counts[x, y] = 1 if x == 0 or y == 0 else counts[x - 1, y] + counts[x, y - 1]
    special = {(3, 3): blue, (4, 2): orange, (4, 3): purple}
    for (x, y), value in counts.items():
        color = special.get((x, y), "#222936")
        axes[1].text(x, y, str(value), ha="center", va="center", fontsize=14,
                     weight="bold" if (x, y) in special else "normal",
                     color="white" if (x, y) in special else color,
                     bbox={"boxstyle": "round,pad=0.24", "facecolor": color if (x, y) in special else "white",
                           "edgecolor": "none"}, zorder=4)
    for start, color in [((3, 3), blue), ((4, 2), orange)]:
        axes[1].annotate("", xy=(4, 3), xytext=start,
                         arrowprops={"arrowstyle": "->", "lw": 2.5, "color": color,
                                     "shrinkA": 18, "shrinkB": 18})
    axes[1].set_title("All paths: last-step addition", fontsize=14, pad=14)
    fig.suptitle("Four east steps + three north steps = 35 paths", fontsize=18,
                 weight="bold", y=0.965)
    for i, step in enumerate(word, 1):
        left = 0.12 + (i - 1) * 0.041
        fig.text(left + 0.017, 0.265, str(i), ha="center", fontsize=10)
        fig.add_artist(Rectangle((left, 0.196), 0.034, 0.046, transform=fig.transFigure,
                                 facecolor=orange if step == "N" else "#edf4f8",
                                 edgecolor=orange if step == "N" else blue))
        fig.text(left + 0.017, 0.206, step, ha="center", fontsize=12,
                 color="white" if step == "N" else blue, weight="bold")
    fig.text(0.265, 0.137, r"North positions $\{2,4,7\}$ ↔ path", ha="center", fontsize=13)
    fig.text(0.75, 0.232, r"$P(4,3)=P(3,3)+P(4,2)$", ha="center", fontsize=14)
    fig.text(0.75, 0.16, r"$35=20+15$", ha="center", fontsize=17, color=purple)
    fig.text(0.5, 0.055, r"Choose the three north positions: $\binom{7}{3}=35$",
             ha="center", fontsize=15)
    fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()