Last active
November 29, 2018 19:42
-
-
Save donkirkby/d0ebe936e0045120409014fe0315ce7d to your computer and use it in GitHub Desktop.
Code golf challenge to produce a maritime signal flag
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
t=dict(r='<rect x="{}" y="{}" width="{}" height="{}" fill="{}"/>',p='<path d="{}" stroke="{}" stroke-width="{}" stroke-dasharray="{}" fill-rule="{}" fill="{}"/>',c='<circle cx="{}" cy="{}" r="{}" fill="{}"/>') | |
s=(ord(input()[0])|32)-97 | |
open('a.svg','w').write(f'<svg xmlns="http://www.w3.org/2000/svg" width="60" height="40">{"".join(t[o[0]].format(*(o[1:].split(":")))for o in"r0:0:30:40:#fff;pM 60,0 H 30 V 40 H 60 L 40,20:0:0:0::#003298|pM 60,0 H 0 V 40 H 60 L 40,20:0:0:0::#f00|r0:0:60:40:#039;r0:8:60:24:#fff;r0:16:60:8:#f00|r0:0:60:40:#fbfc01;r0:13.3:60:13.3:#003298|r0:0:60:20:#039;r0:20:60:20:#f00|r0:0:60:40:#fff;pM 30,0 L 0,20 L 30,40 L 60,20:0:0:0::#f00|r0:0:60:40:#ff0;r10:0:10:40:#039;r30:0:10:40:#039;r50:0:10:40:#039|r0:0:30:40:#fff;r30:0:30:40:#f00|r0:0:60:40:#ff0;c30:20:10.7:#000|r0:0:60:40:#039;r0:13.3:60:13.3:#fff|r0:0:30:40:#ff0;r30:0:30:40:#039|pM0,0H60V40H0:0:0:0::#FF0;pM0,20H60V0H30V40H0:0:0:0::#0|r0:0:60:40:#039;pM 0,0 L 60,40 M 0,40 L 60,0:#fff:10:0::#0|r0:0:60:40:#039;pM 0,0 H 60 V 10 H 0 z M 0,20 H 60 V 30 H 0 z M 0,0 V 40 H 15 V 0 z M 30,0 V 40 H 45 V 0:0:0:0:evenodd:#fff|r0:0:60:40:#f00;pM 0,0 L 60,40 H 0:0:0:0::#ff0|r0:0:60:40:#039;r12:8:36:24:#fff|r0:0:60:40:#ff0|r0:0:60:40:#f00;pM 30,0 V 40 M 0,20 H 60:#ff0:10:0::#0|r0:0:60:40:#fff;r12:8:36:24:#039|r0:0:20:40:#f00;r20:0:20:40:#fff;r40:0:20:40:#039|r0:0:60:40:#f00;r30:0:30:20:#fff;r0:20:30:20:#fff|r0:0:60:40:#fff;pM 0,0 L 60,40 M 0,40 L 60,0:#f00:10:0::#0|r0:0:60:40:#039;r12:8:36:24:#fff;r24:16:12:8:#f00|r0:0:60:40:#fff;pM 30,0 V 40 M 0,20 H 60:#039:10:0::#0|pm0,0h60v40H0:0:0:0::#F00;pm0,0 40,60:#FF0:100:6.1::#0|pM 0,0 H 60 L 30,20:0:0:0::#ff0;pM 0,0 V 40 L 30,20:0:0:0::#000;pM 60,0 V 40 L 30,20:0:0:0::#039;pM 30,20 L 0,40 H 60:0:0:0::#f00".split("|")[s].split(";"))}</svg>') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import re | |
from io import StringIO | |
from pathlib import Path | |
from urllib.parse import urljoin | |
from xml.etree import ElementTree | |
from zipfile import PyZipFile, ZIP_DEFLATED | |
import requests | |
TEMPLATE = """\ | |
t=dict(\ | |
r='<rect x="{}" y="{}" width="{}" height="{}" fill="{}"/>',\ | |
p='<path d="{}" stroke="{}" stroke-width="{}" stroke-dasharray="{}" fill-rule="{}" fill="{}"/>',\ | |
c='<circle cx="{}" cy="{}" r="{}" fill="{}"/>') | |
s=(ord(input()[0])|32)-97 | |
open('a.svg','w').write(f'<svg xmlns="http://www.w3.org/2000/svg" ### REPLACE 1 ###>{"".join(\ | |
t[o[0]].format(*(o[1:].split(":")))for o in\ | |
"### REPLACE 2 ###".split("|")[s].split(";"))}</svg>')""" | |
def main(): | |
downloads_path = Path('downloads') | |
if not downloads_path.exists(): | |
downloads_path.mkdir() | |
index_url = 'https://commons.wikimedia.org/wiki/Category:SVG_signal_flags' | |
index = requests.get(index_url).text | |
matches = re.finditer(r'<a href="(.*_flag.svg)"', index) | |
links = sorted({match.group(1) for match in matches}) | |
for link in links: | |
info_url = urljoin(index_url, link) | |
info = requests.get(info_url).text | |
match = re.search(r'<a href="([^"]+)"[^>]*>Original file', info) | |
svg_url = match.group(1) | |
name = re.search(r'(\w+)_flag', link).group(1).lower() | |
(downloads_path / f'{name}.svg').write_text(requests.get(svg_url).text) | |
for scale in range(15, 16): | |
write_script(downloads_path, scale) | |
def write_script(downloads_path, scale): | |
templates = dict(rect='r{x}:{y}:{width}:{height}:{fill}', | |
path='p{d}:{stroke}:{stroke-width}:{stroke-dasharray}:{fill-rule}:{fill}', | |
circle='c{cx}:{cy}:{r}:{fill}') | |
expected_attribs = dict(rect=set('x y width height fill'.split()), | |
path=set('d stroke stroke-width stroke-dasharray fill-rule fill'.split()), | |
circle=set('cx cy r fill'.split())) | |
images = StringIO() | |
size_header = (f'width="{format_coordinate("width", "900", scale)}" ' | |
f'height="{format_coordinate("height", "600", scale)}"') | |
for svg_path in sorted(downloads_path.iterdir()): | |
tree = ElementTree.parse(svg_path) | |
root = tree.getroot() | |
terms = [] | |
for child in root: | |
tag_name = child.tag.split('}')[-1] | |
template = templates[tag_name] | |
attrib_names = set(child.attrib.keys()) | |
unexpected_attribs = attrib_names - expected_attribs[tag_name] | |
assert not unexpected_attribs, (tag_name, unexpected_attribs) | |
child.attrib.setdefault('fill', '#0') | |
child.attrib.setdefault('fill-rule', '') | |
child.attrib.setdefault('x', '0') | |
child.attrib.setdefault('y', '0') | |
child.attrib.setdefault('stroke', '0') | |
child.attrib.setdefault('stroke-width', '0') | |
child.attrib.setdefault('stroke-dasharray', '0') | |
scaled_attribs = {attrib: format_coordinate(attrib, value, scale) | |
for attrib, value in child.attrib.items()} | |
term = template.format(**scaled_attribs) | |
terms.append(term) | |
display = ';'.join(terms) | |
images.write(f'{display}|') | |
with open('flags_golf.py', 'w') as f: | |
f.write(TEMPLATE.replace('### REPLACE 1 ###', | |
size_header).replace('### REPLACE 2 ###', | |
images.getvalue()[:-1])) | |
with PyZipFile('flags_golf.zip', 'w', ZIP_DEFLATED) as zipped: | |
zipped.write('flags_golf.py', '__main__.py') | |
print(scale, os.stat('flags_golf.zip').st_size) | |
def format_coordinate(attrib, value, scale): | |
if attrib in ('fill', 'fill-rule', 'stroke'): | |
return value | |
if attrib == 'd': | |
matches = re.finditer(r'\d+', value) | |
copied = 0 | |
scaled = '' | |
for match in matches: | |
scaled += value[copied:match.start()] | |
scaled += format_coordinate('x', match.group(0), scale) | |
copied = match.end() | |
return scaled | |
i = int(value) | |
r = i % scale | |
if r == 0: | |
return str(i // scale) | |
else: | |
return str(round(i / scale, 1)) | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment