Implement OpenSSH style identicon calculation

This commit is contained in:
Kristóf Tóth 2022-02-10 00:43:17 +01:00
commit 0dd22bd7bc
3 changed files with 179 additions and 0 deletions

1
identicon/__init__.py Normal file
View File

@ -0,0 +1 @@
from .identicon import Identicon

89
identicon/identicon.py Normal file
View File

@ -0,0 +1,89 @@
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

View File

@ -0,0 +1,89 @@
from base64 import b64decode
from dataclasses import dataclass
from textwrap import dedent
import pytest
from .identicon import Identicon
@dataclass
class ReferenceIcon:
fingerprint: bytes
icon: str
REFERENCE_ICONS = [
ReferenceIcon(
bytearray.fromhex(''.join('b7:a3:e2:ce:09:06:ad:39:c8:1d:ad:b5:95:48:8f:99'.split(':'))),
dedent('''\
+-----------------+
| |
| |
| . |
| .o * . |
| ...E +S . |
|...++ o . . |
|..+oo. o |
| o o.. . . |
| o=.. |
+-----------------+'''
)
),
ReferenceIcon(
b64decode('sX58LC41tVlsctG1+H5PrkbMDfG374yghEg96KlnFZA=='),
dedent('''\
+-----------------+
| . .o|
| E + o|
| .. o = |
| o.o o B o|
| o S. . X +o|
| o +.+o.o =..|
| +.o.=.+. .+|
| .o .+ + ..=+|
| .o .o .oo=|
+-----------------+'''
)
),
ReferenceIcon(
bytearray.fromhex(''.join('9b:cf:42:fd:25:ff:ce:83:e9:e0:f1:d4:10:c3:ae:a8'.split(':'))),
dedent('''\
+-----------------+
| |
| . |
| + |
| . o |
| S. o |
| .oo o + |
| .o. = =o. |
| oo. *o.o |
| E .o..o o=|
+-----------------+'''
)
),
ReferenceIcon(
b64decode('5/WozN6loc0G8DxxrJhV8+aG/6Dvz1/gTBVipWU9nb0='),
dedent('''\
+-----------------+
| o.==|
| + =o=|
| o + +|
| . o o oE |
| SB.+.+o |
| oo*..*o. |
| .ooo* .|
| o *.=.o.|
| .*.*ooo*|
+-----------------+'''
)
)
]
@pytest.mark.parametrize("reference", REFERENCE_ICONS)
def test_identicon(reference):
icon = Identicon(reference.fingerprint)
icon.calculate()
assert str(icon) == reference.icon