solution.py

3.3 kB · python · 115 lines

1FLIP = str.maketrans("01", "10")2HEIGHT = 3 ** 0.5 / 234# BINARY56def mrlygram(number: int):7    a = "0"8    b = "101"9    c = "11111"10    d = "0011100"11    rows = number12    bottom = []13    cursor = 014    for index in range(rows):15        cursor = cursor % 8 + 116        match cursor:17            case 1 | 8:18                center = a19                alternate = d20            case 2 | 7:21                center = b22                alternate = c23            case 3 | 6:24                center = c25                alternate = b26            case 4 | 5:27                center = d28                alternate = a29        binary = center30        target = 4 * number - 1 - (2 * index)31        while len(binary) < target:32            binary = alternate + binary + alternate33            binary = center + binary + center34        while len(binary) != target:35            binary = binary[1:-1]36        bottom.append(binary)37    top = bottom.copy()38    top.reverse()39    binary = top + bottom40    if number % 4 == 1:41        binary = [row.translate(FLIP) for row in binary]42    return binary4344# COORDINATES4546def coordinates(binary):47    width = max(len(row) for row in binary)48    cells = []49    for y, row in enumerate(binary):50        padded = row51        while len(padded) < width:52            padded = "-" + padded + "-"53        for x, cell in enumerate(padded):54            kind = {"1": "fill", "0": "void", "-": "grid"}[cell]55            cells.append({"type": kind, "x": x + 1, "y": y + 1})56    return cells5758# POINTS5960def points(cells):61    triangles = []62    north = True63    for cell in cells:64        left = (cell["x"] - 1) / 265        y = cell["y"]66        if north:67            triangle = [(left, y), (left + 0.5, y - 1), (left + 1, y)]68        else:69            triangle = [(left, y - 1), (left + 0.5, y), (left + 1, y - 1)]70        triangles.append({"type": cell["type"], "points": triangle})71        north = not north72    return triangles7374# SVG7576def svg(triangles, scale=50):77    width = max(x for t in triangles for x, y in t["points"]) * scale78    height = max(y for t in triangles for x, y in t["points"]) * HEIGHT * scale79    shapes = []80    for triangle in triangles:81        if triangle["type"] == "grid":82            continue83        corners = " ".join("%g,%g" % (x * scale, y * HEIGHT * scale) for x, y in triangle["points"])84        color = "black" if triangle["type"] == "fill" else "white"85        shapes.append('<polygon points="%s" fill="%s"/>' % (corners, color))86    header = '<svg xmlns="http://www.w3.org/2000/svg" width="%g" height="%g">' % (width, height)87    return "\n".join([header] + shapes + ["</svg>"])8889# PRINT9091def pretty_print(binary):92    target = 093    for row in binary:94        if len(row) > target:95            target = len(row)96    print("-" * target)97    for row in binary:98        count = len(row)99        while len(row) < target:100            row = "-" + row101            row = row + "-"102        print(f"{row} ({count})")103    print("-" * target)104105if __name__ == "__main__":106    import sys107    number = int(sys.argv[1]) if len(sys.argv) > 1 else 7108    if number < 1 or number % 2 == 0:109        sys.exit("odd numbers only")110    binary = mrlygram(number)111    pretty_print(binary)112    name = f"mrlygram-{number}.svg"113    with open(name, "w") as f:114        f.write(svg(points(coordinates(binary))) + "\n")115    print(f"wrote {name}")