8
\$\begingroup\$

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:

  1. Translate all characters in the input using the following dictionary: {'>': '2', '<': '3', '+': '4', '-': '5', '.': '6', ',': '7', '[': '8', ']': '9'}.

  2. Split the string into chunks of two characters each - pad the last item with a trailing zero if it is only one digit long.

  3. 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.

\$\endgroup\$
7
\$\begingroup\$

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")
    )
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.