diff options
| -rwxr-xr-x | solver.py | 112 | ||||
| -rw-r--r-- | tests_solver/level_test_ar_x.yaml | 8 | ||||
| -rw-r--r-- | tests_solver/level_test_type.yaml | 9 | ||||
| -rw-r--r-- | tests_solver/level_test_type_short.yaml | 9 | ||||
| -rw-r--r-- | tests_solver/test_solver.py | 141 | 
5 files changed, 231 insertions, 48 deletions
@@ -18,18 +18,22 @@ import zlib  import docopt  import yaml -  class Kana: +    types_without_kana = ('void', 'empt') +    types_valid_for_chain = ('norm', 'froz', 'rock', 'ar_u', 'ar_l', 'ar_d', 'ar_r') +    types_with_kana = types_valid_for_chain + ('myst', ) +    types_all = types_with_kana + types_without_kana +      def __init__(self, type_name, kana=None):          self.type_name = type_name          self.kana = kana          #print(type_name)          #print(kana) -        assert type_name in ('void', 'norm', 'empt', 'froz', 'rock', 'myst') -        if type_name in ('norm', 'rock', 'froz', 'myst'): -            assert kana[0] in ('k', 's', 'n') +        assert type_name in self.types_all +        if type_name in self.types_with_kana: +            assert kana[0] in ('k', 's', 'n', 'h', 't', 'm')              assert kana[1] in ('a', 'i', 'u', 'e', 'o')      def __repr__(self): @@ -38,27 +42,11 @@ class Kana:      def __eq__(self, other):          return self.type_name == other.type_name and self.kana == other.kana -kana_void = Kana("void") - - -def is_swappable(kana1, kana2): -    table_ok = { -            "norm": ("norm", "empt", "froz"), -            "froz": ("norm", "empt"), -    } -    if kana1.type_name in table_ok: -        if kana2.type_name in table_ok[kana1.type_name]: -            return True -    elif kana2.type_name in table_ok: -        if kana1.type_name in table_ok[kana2.type_name]: -            return True -    return False +kana_void = Kana('void')  class KanaGrid: -    valid_types_for_chain = ("froz", "norm", "rock") -      def __init__(self, size, grid, action_count=0, score=0, parent=None):          self.width = size[0] @@ -77,13 +65,43 @@ class KanaGrid:                  parent=self.parent,                  ) +    def is_swappable(self, pos1, pos2): +        kana1 = self.get_kana(pos1) +        kana2 = self.get_kana(pos2) +        table_ok = { +            'norm': ('norm', 'empt', 'froz', 'ar_u', 'ar_r', 'ar_d', 'ar_l'), +            'empt': ('norm',         'froz', 'ar_u', 'ar_r', 'ar_d', 'ar_l'), +            'froz': ('norm', 'empt',         'ar_u', 'ar_r', 'ar_d', 'ar_l'), +            'ar_u': ('norm', 'empt', 'froz',                 'ar_d'        ), +            'ar_r': ('norm', 'empt', 'froz',                         'ar_l'), +            'ar_d': ('norm', 'empt', 'froz', 'ar_u'                        ), +            'ar_l': ('norm', 'empt', 'froz',         'ar_r'                ), +        } +        if kana1.type_name in table_ok: +            if kana2.type_name in table_ok[kana1.type_name]: +                ar_vect_ok = { +                        'ar_u': ( 0, -1), +                        'ar_l': (-1,  0), +                        'ar_d': ( 0,  1), +                        'ar_r': ( 1,  0), +                } +                vect1_target = ar_vect_ok.get(kana1.type_name, None) +                vect2_target = ar_vect_ok.get(kana2.type_name, None) +                if vect1_target or vect2_target: +                    vect1 = (pos2[0] - pos1[0], pos2[1] - pos1[1]) +                    vect2 = (pos1[0] - pos2[0], pos1[1] - pos2[1]) +                    if vect1 != vect1_target and vect2 != vect2_target: +                        return False +                return True +        return False +      def action(self, pos, action_type): -        kana = self.get_kana(pos)          if action_type == "reveal": -            if kana.type_name == "myst": +            kana = self.get_kana(pos) +            if kana.type_name == 'myst':                  new_grid = self.copy()                  new_grid.action_count += 1 -                new_grid.set_kana(pos, Kana("norm", kana.kana)) +                new_grid.set_kana(pos, Kana('norm', kana.kana))                  return new_grid          elif action_type in ("up", "right", "down", "left"):              if action_type == "up": @@ -94,8 +112,7 @@ class KanaGrid:                  pos_dest = (pos[0], pos[1]+1)              elif action_type == "left":                  pos_dest = (pos[0]-1, pos[1]) -            kana_dest = self.get_kana(pos_dest) -            if is_swappable(kana, kana_dest): +            if self.is_swappable(pos, pos_dest):                  new_grid = self.copy()                  new_grid.action_count += 1                  new_grid.swap_kana(pos, pos_dest) @@ -105,7 +122,7 @@ class KanaGrid:          for y in range(self.height):              for x in range(self.width):                  kana = self.get_kana((x, y)) -                if kana.type_name in self.valid_types_for_chain: +                if kana.type_name in Kana.types_valid_for_chain:                      yield (x, y)      def populate_chain(self, pos1, chain_positions): @@ -123,7 +140,7 @@ class KanaGrid:              if pos2 in chain_positions:                  continue              kana2 = self.get_kana(pos2) -            if kana2.type_name in self.valid_types_for_chain: +            if kana2.type_name in Kana.types_valid_for_chain:                  if is_kana_compatible(kana1, kana2):                      self.populate_chain(pos2, chain_positions) @@ -172,25 +189,24 @@ class KanaGrid:      def swap_kana(self, pos1, pos2):          kana_dst = self.get_kana(pos2) -        if kana_dst.type_name == "froz": +        if kana_dst.type_name == 'froz':              pos_tmp = pos1              pos1 = pos2              pos2 = pos_tmp              kana_dst = self.get_kana(pos2) -        # important          kana_src = self.get_kana(pos1)          pos_src = pos1          pos_dst = pos2          vect = (pos2[0] - pos1[0], pos2[1] - pos1[1]) -        while is_swappable(kana_src, kana_dst): +        while self.is_swappable(pos_src, pos_dst):              #print("swap between src %s (%s) dst %s (%s)"              #      % (kana_src, pos_src, kana_dst, pos_dst))              self.set_kana(pos_src, kana_dst)              self.set_kana(pos_dst, kana_src) -            if kana_src.type_name != "froz": +            if kana_src.type_name != 'froz':                  break              pos_src = pos_dst @@ -226,20 +242,34 @@ class KanaGrid:  def repr_grid(grid, grid_size): +    indicator_map = { +            'ar_u': ('\x1b[30m∧\x1b[0m', '\x1b[30m∧\x1b[0m'), +            'ar_r': ('\x1b[30m>\x1b[0m', '\x1b[30m>\x1b[0m'), +            'ar_d': ('\x1b[30m∨\x1b[0m', '\x1b[30m∨\x1b[0m'), +            'ar_l': ('\x1b[30m<\x1b[0m', '\x1b[30m<\x1b[0m'), +            'froz': (' \x1b[36m', '\x1b[0m '), +            'rock': (' \x1b[1;40m', '\x1b[0m '), +            'myst': ('\x1b[33m?', '?\x1b[0m'), +    }      lines = []      kana_iter = iter(grid)      for y in range(grid_size[1]): -        line = "" +        line = ''          for x in range(grid_size[0]):              kana = next(kana_iter) -            if kana.type_name == "void": -                line += "      " -            elif kana.type_name == "empt": -                line += "|    |" -            elif kana.type_name == "myst": -                line += "| ?? |" -            elif kana.type_name in ("froz", "norm", "rock"): -                line += "| %s |" % kana.kana +            tname = kana.type_name +            if tname == 'void': +                line += '      ' +            elif tname == 'empt': +                line += '|    |' +            elif tname in indicator_map: +                line += '|%s%s%s|' % ( +                        indicator_map[tname][0], +                        kana.kana, +                        indicator_map[tname][1], +                ) +            elif kana.type_name in Kana.types_with_kana: +                line += '| %s |' % kana.kana          lines.append(line)      return '\n'.join(lines) diff --git a/tests_solver/level_test_ar_x.yaml b/tests_solver/level_test_ar_x.yaml new file mode 100644 index 0000000..633a843 --- /dev/null +++ b/tests_solver/level_test_ar_x.yaml @@ -0,0 +1,8 @@ +# kana quest grid level 1.1 +size: [3, 1] +max_actions: 1 +target_score: 2 +grid: [ +  [ar_r, su  ], [empt, null], [norm, so  ], +] +# The kana 'no' need to be protected else the program convert it into False... diff --git a/tests_solver/level_test_type.yaml b/tests_solver/level_test_type.yaml new file mode 100644 index 0000000..0eec70a --- /dev/null +++ b/tests_solver/level_test_type.yaml @@ -0,0 +1,9 @@ +# kana quest grid level 1.1 +size: [6, 2] +max_actions: 6 +target_score: 8 +grid: [ +  [ar_r, sa  ], [empt, null], [norm, so  ], [rock, se  ], [myst, ka  ], [ar_d, na  ], +  [ar_u, su  ], [ar_l, nu  ], [void, null], [void, null], [void, null], [froz, ke  ], +] +# The kana 'no' need to be protected else the program convert it into False... diff --git a/tests_solver/level_test_type_short.yaml b/tests_solver/level_test_type_short.yaml new file mode 100644 index 0000000..7fa3a9a --- /dev/null +++ b/tests_solver/level_test_type_short.yaml @@ -0,0 +1,9 @@ +# kana quest grid level 1.1 +size: [2, 2] +max_actions: 3 +target_score: 3 +grid: [ +  [myst, ka  ], [ar_d, ne  ], +  [void, null], [froz, ke  ], +] +# The kana 'no' need to be protected else the program convert it into False... diff --git a/tests_solver/test_solver.py b/tests_solver/test_solver.py index e2b45a5..82aca73 100644 --- a/tests_solver/test_solver.py +++ b/tests_solver/test_solver.py @@ -1,6 +1,7 @@  #!/usr/bin/env python3  import os +import pprint  import sys  sys.path.append(os.getcwd()) @@ -8,13 +9,29 @@ sys.path.append(os.getcwd())  from solver import *  def test_is_swappable(): -    assert is_swappable(Kana("norm", "su"), Kana("norm", "su")) -    assert is_swappable(Kana("froz", "su"), Kana("norm", "su")) -    assert is_swappable(Kana("norm", "su"), Kana("empt")) -    assert is_swappable(Kana("empt"), Kana("froz", "su")) -    assert not is_swappable(Kana("norm", "su"), Kana("rock", "su")) -    assert not is_swappable(Kana("froz", "su"), Kana("froz", "su")) -    assert not is_swappable(Kana("empt"), Kana("empt")) + +    swappable_grids = [ +            [Kana("norm", "su"), Kana("norm", "su")], +            [Kana("froz", "su"), Kana("norm", "su")], +            [Kana("norm", "su"), Kana("empt"      )], +            [Kana("empt"      ), Kana("froz", "su")], +    ] + +    for swappable_grid in swappable_grids: +        kanagrid = KanaGrid((2, 1), swappable_grid) +        print(kanagrid.grid) +        assert kanagrid.is_swappable((0, 0), (1, 0)) + +    not_swappable_grids = [ +            [Kana("norm", "su"), Kana("rock", "su")], +            [Kana("froz", "su"), Kana("froz", "su")], +            [Kana("empt"      ), Kana("empt"      )], +    ] + +    for not_swappable_grid in not_swappable_grids: +        kanagrid = KanaGrid((2, 1), not_swappable_grid) +        print(kanagrid.grid) +        assert not kanagrid.is_swappable((0, 0), (1, 0))  def test_kana_grid(): @@ -47,3 +64,113 @@ def test_kana_grid():      assert kanagrid_new.grid == expected_grid + +def test_kana_arrow_swap(): +    kanagrid_orig = KanaGrid((2, 1), [Kana("ar_r", "su"), Kana("empt",)]) +    kanagrid_new = kanagrid_orig.action(pos=(0, 0), action_type="right") +    expected_grid = KanaGrid((2, 1), [Kana("empt",), Kana("ar_r", "su")]) + +    print("kanagrid_orig") +    print(kanagrid_orig) +    print("kanagrid_new") +    print(kanagrid_new) +    print("expected_grid") +    print(expected_grid) + +    assert kanagrid_new.grid == expected_grid.grid + +def test_kana_arrow_swap(): +    moves = [ +        { +            'move_ok': 'up', +            'other_move_ok': 'down', +            'dest_pos': (1, 0), +            'orig': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("ar_u", "ko"), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ]), +            'moved': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("ar_u", "ko"), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ], action_count=1), +        }, +        { +            'move_ok': 'right', +            'other_move_ok': 'left', +            'dest_pos': (2, 1), +            'orig': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("ar_r", "ko"), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ]), +            'moved': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("ar_r", "ko"), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ], action_count=1), +        }, +        { +            'move_ok': 'down', +            'other_move_ok': 'up', +            'dest_pos': (1, 2), +            'orig': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("ar_d", "ko"), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ]), +            'moved': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("ar_d", "ko"), Kana("empt"      ), +            ], action_count=1), +        }, +        { +            'move_ok': 'left', +            'other_move_ok': 'right', +            'dest_pos': (0, 1), +            'orig': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("ar_l", "ko"), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ]), +            'moved': KanaGrid((3, 3), [ +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +                Kana("ar_l", "ko"), Kana("empt"      ), Kana("empt"      ), +                Kana("empt"      ), Kana("empt"      ), Kana("empt"      ), +            ], action_count=1), +        }, +    ] + +    for move in moves: +        print("pprint.pprint(move):") +        pprint.pprint(move) +        for action in ("up", "right", "down", "left"): +            print("\n"*3) +            print("action:", action) + +            # try to move arrowed kana +            kanagrid_test = move['orig'].copy() +            kanagrid_test = kanagrid_test.action((1, 1), action) +            print("=== move arrowed kana ===") +            print(kanagrid_test) +            if action == move['move_ok']: +                assert move['orig'].is_swappable((1, 1), move['dest_pos']) +                assert kanagrid_test == move['moved'] +            else: +                assert kanagrid_test is None + +            for other_pos in ((1, 0), (2, 1), (1, 2), (0, 1)): +                # try to move other kana with the arrowed one +                kanagrid_test = move['orig'].copy() +                kanagrid_test = kanagrid_test.action(other_pos, action) +                print("=== move other kana ===") +                print(kanagrid_test) +                if action == move['other_move_ok'] \ +                        and other_pos == move['dest_pos']: +                    assert move['orig'].is_swappable(other_pos, (1, 1)) +                    assert kanagrid_test == move['moved'] +                else: +                    assert kanagrid_test is None +  | 
