Skip to content

Instantly share code, notes, and snippets.

@qxcv
Created March 22, 2014 08:45
Show Gist options
  • Save qxcv/9703356 to your computer and use it in GitHub Desktop.
Save qxcv/9703356 to your computer and use it in GitHub Desktop.
Script to convert from .bmp wireworlds to Golly wireworlds
#!/usr/bin/python2
from os.path import basename
from subprocess import PIPE, Popen, check_output
from sys import argv, exit, stdout, stderr
from re import compile
USAGE = "USAGE: {0} input.bmp|input.rle > output.rle|output.bmp\n".format(argv[0])
RLE_HEADER = "x = {x}, y = {y}, rule = WireWorld\n"
XPM_HEADER = """/* XPM */
static char *xpm__[] = {{
/* columns rows colors chars-per-pixel */
"{x} {y} 4 1 ",
". c black",
"B c #FF3F00",
"C c yellow",
"A c blue",
/* pixels */
{data}
}};"""
# Note that empty is "." in RLE
# A is "head", B is "tail", C is "conductor"
def sample2rle(sample):
text_triple = sample.split()[1]
r, g, b = map(int, text_triple[1:-1].split(',')[:3])
# emulate the "smart" colour handling that the COMP1100 Wireworld
# implementation uses
if r >= 128 and g >= 128 and b < 128:
return 'C' # conductor, yellow-ish
if r >= 128 and g < 128 and b < 128:
return 'B' # tail, reddish
if r < 128 and g < 128 and b >= 128:
return 'A' # head, bluish
return '.' # empty
def bmp2rle(path):
convert_output = check_output(['convert', path, 'txt:-']).splitlines()
size = map(int, convert_output[0].split()[-1].split(',')[:2])
input_lines = convert_output[1:]
output_lines = []
for j in xrange(size[1]):
input_line = input_lines[j * size[0]:(j + 1) * size[0]]
output_line = ''
current_colour = None
current_count = 0
for elem in input_line:
colour = sample2rle(elem)
if colour != current_colour:
# We've ended an RLE run
if current_colour != None:
output_line += repr(current_count) + current_colour
current_colour = colour
current_count = 1
else:
current_count += 1
output_line += repr(current_count) + current_colour
output_lines.append(output_line)
total_file = RLE_HEADER.format(x=size[0], y=size[1]) + '$'.join(output_lines) + '!'
print(total_file)
def rle2xpm(rle):
"""We use this function to convert RLE to XPM before feeding it to
ImageMagick"""
# So that we ignore the stupid "extended RLE" metadata
lines = filter(lambda l: not l.strip().startswith('#'), rle.splitlines())
# get the header so that we can rip dimensions out of it
header = lines[0]
# now I have TWO problems
size_re = compile('x *= *([0-9]+).+y *= *([0-9]+)')
x_size, y_size = map(int, list(size_re.match(header).groups())[:2])
body = '\n'.join(lines[1:])
elem_re = compile('([0-9]*)([\\.ABC])')
terminal_num_re = compile('([0-9]+)[^\\.ABC0-9]*$')
output_lines = []
for rle_line in body.split('$'):
matches = elem_re.findall(rle_line)
line_str = ''
for num_text, cell in matches:
if not num_text:
num = 1
else:
num = int(num_text)
line_str += num * cell
if len(line_str) < x_size:
line_str += '.' * (x_size - len(line_str))
output_lines.append(line_str)
terminal_num_matches = terminal_num_re.findall(rle_line)
if len(terminal_num_matches) > 0:
# append some blank padding lines, as per spec
blank = '.' * x_size
number = int(terminal_num_matches[0])
# I honestly have no idea what I am doing at this point
# Golly's output doesn't actually seem to match the spec, so I'm
# taking one row of the requested number of blanks
if number < 2: pass
output_lines += [blank] * (number - 1)
line_reprs = ['"' + line + '",' for line in output_lines]
# Strip the trailing comma on our last line
line_reprs[-1] = line_reprs[-1][:-1]
data_string = '\n'.join(line_reprs)
return XPM_HEADER.format(x=x_size, y=y_size, data=data_string)
def rle2bmp(path):
xpm = rle2xpm(open(path).read())
fp = Popen(['convert', '-type', 'truecolor', 'xpm:-', 'bmp:-'], stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, errors = fp.communicate(xpm)
if fp.returncode != 0:
raise Exception("convert failed")
stdout.write(output)
handlers = {
'bmp': bmp2rle,
'rle': rle2bmp,
}
if __name__ == '__main__':
if len(argv) != 2:
stderr.write(USAGE)
exit(1)
fn = argv[1]
ext_list = basename(fn).split('.')
ext = ext_list[-1]
if len(ext_list) < 2 or ext not in handlers.keys():
stderr.write(USAGE)
exit(1)
handlers[ext](fn)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment