Browse Source

update: serial number of solutions

master
Dnomd343 4 weeks ago
parent
commit
908184699b
  1. 257
      verify/classic.py

257
verify/classic.py

@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json import json
from enum import Enum
import functools import functools
from enum import Enum
from pprint import pprint from pprint import pprint
from klotski import Group, Layout from klotski import Block, Group, Layout
@functools.cache @functools.cache
@ -19,16 +19,17 @@ class Solution:
Z = 2 Z = 2
@staticmethod @staticmethod
def __solu_info(layout: str) -> tuple[int, int]: # (max_step, ethnic_size) def __solu_info(layout: str) -> tuple[int, int]: # (ethnic_deep, ethnic_size)
cases = [x for x, y in global_data().items() if layout in y['solutions']] cases = [x for x, y in global_data().items() if layout in y['solutions']]
steps = [global_data()[x]['step'] for x in cases] steps = [global_data()[x]['step'] for x in cases]
assert min(steps) == 0 assert min(steps) == 0
return max(steps), len(cases) return max(steps), len(cases)
def __init__(self, code: str, solu_type: Type): def __init__(self, code: str, solu_type: Type):
self.__name = ''
self.__code = code self.__code = code
self.__type = solu_type self.__type = solu_type
self.__max_step, self.__ethnic_size = Solution.__solu_info(code) self.__ethnic_deep, self.__ethnic_size = Solution.__solu_info(code)
@property @property
def code(self) -> str: def code(self) -> str:
@ -38,6 +39,14 @@ class Solution:
def type(self) -> Type: def type(self) -> Type:
return self.__type return self.__type
@property
def name(self) -> str:
return self.__name
@name.setter
def name(self, name: str) -> None:
self.__name = name
@property @property
def group(self) -> Group: def group(self) -> Group:
return self.layout.group return self.layout.group
@ -47,21 +56,22 @@ class Solution:
return Layout(self.code) return Layout(self.code)
@property @property
def max_step(self) -> int: def ethnic_deep(self) -> int:
return self.__max_step return self.__ethnic_deep
@property @property
def ethnic_size(self) -> int: def ethnic_size(self) -> int:
return self.__ethnic_size return self.__ethnic_size
def __repr__(self): def __repr__(self):
val = f'{self.ethnic_size}, {self.max_step}' return (f'<Solution {self.name} [{self.code}] {self.type.name} '
return f'{self.code} {self.type.name} ({val})' f'({self.ethnic_size} {self.ethnic_deep})>')
class SoluGroup: class SoluGroup:
def __init__(self, name: str): def __init__(self, name: str):
self.__name = name self.__name = ''
self.__inner_name = name # klotski group name
self.__x_solus: list[Solution] = [] self.__x_solus: list[Solution] = []
self.__y_solus: list[Solution] = [] self.__y_solus: list[Solution] = []
self.__z_solus: list[Solution] = [] self.__z_solus: list[Solution] = []
@ -78,6 +88,14 @@ class SoluGroup:
def name(self) -> str: def name(self) -> str:
return self.__name return self.__name
@name.setter
def name(self, name: str) -> None:
self.__name = name
@property
def inner_name(self) -> str:
return self.__inner_name
@property @property
def solus(self) -> list[Solution]: def solus(self) -> list[Solution]:
return self.__x_solus + self.__y_solus + self.__z_solus return self.__x_solus + self.__y_solus + self.__z_solus
@ -94,13 +112,32 @@ class SoluGroup:
def group_size(self) -> int: def group_size(self) -> int:
return self.group.size return self.group.size
@property
def group_valid_size(self) -> int:
def is_invalid_solu(layout: Layout) -> bool:
if str(layout)[0] != 'D': # 2x2 block address not 13
return False
block_seq = layout.dump_seq()
is_type_x = block_seq[12] == Block.SPACE and block_seq[16] == Block.SPACE
is_type_y = block_seq[15] == Block.SPACE and block_seq[19] == Block.SPACE
is_type_z = block_seq[9] == Block.SPACE and block_seq[10] == Block.SPACE
return not (is_type_x or is_type_y or is_type_z)
num = len([x for x in list(self.group.cases()) if is_invalid_solu(x)])
return self.group_size - num
@property @property
def ethnic_sizes(self) -> list[int]: def ethnic_sizes(self) -> list[int]:
return sorted([x.ethnic_size for x in self.solus], key=lambda x: -x) return sorted([x.ethnic_size for x in self.solus], key=lambda x: -x)
@property @property
def max_ethnic_size(self) -> int: def max_ethnic_size(self) -> int:
return max(self.ethnic_sizes) return max(x.ethnic_size for x in self.solus)
@property
def max_ethnic_deep(self) -> int:
return max(x.ethnic_deep for x in self.solus)
@property @property
def min_case(self) -> Layout: def min_case(self) -> Layout:
@ -111,63 +148,195 @@ class SoluGroup:
return min(x.layout for x in self.solus) return min(x.layout for x in self.solus)
def __repr__(self): def __repr__(self):
return (f'<SoluGroup {self.name} ({self.group_size}) ' return (f'<SoluGroup {self.name} {self.inner_name} ({self.group_valid_size}/{self.group_size}) '
f'[{self.min_solu}/{self.min_case}] {self.solu_num} -> {self.ethnic_sizes}>') f'[{self.min_solu}/{self.min_case}] ({self.max_ethnic_deep}) '
f'{self.solu_num} -> {self.ethnic_sizes}>')
def build_m_index(self) -> None:
assert self.__name[-1] == 'M'
assert len(self.__x_solus) == len(self.__y_solus)
self.__x_solus.sort(key=lambda x: (-x.ethnic_size, -x.ethnic_deep, x.layout))
y_index = {str(y.layout.to_horizontal_mirror())[:7]: x for x, y in enumerate(self.__x_solus)}
self.__y_solus.sort(key=lambda x: y_index[x.code])
for num in range(len(self.__x_solus)):
self.__x_solus[num].name = f'{self.name}-{num:03}X'
self.__y_solus[num].name = f'{self.name}-{num:03}Y'
z_map = {x.code: x for x in self.__z_solus}
z_pairs = [tuple(sorted([x.layout, x.layout.to_horizontal_mirror()])) for x in self.__z_solus]
z_pairs = [(z_map[str(x)[:7]], z_map[str(y)[:7]]) for x, y in set(z_pairs)]
z_pairs.sort(key=lambda x: (-x[0].ethnic_size, -x[0].ethnic_deep, x[0].layout))
for num in range(len(z_pairs)):
if z_pairs[num][0] == z_pairs[num][1]:
z_pairs[num][0].name = f'{self.name}-{num:02}Z'
else:
z_pairs[num][0].name = f'{self.name}-{num:02}ZL'
z_pairs[num][1].name = f'{self.name}-{num:02}ZR'
def build_lx_index(self) -> dict[str, str]:
assert self.__name[-1] == 'L'
right_data = {}
self.__x_solus.sort(key=lambda x: (-x.ethnic_size, -x.ethnic_deep, x.layout))
for num in range(len(self.__x_solus)):
self.__x_solus[num].name = f'{self.name}-{num:01}X'
mirror = str(self.__x_solus[num].layout.to_horizontal_mirror())[:7]
right_data[mirror] = f'{num:01}Y'
return right_data
def build_rx_index(self) -> dict[str, str]:
assert self.__name[-1] == 'R'
left_data = {}
self.__x_solus.sort(key=lambda x: (-x.ethnic_size, -x.ethnic_deep, x.layout))
for num in range(len(self.__x_solus)):
self.__x_solus[num].name = f'{self.name}-{num:01}X'
mirror = str(self.__x_solus[num].layout.to_horizontal_mirror())[:7]
left_data[mirror] = f'{num:01}Y'
return left_data
def apply_ly_index(self, data: dict[str, str]) -> None:
assert self.__name[-1] == 'L'
for solu in self.__y_solus:
solu.name = f'{self.name}-{data[solu.code]}'
def apply_ry_index(self, data: dict[str, str]) -> None:
assert self.__name[-1] == 'R'
for solu in self.__y_solus:
solu.name = f'{self.name}-{data[solu.code]}'
def build_lz_index(self) -> dict[str, str]:
assert self.__name[-1] == 'L'
data = {}
self.__z_solus.sort(key=lambda x: (-x.ethnic_size, -x.ethnic_deep, x.layout))
for num in range(len(self.__z_solus)):
self.__z_solus[num].name = f'{self.name}-{num:01}Z'
mirror = str(self.__z_solus[num].layout.to_horizontal_mirror())[:7]
data[mirror] = f'{num:01}Z'
return data
def apply_rz_index(self, data: dict[str, str]) -> None:
assert self.__name[-1] == 'R'
for solu in self.__z_solus:
solu.name = f'{self.name}-{data[solu.code]}'
class SoluPattern: class SoluPattern:
def __init__(self, *solu_groups: SoluGroup): def __init__(self, *solu_groups: SoluGroup):
self.__pattern_id = -1
inner_type_id = solu_groups[0].inner_name[:3]
self.__type_id = {'174': 0, '169': 1, '164': 2, '159': 3, '154': 4, '149': 5}[inner_type_id]
assert len(solu_groups) in [1, 2] assert len(solu_groups) in [1, 2]
if len(solu_groups) == 1: if len(solu_groups) == 1:
self.__is_single = True self.__is_single = True
self.__solu_m = solu_groups[0] self.__group_m = solu_groups[0]
else: else:
self.__is_single = False self.__is_single = False
self.__solu_l, self.__solu_r = solu_groups # TODO: confirm l/r if solu_groups[0].min_solu < solu_groups[1].min_solu:
assert self.__solu_l.name[:-1] == self.__solu_r.name[:-1] self.__group_l, self.__group_r = solu_groups
assert self.__solu_l.solu_num == self.__solu_r.solu_num else:
assert self.__solu_l.group_size == self.__solu_r.group_size self.__group_r, self.__group_l = solu_groups
assert self.__solu_l.ethnic_sizes == self.__solu_r.ethnic_sizes assert self.__group_l.inner_name[:-1] == self.__group_r.inner_name[:-1]
assert self.__group_l.solu_num == self.__group_r.solu_num
assert self.__group_l.group_size == self.__group_r.group_size
assert self.__group_l.ethnic_sizes == self.__group_r.ethnic_sizes
@property
def type_id(self) -> int:
return self.__type_id
@property
def pattern_id(self) -> int:
return self.__pattern_id
@pattern_id.setter
def pattern_id(self, id_val: int) -> None:
self.__pattern_id = id_val
if self.__is_single:
self.__group_m.name = f'{self.type_id}-{self.pattern_id:02}M'
else:
self.__group_l.name = f'{self.type_id}-{self.pattern_id:02}L'
self.__group_r.name = f'{self.type_id}-{self.pattern_id:02}R'
@property @property
def pattern_size(self) -> int: def pattern_size(self) -> int:
if self.__is_single: if self.__is_single:
return self.__solu_m.group_size return self.__group_m.group_size
return self.__solu_l.group_size * 2 return self.__group_l.group_size * 2
@property
def pattern_valid_size(self) -> int:
if self.__is_single:
return self.__group_m.group_valid_size
assert self.__group_l.group_valid_size == self.__group_r.group_valid_size
return self.__group_l.group_valid_size * 2
@property @property
def ethnic_sizes(self) -> list[int]: def ethnic_sizes(self) -> list[int]:
if self.__is_single: if self.__is_single:
return self.__solu_m.ethnic_sizes return self.__group_m.ethnic_sizes
return self.__solu_l.ethnic_sizes assert self.__group_l.ethnic_sizes == self.__group_r.ethnic_sizes
return self.__group_l.ethnic_sizes
@property @property
def max_ethnic_size(self) -> int: def max_ethnic_size(self) -> int:
return max(self.ethnic_sizes) if self.__is_single:
return self.__group_m.max_ethnic_size
assert self.__group_l.max_ethnic_size == self.__group_r.max_ethnic_size
return self.__group_l.max_ethnic_size
@property
def max_ethnic_deep(self) -> int:
if self.__is_single:
return self.__group_m.max_ethnic_deep
assert self.__group_l.max_ethnic_deep == self.__group_r.max_ethnic_deep
return self.__group_l.max_ethnic_deep
@property @property
def min_case(self) -> Layout: def min_case(self) -> Layout:
if self.__is_single: if self.__is_single:
return self.__solu_m.min_case return self.__group_m.min_case
else: else:
return min(self.__solu_l.min_case, self.__solu_r.min_case) return min(self.__group_l.min_case, self.__group_r.min_case)
@property @property
def min_solu(self) -> Layout: def min_solu(self) -> Layout:
if self.__is_single: if self.__is_single:
return self.__solu_m.min_solu return self.__group_m.min_solu
else:
return min(self.__group_l.min_solu, self.__group_r.min_solu)
@property
def solus(self) -> list[Solution]:
if self.__is_single:
return self.__group_m.solus
else: else:
return min(self.__solu_l.min_solu, self.__solu_r.min_solu) return self.__group_l.solus + self.__group_r.solus
def __repr__(self): def __repr__(self):
if self.__is_single: if self.__is_single:
name = self.__solu_m.name name = self.__group_m.name
solu_num = f'{self.__solu_m.solu_num}' inner_name = self.__group_m.inner_name
solu_num = f'{self.__group_m.solu_num}'
group_repr = f'{self.__group_m}'
else: else:
name = f'{self.__solu_l.name}|{self.__solu_r.name}' name = f'{self.__group_l.name}|{self.__group_r.name}'
solu_num = f'{self.__solu_l.solu_num}*2' inner_name = f'{self.__group_l.inner_name}|{self.__group_r.inner_name}'
return (f'<SoluPattern {name} ({self.pattern_size}) ' solu_num = f'{self.__group_l.solu_num}*2'
f'[{self.min_solu}/{self.min_case}] {solu_num} -> {self.ethnic_sizes}>') group_repr = f'{self.__group_l}\n{self.__group_r}'
return (f'<SoluPattern {name} {inner_name} ({self.pattern_valid_size}/{self.pattern_size}) '
f'[{self.min_solu}/{self.min_case}] ({self.max_ethnic_deep}) '
f'{solu_num} -> {self.ethnic_sizes}>\n{group_repr}')
def build_index(self) -> None:
if self.__is_single:
self.__group_m.build_m_index()
else:
self.__group_r.apply_ry_index(self.__group_l.build_lx_index())
self.__group_l.apply_ly_index(self.__group_r.build_rx_index())
self.__group_r.apply_rz_index(self.__group_l.build_lz_index())
def load_valid_solutions(num: int) -> list[Solution]: def load_valid_solutions(num: int) -> list[Solution]:
@ -178,7 +347,7 @@ def load_valid_solutions(num: int) -> list[Solution]:
return data_x + data_y + data_z return data_x + data_y + data_z
def split_into_groups(solus: list[Solution]) -> list[SoluPattern]: def split_patterns(solus: list[Solution]) -> list[SoluPattern]:
groups = {} groups = {}
for solu in solus: for solu in solus:
group = str(solu.group) group = str(solu.group)
@ -200,21 +369,21 @@ def split_into_groups(solus: list[Solution]) -> list[SoluPattern]:
for pattern_tuple in sorted(set(pattern_tuples)): for pattern_tuple in sorted(set(pattern_tuples)):
patterns.append(SoluPattern(*[groups[x] for x in pattern_tuple])) patterns.append(SoluPattern(*[groups[x] for x in pattern_tuple]))
return sorted(patterns, key=lambda x: (-x.pattern_size, -x.max_ethnic_size, x.min_solu)) patterns.sort(key=lambda x: (-x.pattern_valid_size, -x.pattern_size, -x.max_ethnic_size, x.min_solu))
for pattern_id, pattern in enumerate(patterns):
pattern.pattern_id = pattern_id
pattern.build_index()
return patterns
def main() -> None: def main() -> None:
solus = []
# solu = Solution('DAAF4CC', Solution.Type.X)
# print(solu)
# for solu_group in split_into_groups(load_valid_solutions(1)):
# print(solu_group)
for num in range(6): # 0/1/2/3/4/5 for num in range(6): # 0/1/2/3/4/5
for solu_group in split_into_groups(load_valid_solutions(num)): for solu_pattern in split_patterns(load_valid_solutions(num)):
print(solu_group) solus.extend(solu_pattern.solus)
print(solu_pattern, end='\n\n')
print('-' * 238) print('-' * 238)
pprint(solus)
if __name__ == "__main__": if __name__ == "__main__":

Loading…
Cancel
Save