Files
evilnkode/src/utils.py
2025-08-29 14:14:37 -05:00

83 lines
2.3 KiB
Python

import random
from enum import Enum
from math import factorial, comb
from src.evilkode import Observation
from src.keypad import Keypad
def total_valid_nkode_states(k: int, p: int) -> int:
return factorial(k) ** (p-1)
def total_shuffle_states(k: int, p: int) -> int:
return comb((p-1), (p-1) // 2) * factorial(k)
class ShuffleTypes(Enum):
FULL_SHUFFLE = "FULL_SHUFFLE"
SPLIT_SHUFFLE = "SPLIT_SHUFFLE"
TOWER_SHUFFLE = "TOWER_SHUFFLE"
def observations(target_passcode: list[int], number_of_keys:int, properties_per_key: int, min_complexity: int, min_disparity: int, shuffle_type: ShuffleTypes, number_of_observations: int = 100):
k = number_of_keys
p = properties_per_key
keypad = Keypad.new_keypad(k, p)
def obs_gen():
for _ in range(number_of_observations):
yield Observation(
keypad=keypad.keypad.copy(),
key_selection=keypad.key_entry(target_passcode=target_passcode)
)
match shuffle_type:
case ShuffleTypes.FULL_SHUFFLE:
keypad.full_shuffle()
case ShuffleTypes.SPLIT_SHUFFLE:
keypad.split_shuffle()
case ShuffleTypes.TOWER_SHUFFLE:
keypad.tower_shuffle()
case _:
raise Exception(f"no shuffle type {shuffle_type}")
return obs_gen()
def passcode_generator(k: int, p: int, n: int, c: int, d: int) -> list[int]:
assert n >= c
assert p*k >= c
assert n >= d
assert p >= d
passcode_prop = []
passcode_set = []
valid_choices = {i for i in range(k*p)}
repeat_set = n-d
repeat_prop = n-c
prop_added = set()
set_added = set()
for _ in range(n):
prop = random.choice(list(valid_choices))
prop_set = prop//p
passcode_prop.append(prop)
passcode_set.append(prop_set)
if prop in prop_added:
repeat_prop -= 1
if prop_set in set_added:
repeat_set -= 1
prop_added.add(prop)
set_added.add(prop_set)
if repeat_prop <= 0:
valid_choices -= prop_added
if repeat_set <= 0:
for el in valid_choices.copy():
if el // p in set_added:
valid_choices.remove(el)
return passcode_prop