TheBird

solution.py

3.3 kB · python · 117 lines

1from lib.paths import data23FLIP = str.maketrans("01", "10")4HEIGHT = 3 ** 0.5 / 256# BINARY78def mrlygram(number: int):9    a = "0"10    b = "101"11    c = "11111"12    d = "0011100"13    rows = number14    bottom = []15    cursor = 016    for index in range(rows):17        cursor = cursor % 8 + 118        match cursor:19            case 1 | 8:20                center = a21                alternate = d22            case 2 | 7:23                center = b24                alternate = c25            case 3 | 6:26                center = c27                alternate = b28            case 4 | 5:29                center = d30                alternate = a31        binary = center32        target = 4 * number - 1 - (2 * index)33        while len(binary) < target:34            binary = alternate + binary + alternate35            binary = center + binary + center36        while len(binary) != target:37            binary = binary[1:-1]38        bottom.append(binary)39    top = bottom.copy()40    top.reverse()41    binary = top + bottom42    if number % 4 == 1:43        binary = [row.translate(FLIP) for row in binary]44    return binary4546# COORDINATES4748def coordinates(binary):49    width = max(len(row) for row in binary)50    cells = []51    for y, row in enumerate(binary):52        padded = row53        while len(padded) < width:54            padded = "-" + padded + "-"55        for x, cell in enumerate(padded):56            kind = {"1": "fill", "0": "void", "-": "grid"}[cell]57            cells.append({"type": kind, "x": x + 1, "y": y + 1})58    return cells5960# POINTS6162def points(cells):63    triangles = []64    north = True65    for cell in cells:66        left = (cell["x"] - 1) / 267        y = cell["y"]68        if north:69            triangle = [(left, y), (left + 0.5, y - 1), (left + 1, y)]70        else:71            triangle = [(left, y - 1), (left + 0.5, y), (left + 1, y - 1)]72        triangles.append({"type": cell["type"], "points": triangle})73        north = not north74    return triangles7576# SVG7778def svg(triangles, scale=50):79    width = max(x for t in triangles for x, y in t["points"]) * scale80    height = max(y for t in triangles for x, y in t["points"]) * HEIGHT * scale81    shapes = []82    for triangle in triangles:83        if triangle["type"] == "grid":84            continue85        corners = " ".join("%g,%g" % (x * scale, y * HEIGHT * scale) for x, y in triangle["points"])86        color = "black" if triangle["type"] == "fill" else "white"87        shapes.append('<polygon points="%s" fill="%s"/>' % (corners, color))88    header = '<svg xmlns="http://www.w3.org/2000/svg" width="%g" height="%g">' % (width, height)89    return "\n".join([header] + shapes + ["</svg>"])9091# PRINT9293def pretty_print(binary):94    target = 095    for row in binary:96        if len(row) > target:97            target = len(row)98    print("-" * target)99    for row in binary:100        count = len(row)101        while len(row) < target:102            row = "-" + row103            row = row + "-"104        print(f"{row} ({count})")105    print("-" * target)106107if __name__ == "__main__":108    import sys109    number = int(sys.argv[1]) if len(sys.argv) > 1 else 7110    if number < 1 or number % 2 == 0:111        sys.exit("odd numbers only")112    binary = mrlygram(number)113    pretty_print(binary)114    path = data(f"mrlygram-{number}.svg")115    with open(path, "w") as f:116        f.write(svg(points(coordinates(binary))) + "\n")117    print(f"wrote {path}")