codex/factorial-permutation-choice-tree.py

factorial-permutation-choice-tree.py
"""Draw all orderings of ABC; tested with Python 3.14.4 and matplotlib 3.10.7+dfsg1.

Write factorial-permutation-choice-tree.png in the current directory.
"""

from itertools import permutations
from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt


def main():
    words = ["".join(p) for p in permutations("ABC")]
    colors = {"A": "#246d9a", "B": "#b45925", "C": "#388363"}
    fig, ax = plt.subplots(figsize=(8.4, 5.4), dpi=120, facecolor="white")
    nodes = {"": (0, 2.5)}
    for depth in range(1, 4):
        for prefix in sorted({word[:depth] for word in words}):
            leaves = [i for i, word in enumerate(words) if word.startswith(prefix)]
            nodes[prefix] = (depth, sum(leaves) / len(leaves))
    for prefix, (x, y) in nodes.items():
        color = colors[prefix[0]] if prefix else "#344454"
        if prefix:
            parent_x, parent_y = nodes[prefix[:-1]]
            ax.plot([parent_x, x], [parent_y, y], color=color, lw=1.7, zorder=1)
        ax.text(x, y, prefix or "Start", ha="center", va="center", fontsize=14,
                color=color, zorder=2,
                bbox={"boxstyle": "round,pad=0.35", "fc": "white", "ec": color})
    for x, label in enumerate(["", "First: 3 choices", "Second: 2 choices", "Third: 1 choice"]):
        ax.text(x, 5.8, label, ha="center", fontsize=11, color="#344454")
    ax.set(xlim=(-0.35, 3.55), ylim=(6.2, -0.6))
    ax.axis("off")
    fig.suptitle("Three distinct letters, six complete orderings", fontsize=16)
    fig.text(0.5, 0.04, "ABC    ACB    BAC    BCA    CAB    CBA       |       3 × 2 × 1 = 3! = 6",
             ha="center", fontsize=12, color="#344454")
    fig.subplots_adjust(left=0.03, right=0.97, top=0.88, bottom=0.1)
    fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()