def test_permute_simple(): board = Board(5, 3) setting_data = ("ABCDE", "FGHIJ", "KLMNO") board.set_data_from_string(setting_data) tile_with_c = board[2, 0] tile_with_n = board[3, 2] board.circular_permute_tiles([Pos(2, 0), Pos(3, 2)]) print(board.render()) assert tile_with_c.x == 3 assert tile_with_c.y == 2 assert tile_with_c.data == "C" assert board[3, 2].data == "C" assert tile_with_n.x == 2 assert tile_with_n.y == 0 assert tile_with_n.data == "N" assert board[2, 0].data == "N" render_result = """ ABNDE FGHIJ KLMCO """ assert strip_multiline(board.render()) == strip_multiline(render_result)
def circular_permute_tiles(self, positions): """ positions est un itérable. """ made_first_iteration = False for pos in positions: if made_first_iteration: cur_pos = pos cur_tile = self._tiles[cur_pos.y][cur_pos.x] cur_tile.pos = Pos(prev_pos) cur_tile.x = prev_pos.x cur_tile.y = prev_pos.y self._tiles[prev_pos.y][prev_pos.x] = cur_tile prev_pos = cur_pos else: first_pos = pos first_tile = self._tiles[first_pos.y][first_pos.x] prev_pos = first_pos made_first_iteration = True first_tile.pos = Pos(pos) first_tile.x = pos.x first_tile.y = pos.y self._tiles[pos.y][pos.x] = first_tile
def test_adj_default(): board_adj_default_cross = Board(10, 10) p1 = Pos(5, 5) p2 = Pos(6, 5) p3 = Pos(4, 4) # cross: assert board_adj_default_cross.is_adjacent(p1, p2) == True # diag : assert board_adj_default_cross.is_adjacent(p1, p3) == False set_default_adjacency(AdjacencyEvaluatorCross) board_adj_default_cross_2 = Board(10, 10) # cross: assert board_adj_default_cross_2.is_adjacent(p1, p2) == True # diag : assert board_adj_default_cross_2.is_adjacent(p1, p3) == False set_default_adjacency(AdjacencyEvaluatorCrossDiag) board_adj_default_cross_diag = Board(10, 10) # cross: assert board_adj_default_cross_diag.is_adjacent(p1, p2) == True # diag : assert board_adj_default_cross_diag.is_adjacent(p1, p3) == True
def test_getitem_fail(): board = Board(5, 14) failed_at_failing = False try: a = board[0, 14] failed_at_failing = True except BoardIndexError as e: print(e) try: p = Pos(5, 0) a = board[p] failed_at_failing = True except BoardIndexError as e: print(e) try: a = board[0, -15] failed_at_failing = True except BoardIndexError as e: print(e) try: p = Pos(-6, 0) a = board[p] failed_at_failing = True except BoardIndexError as e: print(e) assert failed_at_failing == False
def test_eq(): p_1 = Pos(34, 78) p_2 = Pos(35, 78) p_3 = Pos(34, 77) p_4 = Pos(34, 78) p_5 = Pos(0, 0) assert p_1 == p_1 assert p_1 != p_2 assert p_1 != p_3 assert p_1 == p_4 assert p_1 != p_5
def __init__(self, board, pos_start, pos_end, pass_through_condition=propag_cond_default): # FUTURE : pathfinding avec tous les shortest paths possibles. # pathfinding avec tous les paths possibles super().__init__(board) self.pass_through_condition = pass_through_condition pos_start = Pos(pos_start) pos_end = Pos(pos_end) self.pos_start = pos_start self.pos_end = pos_end iter_propag = BoardIteratorPropagation(self.board, self.pos_start, pass_through_condition) try: while pos_end not in iter_propag.propagated_poss: next(iter_propag) except StopIteration: self.path = None return propagated_poss = iter_propag.propagated_poss # Et maintenant, on parcourt la propagation à l'envers, # pour retrouver le chemin. pos_cur = pos_end dist_cur = propagated_poss[pos_cur] self.path = [pos_cur] while pos_cur != pos_start: advanced = False for adj_pos in self.board.adjacency.adjacent_positions(pos_cur): if (propagated_poss.get(adj_pos, -2) == dist_cur - 1) and pass_through_condition( self.board[adj_pos], self.board[pos_cur]): pos_cur = adj_pos dist_cur -= 1 self.path.append(pos_cur) advanced = True break if not advanced: raise Exception( "No adj pos with dist-1. Not supposed to happen")
def test_replace_simple(): board = Board(5, 3) setting_data = ("ABCDE", "FGHIJ", "KLMNO") board.set_data_from_string(setting_data) new_tile = Tile() new_tile.data = "Z" board.replace_tile(new_tile, Pos(3, 1)) print(board.render()) assert new_tile.x == 3 assert new_tile.y == 1 assert board[3, 1].data == "Z" render_result = """ ABCDE FGHZJ KLMNO """ assert strip_multiline(board.render()) == strip_multiline(render_result) print(board.render())
def test_permute_column(): board = Board(5, 7) setting_data = ("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY", "01234", "56789") board.set_data_from_string(setting_data) pos_to_permute = [Pos(tile.x, tile.y) for tile in board[2, :]] assert len(pos_to_permute) == 7 board.circular_permute_tiles(pos_to_permute) print(board.render()) render_result = """ ABHDE FGMIJ KLRNO PQWST UV2XY 01734 56C89 """ assert strip_multiline(board.render()) == strip_multiline(render_result) # Pour vérifier que la fonction de permutation ne vide pas la liste. # Ça le faisait avant, et c'était mal. assert len(pos_to_permute) == 7
def test_adj_diag(): simple_board = Board(10, 10, class_adjacency=AdjacencyEvaluatorCrossDiag) p1 = Pos(5, 5) p2 = Pos(6, 5) p3 = Pos(4, 4) # same : assert simple_board.is_adjacent(p1, p1) == False # cross : assert simple_board.is_adjacent(p1, p2) == True # diag : assert simple_board.is_adjacent(p1, p3) == True # none : assert simple_board.is_adjacent(p2, p3) == False
def __next__(self): while self.nb_sub_coord_to_skip: self._apply_skip_sub_coord() self.nb_sub_coord_to_skip -= 1 self._update_col_line_modification(True) try: val_coord_main = next(self.iter_main) must_change_sub = False except StopIteration: # Faut repartir à la "ligne" suivante. must_change_sub = True self._update_col_line_modification(must_change_sub) if must_change_sub: self._apply_skip_sub_coord() val_coord_main = next(self.iter_main) self._update_col_line_modification(True) if self.id_coord_main == Coord.X: x = val_coord_main y = self.val_coord_sub else: x = self.val_coord_sub y = val_coord_main new_pos = Pos(x, y) self._update_indicators(new_pos) return self.board.get_tile(self.current_pos)
def __init__(self): board = [] for x in range(0, 8): row = [Pos(x, y) for y in range(0, 8)] board.append(tuple(row)) self.board = tuple(board)
def test_from_obj(): class Whatever: def __init__(self): self.x = 23.9 self.y = 45.8 p = Pos(Whatever()) assert p.x == 23 and p.y == 45
def getPlayerPositions(self): poss = [] for r in range(1, 6): for c in range(1, 6): pos = Pos(r, c) if self.board.hasPiece(pos, self.playerPiece): poss.append(pos) return poss
def test_getitem_pos(): board = Board(3, 3) p = Pos(1, 0) board[p].data = "|" board[Pos(0, -2)].data = "-" board[Pos(-2, 1)].data = "*" board[Pos(-1, -2)].data = "~" board[{"x": 1, "y": 2}].data = "I" render_result = """ .|. -*~ .I. """ print(board.render()) assert strip_multiline(board.render()) == strip_multiline(render_result)
def __init__(self, board, pos_start, propag_condition=propag_cond_default): # TODO : avec plusieurs pos_start. super().__init__(board) self.propag_condition = propag_condition # Dict # - clé : la pos propagée. # - valeur : la distance depuis la pos de départ jusqu'à la pos propagée. self.propagated_poss = {} # liste de tuple de 2 éléments : la distance et la pos propagée. self.to_propagate_poss = [(0, Pos(pos_start))]
def __next__(self): self.current_posis_index += 1 if self.current_posis_index >= len(self.posis): raise StopIteration new_pos = Pos(self.posis[self.current_posis_index]) self._update_indicators(new_pos) return self.board.get_tile(self.current_pos)
def __getitem__(self, args): # FUTURE : on a le droit de faire du *args, **kwargs avec getitem ? # Et ça donne quoi si on le fait ? À tester. if not args: return BoardIteratorRect(self) try: pos = Pos(args) except ValueError: pos = None if pos is not None: # Mode un seul élément return self._get_tile(pos.x, pos.y) slice_x = None slice_y = None id_coord_main = Coord.X try: iter_on_args = iter(args) slice_x = next(iter_on_args) slice_y = next(iter_on_args) id_coord_main = next(iter_on_args) except TypeError: slice_x = args except StopIteration: pass if (slice_x is None or slice_y is None or isinstance(slice_x, slice) or isinstance(slice_y, slice)): # Mode itération if slice_x is None: slice_x = slice(None, None, None) if isinstance(slice_x, int): slice_x = slice(slice_x, slice_x + 1, None) if slice_y is None: slice_y = slice(None, None, None) if isinstance(slice_y, int): slice_y = slice(slice_y, slice_y + 1, None) dict_coord_from_str = {"X": Coord.X, "Y": Coord.Y} if isinstance(id_coord_main, str): id_coord_main = id_coord_main.upper() if id_coord_main in dict_coord_from_str: id_coord_main = dict_coord_from_str[id_coord_main] return BoardIteratorRect(self, slice_x, slice_y, id_coord_main) # Mode fail raise Exception("TODO fail get item" + "".join(args))
def __init__(self, x=None, y=None, board_owner=None): # TODO : il faut accepter le même bazar de param que pour l'objet Pos. Ou pas. self.x = x self.y = y # TODO : est-ce qu'on autorise des tiles sans coord, qui "flotte un peu dans les airs", ou pas ? try: self.pos = Pos(x, y) except: self.pos = None self.board_owner = board_owner self.data = "." self.mobile_items = []
def adjacent_positions(self, pos): # TODO : faudrait renvoyer des tiles ou des positions ? # Il est conseillé de mettre dans le même ordre que l'ordre des Direction. # C'est à dire dans le sens des aiguilles d'une montre. # (Mais ce n'est pas tout le temps possible avec des fonctions d'adjacences tordues) offsets = [(0, -1), (+1, 0), (0, +1), (-1, 0)] for offset in offsets: x = pos.x + offset[0] y = pos.y + offset[1] # TODO : le check de inbounds devrait être dans la classe board, tellement c'est un truc basique. if (0 <= x < self.board.w) and (0 <= y < self.board.h): yield Pos(x, y)
def move(self, board_owner=None, tile_owner=None, z_index=None, *args, **kwargs): """ Param prioritaire : tile_owner. Sinon : les autres params. """ # FUTURE : j'ai plein de fonctions qui crée une pos à partir de args et kwargs. # Y'aurait peut-être moyen de le factoriser avec un décorateur. if self.tile_owner is not None: # --- suppression du mobitem de la tile où il était avant --- # Si self n'est pas mobile_items, ça va raiser une exception. # C'est ce qu'on veut, parce que not supposed to happen. index_myself = self.tile_owner.mobile_items.index(self) del self.tile_owner.mobile_items[index_myself] # --- définition éventuelle de board_owner, à partir de l'actuel board_owner --- if board_owner is None: board_owner = self.tile_owner.board_owner # --- définition éventuelle de board_owner, à partir du nouveau tile_owner --- if tile_owner is not None: board_owner = tile_owner.board_owner # --- définition éventuelle de tile_owner, à partir de board_owner et des param de pos --- if tile_owner is None and board_owner is not None: try: pos = Pos(*args, **kwargs) tile_owner = board_owner[pos] except: tile_owner = None # --- Enregistrement dans le nouveau tile_owner, si défini --- if tile_owner is not None: self.tile_owner = tile_owner if z_index is None: tile_owner.mobile_items.append(self) else: tile_owner.mobile_items.insert(z_index, self)
def test_coord_outing(): p = Pos(23.9, 45.8) t = p.as_tuple() assert t[0] == 23 and t[1] == 45 d = p.as_dict() assert d["x"] == 23 and d["y"] == 45
def test_from_list(): p = Pos([23.9, 45.8]) assert p.x == 23 and p.y == 45
def test_from_dict(): p = Pos({"x": 23.9, "y": 45.8}) assert p.x == 23 and p.y == 45
def test_directions_computing(): center = Pos(4, 7) assert compute_direction(center, Pos(4, 5)) == Dir.UP assert compute_direction(center, Pos(5, 5)) == Dir.UP_RIGHT assert compute_direction(center, Pos(6, 5)) == Dir.UP_RIGHT assert compute_direction(center, Pos(6, 6)) == Dir.UP_RIGHT assert compute_direction(center, Pos(6, 7)) == Dir.RIGHT assert compute_direction(center, Pos(6, 8)) == Dir.DOWN_RIGHT assert compute_direction(center, Pos(6, 9)) == Dir.DOWN_RIGHT assert compute_direction(center, Pos(5, 9)) == Dir.DOWN_RIGHT assert compute_direction(center, Pos(4, 9)) == Dir.DOWN assert compute_direction(center, Pos(3, 9)) == Dir.DOWN_LEFT assert compute_direction(center, Pos(2, 9)) == Dir.DOWN_LEFT assert compute_direction(center, Pos(2, 8)) == Dir.DOWN_LEFT assert compute_direction(center, Pos(2, 7)) == Dir.LEFT assert compute_direction(center, Pos(2, 6)) == Dir.UP_LEFT assert compute_direction(center, Pos(2, 5)) == Dir.UP_LEFT assert compute_direction(center, Pos(3, 5)) == Dir.UP_LEFT
def test_from_iterable(): p = Pos(range(10, 40, 7)) assert p.x == 10 and p.y == 17
def test_from_param(): p = Pos(23.9, 45.8) assert p.x == 23 and p.y == 45
def test_from_param_xy(): p = Pos(x=23.9, y=45.8) assert p.x == 23 and p.y == 45
def test_from_pos(): p_1 = Pos(x=23.9, y=45.8) p_2 = Pos(p_1) p_1.x = 0 p_1.y = 1 assert p_2.x == 23 and p_2.y == 45
def test_push_cols_lines(): """ Test de déplacement de toutes les tiles d'une ligne ou d'une colonne, en ajoutant une nouvelle tile qui va pousser les autres. Comme dans le jeu de plateau 'Labyrinthe', et dans le challenge CodinGame 'Xmas Rush' """ # PUSH 3 RIGHT board = Board(5, 7) setting_data = ("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY", "01234", "56789") board.set_data_from_string(setting_data) added_tile = Tile() added_tile.data = "#" pos_to_permute = [Pos(tile.x, tile.y) for tile in board[::-1, 3]] board.circular_permute_tiles(pos_to_permute) removed_tile = board[0, 3] board.replace_tile(added_tile, Pos(0, 3)) print(board.render()) assert removed_tile.data == "T" render_result = """ ABCDE FGHIJ KLMNO #PQRS UVWXY 01234 56789 """ assert strip_multiline(board.render()) == strip_multiline(render_result) print("") # PUSH 0 LEFT board = Board(5, 7) setting_data = ("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY", "01234", "56789") board.set_data_from_string(setting_data) added_tile = Tile() added_tile.data = "#" pos_to_permute = [Pos(tile.x, tile.y) for tile in board[:, 0]] board.circular_permute_tiles(pos_to_permute) removed_tile = board[4, 0] board.replace_tile(added_tile, Pos(4, 0)) print(board.render()) assert removed_tile.data == "A" render_result = """ BCDE# FGHIJ KLMNO PQRST UVWXY 01234 56789 """ assert strip_multiline(board.render()) == strip_multiline(render_result) print("") # PUSH 4 DOWN board = Board(5, 7) setting_data = ("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY", "01234", "56789") board.set_data_from_string(setting_data) added_tile = Tile() added_tile.data = "#" pos_to_permute = [Pos(tile.x, tile.y) for tile in board[4, ::-1]] board.circular_permute_tiles(pos_to_permute) removed_tile = board[4, 0] board.replace_tile(added_tile, Pos(4, 0)) print(board.render()) assert removed_tile.data == "9" render_result = """ ABCD# FGHIE KLMNJ PQRSO UVWXT 0123Y 56784 """ assert strip_multiline(board.render()) == strip_multiline(render_result) print("") # PUSH 1 UP board = Board(5, 7) setting_data = ("ABCDE", "FGHIJ", "KLMNO", "PQRST", "UVWXY", "01234", "56789") board.set_data_from_string(setting_data) added_tile = Tile() added_tile.data = "#" pos_to_permute = [Pos(tile.x, tile.y) for tile in board[1, :]] board.circular_permute_tiles(pos_to_permute) removed_tile = board[1, board.h - 1] board.replace_tile(added_tile, Pos(1, board.h - 1)) print(board.render()) assert removed_tile.data == "B" render_result = """ AGCDE FLHIJ KQMNO PVRST U1WXY 06234 5#789 """ assert strip_multiline(board.render()) == strip_multiline(render_result) print("")
def test_str(): p = Pos(23.9, 45.8) assert str(p) == "<Pos 23, 45 >"