Skip to content

Instantly share code, notes, and snippets.

@donkirkby
Last active November 29, 2018 19:42
Show Gist options
  • Save donkirkby/d0ebe936e0045120409014fe0315ce7d to your computer and use it in GitHub Desktop.
Save donkirkby/d0ebe936e0045120409014fe0315ce7d to your computer and use it in GitHub Desktop.
Code golf challenge to produce a maritime signal flag
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>')
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