mirror of https://github.com/dnomd343/klotski.git
3 changed files with 1571 additions and 12 deletions
File diff suppressed because it is too large
@ -0,0 +1,198 @@ |
|||||
|
#!/usr/bin/env python3 |
||||
|
|
||||
|
import re |
||||
|
import json |
||||
|
import functools |
||||
|
from klotski import Block, Layout |
||||
|
|
||||
|
type Info = tuple[int, int, str, int, str] |
||||
|
|
||||
|
|
||||
|
@functools.cache |
||||
|
def solu_data() -> dict[str, dict]: |
||||
|
return json.loads(open('./data/data.json').read()) |
||||
|
|
||||
|
|
||||
|
def solu_ethnic_info(code: str) -> tuple[int, int]: # (ethnic_size, ethnic_deep) |
||||
|
cases = [x for x, y in solu_data().items() if code in y['solutions']] |
||||
|
steps = [solu_data()[x]['step'] for x in cases] |
||||
|
return len(cases), max(steps) |
||||
|
|
||||
|
|
||||
|
def is_ethnic_case(code: Layout) -> bool: |
||||
|
seq = code.dump_seq() |
||||
|
if seq[13] != Block.B_2x2: |
||||
|
return True |
||||
|
if seq[12] == Block.SPACE and seq[16] == Block.SPACE: |
||||
|
return True |
||||
|
if seq[15] == Block.SPACE and seq[19] == Block.SPACE: |
||||
|
return True |
||||
|
if seq[9] == Block.SPACE and seq[10] == Block.SPACE: |
||||
|
return True |
||||
|
return False |
||||
|
|
||||
|
|
||||
|
def split_name(code: str, name: str) -> Info: |
||||
|
match = re.match(r'^([0-5])-(\d{2})([MLR])-(\d{3})(X|Y|Z|ZL|ZR)$', name) |
||||
|
type_id, solu_type = int(match[1]), match[5] |
||||
|
assert Layout(code).n_1x2 == type_id |
||||
|
|
||||
|
layout_seq = Layout(code).dump_seq() |
||||
|
assert solu_type[0] in ['X', 'Y', 'Z'] |
||||
|
if solu_type[0] == 'X': |
||||
|
assert layout_seq[12] == Block.SPACE and layout_seq[16] == Block.SPACE |
||||
|
elif solu_type[0] == 'Y': |
||||
|
assert layout_seq[15] == Block.SPACE and layout_seq[19] == Block.SPACE |
||||
|
elif solu_type[0] == 'Z': |
||||
|
assert layout_seq[9] == Block.SPACE and layout_seq[10] == Block.SPACE |
||||
|
|
||||
|
return type_id, int(match[2]), match[3], int(match[4]), solu_type |
||||
|
|
||||
|
|
||||
|
def split_into_groups(data: dict[str, Info]) -> dict[tuple[int, int, str], dict[str, Info]]: |
||||
|
groups = {} |
||||
|
for code, info in data.items(): |
||||
|
name = info[0], info[1], info[2] |
||||
|
if name not in groups: |
||||
|
groups[name] = {} |
||||
|
groups[name][code] = info |
||||
|
|
||||
|
for name, codes in groups.items(): |
||||
|
group = Layout(list(codes)[0]).group |
||||
|
cases = list(group.cases()) |
||||
|
for code in codes: |
||||
|
assert Layout(code) in cases |
||||
|
|
||||
|
if name[2] == 'M': |
||||
|
assert group.is_horizontal_mirror |
||||
|
else: # L / R |
||||
|
assert not group.is_horizontal_mirror |
||||
|
|
||||
|
if name[2] == 'L': |
||||
|
min_solu_l = min(codes.keys()) |
||||
|
min_solu_r = min(groups[(name[0], name[1], 'R')].keys()) |
||||
|
assert min_solu_l < min_solu_r |
||||
|
|
||||
|
return groups |
||||
|
|
||||
|
|
||||
|
def verify_m_group(data: dict[str, Info]) -> tuple[int, int, int, str]: |
||||
|
for code, info in data.items(): |
||||
|
assert info[4] in ['X', 'Y', 'Z', 'ZL', 'ZR'] |
||||
|
if info[4] == 'X': |
||||
|
y_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert data[y_code] == (info[0], info[1], info[2], info[3], 'Y') |
||||
|
elif info[4] == 'Y': |
||||
|
x_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert data[x_code] == (info[0], info[1], info[2], info[3], 'X') |
||||
|
elif info[4] == 'Z': |
||||
|
assert Layout(code).is_horizontal_mirror |
||||
|
elif info[4] == 'ZL': |
||||
|
zr_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert code < zr_code |
||||
|
assert data[zr_code] == (info[0], info[1], info[2], info[3], 'ZR') |
||||
|
elif info[4] == 'ZR': |
||||
|
zl_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert zl_code < code |
||||
|
assert data[zl_code] == (info[0], info[1], info[2], info[3], 'ZL') |
||||
|
|
||||
|
x_solus = sorted([(x, y[3]) for x, y in data.items() if y[4] == 'X'], key=lambda x: x[1]) |
||||
|
assert [x[1] for x in x_solus] == list(range(len(x_solus))) |
||||
|
x_data = [(*solu_ethnic_info(x[0]), x[0]) for x in x_solus] |
||||
|
assert len(set(x_data)) == len(x_data) and x_data == sorted(x_data, key=lambda x: (-x[0], -x[1], x[2])) |
||||
|
|
||||
|
z_solus = sorted([(x, y[3]) for x, y in data.items() if y[4] in {'Z', 'ZL'}], key=lambda x: x[1]) |
||||
|
assert [x[1] for x in z_solus] == list(range(len(z_solus))) |
||||
|
z_data = [(*solu_ethnic_info(x[0]), x[0]) for x in z_solus] |
||||
|
assert len(set(z_data)) == len(z_data) and z_data == sorted(z_data, key=lambda x: (-x[0], -x[1], x[2])) |
||||
|
|
||||
|
group = Layout(list(data)[0]).group |
||||
|
valid_size = len([x for x in list(group.cases()) if is_ethnic_case(x)]) |
||||
|
max_ethnic_size = max(x_data[0][0] if x_data else -1, z_data[0][0] if z_data else -1) |
||||
|
min_solu = min(list(data)) |
||||
|
return valid_size, group.size, max_ethnic_size, min_solu |
||||
|
|
||||
|
|
||||
|
def verify_lr_group(l_data: dict[str, Info], r_data: dict[str, Info]) -> tuple[int, int, int, str]: |
||||
|
assert len(l_data) == len(r_data) |
||||
|
for code, info in l_data.items(): |
||||
|
assert info[4] in ['X', 'Y', 'Z'] |
||||
|
if info[4] == 'X': |
||||
|
y_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert r_data[y_code] == (info[0], info[1], 'R', info[3], 'Y') |
||||
|
elif info[4] == 'Y': |
||||
|
x_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert r_data[x_code] == (info[0], info[1], 'R', info[3], 'X') |
||||
|
elif info[4] == 'Z': |
||||
|
assert not Layout(code).is_horizontal_mirror |
||||
|
z_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert r_data[z_code] == (info[0], info[1], 'R', info[3], 'Z') |
||||
|
for code, info in r_data.items(): |
||||
|
assert info[4] in ['X', 'Y', 'Z'] |
||||
|
if info[4] == 'X': |
||||
|
y_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert l_data[y_code] == (info[0], info[1], 'L', info[3], 'Y') |
||||
|
elif info[4] == 'Y': |
||||
|
x_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert l_data[x_code] == (info[0], info[1], 'L', info[3], 'X') |
||||
|
elif info[4] == 'Z': |
||||
|
assert not Layout(code).is_horizontal_mirror |
||||
|
z_code = str(Layout(code).to_horizontal_mirror())[:7] |
||||
|
assert l_data[z_code] == (info[0], info[1], 'L', info[3], 'Z') |
||||
|
|
||||
|
lx_solus = sorted([(x, y[3]) for x, y in l_data.items() if y[4] == 'X'], key=lambda x: x[1]) |
||||
|
assert [x[1] for x in lx_solus] == list(range(len(lx_solus))) |
||||
|
lx_data = [(*solu_ethnic_info(x[0]), x[0]) for x in lx_solus] |
||||
|
assert len(set(lx_data)) == len(lx_data) and lx_data == sorted(lx_data, key=lambda x: (-x[0], -x[1], x[2])) |
||||
|
|
||||
|
rx_solus = sorted([(x, y[3]) for x, y in r_data.items() if y[4] == 'X'], key=lambda x: x[1]) |
||||
|
assert [x[1] for x in rx_solus] == list(range(len(rx_solus))) |
||||
|
rx_data = [(*solu_ethnic_info(x[0]), x[0]) for x in rx_solus] |
||||
|
assert len(set(rx_data)) == len(rx_data) and rx_data == sorted(rx_data, key=lambda x: (-x[0], -x[1], x[2])) |
||||
|
|
||||
|
lz_solus = sorted([(x, y[3]) for x, y in l_data.items() if y[4] == 'Z'], key=lambda x: x[1]) |
||||
|
assert [x[1] for x in lz_solus] == list(range(len(lz_solus))) |
||||
|
lz_data = [(*solu_ethnic_info(x[0]), x[0]) for x in lz_solus] |
||||
|
assert len(set(lz_data)) == len(lz_data) and lz_data == sorted(lz_data, key=lambda x: (-x[0], -x[1], x[2])) |
||||
|
|
||||
|
l_group = Layout(list(l_data)[0]).group |
||||
|
r_group = Layout(list(r_data)[0]).group |
||||
|
assert l_group.size == r_group.size |
||||
|
|
||||
|
l_valid_size = len([x for x in list(l_group.cases()) if is_ethnic_case(x)]) |
||||
|
r_valid_size = len([x for x in list(r_group.cases()) if is_ethnic_case(x)]) |
||||
|
assert l_valid_size == r_valid_size |
||||
|
|
||||
|
max_ethnic_size = max(lx_data[0][0] if lx_data else -1, rx_data[0][0] if rx_data else -1, lz_data[0][0] if lz_data else -1) |
||||
|
min_solu = min(list(l_data) + list(r_data)) |
||||
|
return l_valid_size * 2, l_group.size * 2, max_ethnic_size, min_solu |
||||
|
|
||||
|
|
||||
|
def verify_pattern(info: dict[int, tuple[int, int, int, str]]) -> None: |
||||
|
assert list(info.keys()) == list(range(len(info))) |
||||
|
info = list(info.values()) |
||||
|
assert info == sorted(info, key=lambda x: (-x[0], -x[1], -x[2], x[3])) |
||||
|
|
||||
|
|
||||
|
def main() -> None: |
||||
|
data = json.loads(open('classic.json').read()) |
||||
|
data = {x: split_name(x, y) for x, y in data.items()} |
||||
|
groups = split_into_groups(data) |
||||
|
|
||||
|
pattern_info = {0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}} |
||||
|
for name, group in groups.items(): |
||||
|
if name[2] == 'M': |
||||
|
pattern_info[name[0]][name[1]] = verify_m_group(group) |
||||
|
elif name[2] == 'L': |
||||
|
pattern_info[name[0]][name[1]] = verify_lr_group(group, groups[(name[0], name[1], 'R')]) |
||||
|
|
||||
|
verify_pattern(pattern_info[0]) |
||||
|
verify_pattern(pattern_info[1]) |
||||
|
verify_pattern(pattern_info[2]) |
||||
|
verify_pattern(pattern_info[3]) |
||||
|
verify_pattern(pattern_info[4]) |
||||
|
verify_pattern(pattern_info[5]) |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
main() |
Loading…
Reference in new issue