from dataclasses import dataclass from copy import deepcopy @dataclass class Coordinate: x: int y: int def __add__(self, other): return self.__class__( self.x+other.x, self.y+other.y ) class Identicon: WIDTH = 17 HEIGHT = 9 SYMBOLS = ' .o+=*BOX@%&#/^' START_SYMBOL = 'S' END_SYMBOL = 'E' BISHOP_START = Coordinate(8, 4) MOVES = { 0: Coordinate(-1, -1), # 00 ↖ 1: Coordinate( 1, -1), # 01 ↗ 2: Coordinate(-1, 1), # 10 ↙ 3: Coordinate( 1, 1), # 11 ↘ } def __init__(self, data): self._data = data self._grid = [[0] * self.WIDTH for _ in range(self.HEIGHT)] self._bishop_position = deepcopy(self.BISHOP_START) self._end_position = None def calculate(self): for command in self._get_commands(): self._execute(command) self._end_position = self._bishop_position def _get_commands(self): commands = [] for octet in self._data: commands.extend([ octet & 0b11, # 00 00 00 [00] (octet >> 2) & 0b11, # 00 00 [00] 00 (octet >> 4) & 0b11, # 00 [00] 00 00 (octet >> 6) & 0b11, # [00] 00 00 00 ]) return commands def _execute(self, command): move = self.MOVES[command] self._bishop_position = self._ensure_within_grid_borders(self._bishop_position + move) self._increment_current_position() def _ensure_within_grid_borders(self, coordinate: Coordinate): coordinate.x = max(0, min(self.WIDTH-1, coordinate.x)) coordinate.y = max(0, min(self.HEIGHT-1, coordinate.y)) return coordinate def _increment_current_position(self): self._grid[self._bishop_position.y][self._bishop_position.x] += 1 def __str__(self): icon = self._header+'\n' for y, row in enumerate(self._grid): icon += '|' for x, cell in enumerate(row): icon += self._determine_symbol(Coordinate(x, y), cell) icon += '|\n' icon += self._header return icon @property def _header(self): return f'+{self.WIDTH*"-"}+' def _determine_symbol(self, coordinate, cell): symbol = self.SYMBOLS[min(cell, len(self.SYMBOLS)-1)] if coordinate == self.BISHOP_START: symbol = self.START_SYMBOL elif coordinate == self._end_position: symbol = self.END_SYMBOL return symbol