83 lines
2.3 KiB
Python
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
|