Just for fun, I created a script called TinyBF which can compress Brainfuck programs into a format half their size. For example, take a famous hello world program clocking in at 78 bytes:
--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+.
When put into TinyBF format, it is only 39 bytes:
7#!00"7!'!7?,,,?#B!?.>!,ドル.>?!.
The steps for encoding are:
Translate all characters in the input using the following dictionary:
{'>': '2', '<': '3', '+': '4', '-': '5', '.': '6', ',': '7', '[': '8', ']': '9'}
.Split the string into chunks of two characters each - pad the last item with a trailing zero if it is only one digit long.
Make a string using the resultant charcodes of the previous list.
Without further ado, here's the compress and decompress methods (tinybf.py
):
def compress(code):
"""compress(code): Compress a string of Brainfuck code into TinyBF format."""
translated = code.translate({62: 50, 60: 51, 43: 52, 45: 53, 46: 54, 44: 55, 91: 56, 93: 57})
return "".join(chr(int(translated[pos:pos + 2].ljust(2, "0"))) for pos in range(0, len(translated), 2))
def decompress(code):
"""decompress(code): Decompress a string of TinyBF code into Brainfuck format."""
translated = "".join(str(ord(c)) for c in code).translate({50: 62, 51: 60, 52: 43, 53: 45, 54: 46, 55: 44, 56: 91, 57: 93})
return translated.rstrip("0")
And here's the script/output I used to test it:
import tinybf
original_code = "--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+."
compressed_code = tinybf.compress(original_code)
print(compressed_code) # 7#!00"7!'!7?,,,?#B!?.>!,ドル.>?!.
print(len(original_code), len(compressed_code)) # 78 39
new_code = tinybf.decompress(compressed_code)
print(new_code == original_code) # True
Any advice on the code or improvements I could make? I'm also looking for name advice if possible - I settld on TinyBF for this question, but I also considered Braincrunch and Tinyfuck, so if you have thoughts on those, that'd be great.
1 Answer 1
Since you seem to want code golf over readable code this may not be the answer you want. All the suggestions I make are things I would prefer, but I don't think it's that uncommon for others to disagree. The outcome either way is still pretty ugly.
- Your code isn't PEP 8 compliant, and is generally pretty hard to read.
Your one-line docstrings are quite poor, docstring wise. Contrast with:
"""Compress Brainfuck code to TinyBF"""
Use
str.maketrans
rather than manually make the translation table.COMPRESS_TABLE = str.maketrans({'>': '2', '<': '3', '+': '4', '-': '5', '.': '6', ',': '7', '[': '8', ']': '9'})
Alturnately you can define both from a common string.
_COMPRESS_STR = '>2 <3 +4 -5 .6 ,7 [8 ]9' COMPRESS_TABLE = str.maketrans(dict(_COMPRESS_STR.split())) DECOMPRESS_TABLE = str.maketrans(dict(_COMPRESS_STR[::-1].split()))
- I would prefer
itertools.zip_longest
over manually zipping and justifying. - You can split
decompress
over multiple lines to make the code more readable.
import itertools
_COMPRESS_STR = '>2 <3 +4 -5 .6 ,7 [8 ]9'
COMPRESS_TABLE = str.maketrans(dict(_COMPRESS_STR.split()))
DECOMPRESS_TABLE = str.maketrans(dict(_COMPRESS_STR[::-1].split()))
def compress(code: str) -> str:
"""Compress Brainfuck code into TinyBF"""
return "".join(
chr(int(a + b))
for a, b in itertools.zip_longest(
*2*[iter(code.translate(COMPRESS_TABLE))],
fillvalue="0",
)
)
def decompress(code: str) -> str:
"""Decompress TinyBF into Brainfuck"""
return (
"".join(str(ord(c)) for c in code)
.translate(DECOMPRESS_TABLE)
.rstrip("0")
)