Created
August 25, 2019 04:33
-
-
Save RickyCook/dcdaec6683ae54a960c7731d9a2ccaf5 to your computer and use it in GitHub Desktop.
Decode a messy binary sequence whose value is ASCII/UTF-8 and that's all you know
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
from copy import deepcopy | |
from pprint import pprint | |
class Decoder(object): | |
def __init__( | |
self, | |
seq, | |
orange_val, | |
silver_val, | |
char_width=8, | |
reverse=False, | |
start_at=0, | |
): | |
self.raw_seq = seq | |
self.orange_val = orange_val | |
self.silver_val = silver_val | |
self.char_width = char_width | |
self.reverse = reverse | |
self.start_at = start_at | |
@property | |
def seq_1(self): | |
""" Slice the start of seq as necessary | |
Examples: | |
>>> Decoder('obsob', 1, 0).seq_1 | |
'obsob' | |
>>> Decoder('obsob', 1, 0, start_at=2).seq_1 | |
'sob' | |
""" | |
return self.raw_seq[self.start_at:] | |
@property | |
def seq(self): | |
""" Get the sequence that we are processing (maybe without silver) | |
Examples: | |
>>> Decoder('obsob', 1, 0).seq | |
'obsob' | |
>>> Decoder('obsob', 1, 1).seq | |
'obsob' | |
>>> Decoder('obsob', 1, None).seq | |
'obob' | |
""" | |
if self.silver_val is None: | |
return ''.join( | |
ring for ring in self.seq_1 if ring != 's' | |
) | |
return self.seq_1 | |
@property | |
def black_val(self): | |
""" Get the value of the black ring (inverse of orange) | |
Examples: | |
>>> Decoder('', 1, 0).black_val | |
0 | |
>>> Decoder('', 0, 0).black_val | |
1 | |
""" | |
if self.orange_val == 1: | |
return 0 | |
else: | |
return 1 | |
def bit_value(self, ring): | |
""" Get the bit value of a ring | |
>>> d = Decoder('', 0, 0) | |
>>> d.bit_value('o') | |
0 | |
>>> d.bit_value('b') | |
1 | |
>>> d.bit_value('s') | |
0 | |
>>> d = Decoder('', 1, 0) | |
>>> d.bit_value('o') | |
1 | |
>>> d.bit_value('b') | |
0 | |
>>> d.bit_value('s') | |
0 | |
>>> Decoder('', 0, 1).bit_value('s') | |
1 | |
>>> Decoder('', 0, None).bit_value('s') | |
""" | |
if ring == 'o': | |
return self.orange_val | |
if ring == 'b': | |
return self.black_val | |
if ring == 's': | |
return self.silver_val | |
raise ValueError('Invalid ring bit: %r', ring) | |
@property | |
def bit_list(self): | |
""" Convert the sequence into a bit list | |
Examples: | |
>>> Decoder('obsobs', 0, 0).bit_list | |
[0, 1, 0, 0, 1, 0] | |
>>> Decoder('obsobs', 1, 0).bit_list | |
[1, 0, 0, 1, 0, 0] | |
>>> Decoder('obsobs', 0, 1).bit_list | |
[0, 1, 1, 0, 1, 1] | |
>>> Decoder('obsobs', 0, None).bit_list | |
[0, 1, 0, 1] | |
""" | |
return [self.bit_value(ring) for ring in self.seq] | |
@property | |
def bit_str(self): | |
""" Convert the bit list into a bit string | |
Examples: | |
>>> Decoder('obsobs', 0, 0).bit_str | |
'010010' | |
>>> Decoder('obsobs', 1, 0).bit_str | |
'100100' | |
>>> Decoder('obsobs', 0, 1).bit_str | |
'011011' | |
>>> Decoder('obsobs', 0, None).bit_str | |
'0101' | |
""" | |
return ''.join( | |
str(bit) for bit in self.bit_list | |
) | |
@property | |
def bit_str_ords_1(self): | |
""" Chunk the bit string into chars | |
Examples: | |
>>> t_val = 'ooobobb' | |
>>> e_val = 'oobbobo' | |
>>> Decoder(t_val + e_val, 1, None, char_width=7).bit_str_ords_1 | |
['1110100', '1100101'] | |
>>> t_val = 'booobobb' | |
>>> e_val = 'boobbobo' | |
>>> Decoder(t_val + e_val, 1, None, char_width=8).bit_str_ords_1 | |
['01110100', '01100101'] | |
""" | |
bit_str = self.bit_str | |
return [bit_str[i:i+self.char_width] for i in range(0, len(bit_str), self.char_width)] | |
@property | |
def bit_str_ords(self): | |
""" Reverse the ords as necessary | |
Examples: | |
>>> t_val = 'ooobobb' | |
>>> e_val = 'oobbobo' | |
>>> Decoder(t_val + e_val, 1, None, char_width=7, reverse=True).bit_str_ords | |
['0010111', '1010011'] | |
>>> t_val = 'booobobb' | |
>>> e_val = 'boobbobo' | |
>>> Decoder(t_val + e_val, 1, None, char_width=8, reverse=True).bit_str_ords | |
['00101110', '10100110'] | |
""" | |
if self.reverse: | |
return [i[::-1] for i in self.bit_str_ords_1] | |
return self.bit_str_ords_1 | |
@property | |
def bit_str_chrs(self): | |
""" Convert the bit string ords to chars | |
Examples: | |
>>> t_val = 'ooobobb' | |
>>> e_val = 'oobbobo' | |
>>> Decoder(t_val + e_val, 1, None, char_width=7).bit_str_chrs | |
['t', 'e'] | |
""" | |
return [chr(int(i, base=2)) for i in self.bit_str_ords] | |
rings = 'obobobbobbobbbobbobboobobboobobbbbbobbobboobobboobobbbbbobbbbooobbooooobobbobobobbobobbboboobbbbooobobobb' | |
# import doctest | |
# doctest.testmod(verbose=True) | |
# sys.exit(0) | |
# parts = 'osboobboobboobboobbobbobboob' | |
# parts_a = 'obobbooobboobb' | |
# parts_b = 'sobobbooobboo' | |
# parts_a = 'obobobobobboboobbobb' | |
# parts_b = 'sobobobobobboboobbob' | |
opts = dict( | |
orange_val = [1, 0], | |
silver_val = [1, 0, None], | |
char_width = [7, 8], | |
reverse = [True, False], | |
start_at = [0, 2], | |
) | |
# PRODUCT OF OPTS | |
# not part of the workout; just test all combinations of opts | |
all_opts = [] | |
for k, vals in opts.items(): | |
if len(all_opts) == 0: | |
for val in vals: | |
all_opts.append({k: val}) | |
else: | |
all_opts_orig = all_opts | |
all_opts = [] | |
for val in vals: | |
for o in all_opts_orig: | |
oo = deepcopy(o) | |
oo[k] = val | |
all_opts.append(oo) | |
col_fs = '{:<10}' | |
headers = list(opts.keys()) + ['string'] | |
print(' | '.join(col_fs.format(h) for h in headers)) | |
col_fs = { | |
h: '{!r:<10}' for h in headers | |
} | |
col_fs['string'] = '{:<10}' | |
for opts in all_opts: | |
d = Decoder(rings, **opts) | |
try: | |
opts['string'] = '%r' % d.bit_str_chrs | |
except Exception: | |
opts['string'] = 'ERROR' | |
print(' | '.join(col_fs[h].format(opts[h]) for h in headers)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment