Skip to content

Instantly share code, notes, and snippets.

@perey
Created September 18, 2015 10:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save perey/4e060bebbee30ddca3d9 to your computer and use it in GitHub Desktop.
Save perey/4e060bebbee30ddca3d9 to your computer and use it in GitHub Desktop.
Hexagon maker for "Beatles Songs and Hexagons" challenge
#!/usr/bin/env python3
"""Triangular code checker and hexagon builder.
http://codegolf.stackexchange.com/questions/58186/beatles-songs-and-hexagons
When run from the command line, this program accepts two filenames as
arguments. It will:
a) Check that they conform to the format specified in the challenge
b) Merge them into a hexagon and print the result to STDOUT
The first file supplied must be the upwards-pointing triangle.
"""
import argparse
def triangle_check(lines, upwards=True):
"""Check a block of code for correct triangular formatting.
Keyword arguments:
lines -- The lines of code to be checked. This must be a
sequence, or some other iterable that has a len() and can be
reversed().
upwards -- True if the triangle is pointing upwards (shortest
line first), False otherwise. The default is True.
"""
linecount = len(lines)
if not upwards:
lines = reversed(lines)
for n, line in enumerate(lines):
# Is the line at least long enough to fit the required content, plus
# all left padding?
if len(line) < linecount + n:
return False
# Is all left padding, and any right padding, nothing but whitespace?
# (If your language of choice is Whitespace, you're on your own.)
if linecount - n - 1 > 0 and not line[:linecount - n - 1].isspace():
return False
if linecount + n < len(line) and not line[linecount + n:].isspace():
return False
return True
def make_hexagon(upwards, downwards):
"""Assemble two triangular blocks of code into a hexagon."""
linecount = len(upwards)
if linecount != len(downwards):
raise ValueError('code blocks have mismatched lengths')
tophalf, bottomhalf = [], []
for n, (up_line, down_line) in enumerate(zip(upwards, downwards)):
up_padlen = linecount - n - 1
up_codelen = 2 * n + 1
down_padlen = n
down_codelen = 2 * linecount - up_codelen
up_pad = up_line[:up_padlen]
up_code = up_line[up_padlen:up_padlen + up_codelen]
down_pad = down_line[:down_padlen]
down_code = down_line[down_padlen:down_padlen + down_codelen]
tophalf.append(''.join((up_pad, up_code, down_code, up_code)))
bottomhalf.append(''.join((down_pad, down_code, up_code, down_code)))
return tophalf + bottomhalf
def read_and_check(filename, upwards=True):
"""Open a file and check that its contents are triangular."""
with open(filename) as f:
lines = f.readlines()
if not triangle_check(lines, upwards=upwards):
raise ValueError('{} does not contain a valid '
'triangle'.format(filename))
return lines
def main():
"""Open two files, check their formatting, and assemble them."""
parser = argparse.ArgumentParser(description='Assemble two code triangles '
'into a code hexagon')
parser.add_argument('upfile', help='The filename for the upwards-pointing '
'code triangle')
parser.add_argument('downfile', help='The filename for the downwards-'
'pointing code triangle')
args = parser.parse_args()
up_lines = read_and_check(args.upfile, upwards=True)
down_lines = read_and_check(args.downfile, upwards=False)
print(*make_hexagon(up_lines, down_lines), sep='\n')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment