def is_one_hive(tiles: List[board.Tile]): tiles_count = len(tiles) main_hive = [] if len(tiles) <= 1: return True main_tile = tiles.pop(0) main_hive.append(main_tile) stop = False actual_tiles = [] while not stop: possible_tiles = hexutil.touching_hexagons(hexutil.Point(main_tile.x, main_tile.y)) # check for above piece possible_tiles.append(hexutil.Point(main_tile.x, main_tile.y)) for tile in possible_tiles: match = next((i for i in range(0, len(tiles)) if tiles[i].x == tile.x and tiles[i].y == tile.y), None) if match is not None: touching_tile = tiles.pop(match) actual_tiles.append(touching_tile) main_hive.append(touching_tile) if len(actual_tiles) != 0: main_tile = actual_tiles.pop(0) else: stop = True return tiles_count == len(main_hive)
def on_event(self, event): if event.type == pygame.QUIT: self._running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: pygame.quit() elif event.type == pygame.MOUSEBUTTONDOWN: clicked = pygame.mouse.get_pos() clicked = self.convert_to_play_area_coordinates( clicked[0], clicked[1]) if clicked[0] > 0 and clicked[1] > 0: self.on_play_area_click(event) else: print(piece) # run player click elif event.type == pygame.MOUSEBUTTONUP: self.drag = False self.last_coord = None elif event.type == pygame.MOUSEMOTION: if self.drag: place = pygame.mouse.get_pos() coord = hexutil.Point(place[0], place[1]) # don't need to convert so it can technically move offscreen correctly self.origin = hexutil.Point( self.origin.x + (coord.x - self.last_coord.x), self.origin.y + (coord.y - self.last_coord.y), ) self.last_coord = coord
def render_piece_in_play(self, surface, tile, scale): font = self.font() text = font.render(tile.piece.species, True, self.species_color_map[tile.piece.species]) offset = 0 if tile.z > 0: offset = 5 * tile.z text_rect: pygame.rect = text.get_rect() # set the center of the rectangular object. center = hexutil.hexagon_to_pixel(self.origin, hexutil.Point(tile.x, tile.y), scale) text_rect.center = (center.x + offset, center.y + offset) corners = hexutil.polygon_corners(self.origin, hexutil.Point(tile.x, tile.y), scale) corners = [hexutil.Point(i.x + offset, i.y + offset) for i in corners] pygame.draw.polygon(surface, ((50, 50, 50), (222, 220, 193))[tile.piece.color == 'W'], corners, 0) if tile.piece is self.selected_piece: pygame.draw.polygon(surface, (55, 250, 250), corners, scale // 8) else: pygame.draw.polygon(surface, ((90, 90, 90), (235, 233, 209))[tile.piece.color == 'W'], corners, scale // 12) surface.blit(text, text_rect)
def on_play_area_click(self, event): clicked = pygame.mouse.get_pos() clicked = self.convert_to_play_area_coordinates(clicked[0], clicked[1]) if event.button == 4: # prevents getting trapped at small scales self.scale = int(self.scale * 1.15) + 1 return elif event.button == 5: self.scale = max(int(self.scale * .85), 2) return elif event.button == 3: self.drag = True self.last_coord = hexutil.Point(clicked[0], clicked[1]) return elif event.button == 1: est = hexutil.pixel_to_closest_hexagon( self.origin, hexutil.Point(clicked[0], clicked[1]), self.scale) if est is not None: # todo: figure out z axis here if self.board.space_occupied(est.x, est.y): if self.selected_piece is not None: self.selected_piece = None return z = 0 while self.board.space_occupied(est.x, est.y, z) and z < 7: z = z + 1 self.selected_piece = self.board.piece_at( est.x, est.y, z - 1) else: self.selected_piece = None return
def test_can_slide_two(self): t = [ board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(2, 1, 0, piece.create_ladybug('W')) ] first = t.pop(0) self.assertTrue(hiveutil.can_slide_to(hexutil.Point(first.x, first.y), hexutil.Point(3, 0), t, first.z))
def test_can_not_hop_hopper_space(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(2, 1, 0, piece.create_ladybug('W')), board.Tile(3, 1, 0, piece.create_ladybug('W')), board.Tile(4, 0, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_hopper('B')) self.assertFalse(hiveutil.space_hopable(p, hexutil.Point(5, 0), t, hiveutil.generate_hive_movement_cloud(t, p))) self.assertTrue(hiveutil.space_hopable(p, hexutil.Point(3, 0), t, hiveutil.generate_hive_movement_cloud(t, p)))
def test_can_not_slide_ring(self): t = [ board.Tile(1, 1, 0, piece.create_ladybug('W')), board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(2, 1, 0, piece.create_ladybug('W')), board.Tile(2, 2, 0, piece.create_ladybug('W')), board.Tile(1, 2, 0, piece.create_ladybug('W')), board.Tile(0, 1, 0, piece.create_ladybug('W')), board.Tile(1, 0, 0, piece.create_ladybug('W')), ] first = t.pop(0) self.assertFalse(hiveutil.can_slide_to(hexutil.Point(first.x, first.y), hexutil.Point(3, 1), t, first.z))
def space_crawable(start: board.Tile, end: hexutil.Point, tiles: List[board.Tile], movement_cloud): # todo: need to be able to work in 3d space possible_paths = [deque()] # todo: add z axis to Move object possible_paths[0].append(Move(hexutil.Point(start.x, start.y), 0)) temp = tiles[:] if start in temp: temp.remove(start) current_path = possible_paths[0] spots = start.piece.crawl_spaces or 20 for i in range(1, spots + 1): paths_to_extend = possible_paths[:] p: deque[Move] for p in paths_to_extend: path_to_be_removed = p neighbors = hexutil.touching_hexagons(p[-1].position) for n in neighbors: # todo: add z axis in can_slide check if n in movement_cloud and can_slide_to(p[-1].position, n, temp, start.z): # not (dir != p[-1]dir and not p[-2].hex == n) if len(p) >= 2 and p[-2].position == n: continue elif n == end and start.piece.crawl_spaces is None: return True # todo: add in z axis when adding new move (axis of start z, cannot change) possible_paths.append(p + deque([Move(n, direction_of_crawl(p[-1].position, n, temp))])) # at end and cannot be moved to elif n == end: return False possible_paths.remove(path_to_be_removed) return any(move[-1].position == end for move in possible_paths)
def test_can_not_crawl_to_spider_back_and_forth(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_spider('W')) self.assertFalse(hiveutil.space_crawable(p, hexutil.Point(0, 1), t, hiveutil.generate_hive_movement_cloud(t, p)))
def test_can_crawl_to_spider(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_spider('W')) self.assertTrue(hiveutil.space_crawable(p, hexutil.Point(2, 0), t, hiveutil.generate_hive_movement_cloud(t, p)))
def test_valid_moves_single_ant(self): t = [ board.Tile(0, 0, 0, piece.create_ant('W')), board.Tile(1, 0, 0, piece.create_ladybug('W')), ] moves = hexutil.touching_hexagons(hexutil.Point(1, 0)) self.assertCountEqual(hiveutil.generate_valid_moves(t[0], t), moves)
def test_can_not_crawl_to_beetle_out_of_range(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_beetle('W')) self.assertFalse(hiveutil.space_crawable(p, hexutil.Point(1, 1), t, hiveutil.generate_hive_movement_cloud(t, p)))
def test_can_crawl_to_beetle_on_top(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), board.Tile(1, 1, 0, piece.create_ladybug('W')), ] p = board.Tile(1, 0, 1, piece.create_beetle('W')) self.assertTrue(hiveutil.space_crawable(p, hexutil.Point(1, 1), t, hiveutil.generate_hive_movement_cloud(t, p)))
def space_hopable(start: board.Tile, end: hexutil.Point, tiles: List[board.Tile], movement_cloud): # determine if start and end are in the same line x_dist = hexutil.relative_distance_x(hexutil.Point(start.x, start.y), end) y_dist = hexutil.relative_distance_y(hexutil.Point(start.x, start.y), end) angle = angle_of_vector(x_dist, y_dist) if angle % 60 != 0: return False # determine if there are spaces open in that line match = -1 space = start while end != space: n = hexutil.touching_hexagons(space)[int(angle / 60)] match = next((i for i in range(0, len(tiles)) if tiles[i].x == n.x and tiles[i].y == n.y), None) if match is None and end != n: return False space = n return True
def get_occupied_tiles(self): tiles = [] for p in self.play_space.items(): point = hexutil.Point(p[0][0], p[0][1]) if self.space_occupied(point.x, point.y, 0): for z in range(0, len(p[1])): tiles.append(Tile(point.x, point.y, z, self.piece_at(point.x, point.y, z))) tiles.sort(key=lambda tile: (tile.z, tile.x, tile.y)) return tiles
def render_player_space(self): line_start = self.convert_to_play_area_coordinates(0, self.height) pygame.draw.line(self._player_surface, (100, 100, 100), (line_start[0] - 1, 0), (line_start[0] - 1, self.height)) font = pygame.font.SysFont(None, 36) labels = [] tile: piece.Piece last_tile: piece.Piece = piece.Piece("", "") offset = 0 for tile in self.board.player_1.piece_reserve + self.board.player_2.piece_reserve: if tile.color == last_tile.color and tile.species == last_tile.species: offset += 5 labels.pop() else: offset = 0 text = font.render(tile.species, True, (255, 0, 0)) text_rect = text.get_rect() center = hexutil.hexagon_to_pixel( self.player_piece_layout[tile.color][tile.species], hexutil.Point(0, 0), self.player_piece_offset(1) * .9) text_rect.center = (center.x + offset, center.y + offset) labels.append((text, text_rect)) corners = hexutil.polygon_corners( self.player_piece_layout[tile.color][tile.species], hexutil.Point(0, 0), self.player_piece_offset(1) * .9) corners = [ hexutil.Point(i.x + offset, i.y + offset) for i in corners ] pygame.draw.polygon(self._player_surface, ((50, 50, 50), (255, 254, 242))[tile.color == 'W'], corners, 0) pygame.draw.polygon(self._player_surface, (0, 0, 0), corners, 2) last_tile = tile self._display_surface.blit(self._player_surface, (0, 0)) for label in labels: self._display_surface.blit(label[0], label[1])
def test_can_not_crawl_to_ant_gates(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(2, 1, 0, piece.create_ladybug('W')), board.Tile(2, 2, 0, piece.create_ladybug('W')), board.Tile(1, 2, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_ant('W')) self.assertFalse(hiveutil.space_crawable(p, hexutil.Point(1, 1), t, hiveutil.generate_hive_movement_cloud(t, p)))
def test_can_crawl_to_spider_strange_case(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(2, 1, 0, piece.create_ladybug('W')), board.Tile(2, 2, 0, piece.create_ladybug('W')), board.Tile(1, 3, 0, piece.create_ladybug('W')), board.Tile(0, 3, 0, piece.create_ladybug('W')), board.Tile(0, 2, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_spider('W')) cloud = hiveutil.generate_hive_movement_cloud(t, p) self.assertTrue(hiveutil.space_crawable(p , hexutil.Point(1, 2), t, cloud)) self.assertTrue(hiveutil.space_crawable(p, hexutil.Point(1, 1), t, cloud)) self.assertTrue(hiveutil.space_crawable(p, hexutil.Point(2, -1), t, cloud)) self.assertTrue(hiveutil.space_crawable(p, hexutil.Point(-1, 2), t, cloud))
def generate_hive_movement_cloud(tiles: List[board.Tile], mover): possible_spots = [] for tile in tiles: neighbors = hexutil.touching_hexagons(hexutil.Point(tile.x, tile.y)) for n in neighbors: if n in possible_spots: continue match = next((i for i in range(0, len(tiles)) if tiles[i].x == n.x and tiles[i].y == n.y and tiles[i].z == mover.z), None) # if tile not occupied if match is None: possible_spots.append(n) return possible_spots
def test_can_crawl_to_ant_gates_in_big_ring(self): t = [ board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(3, 0, 0, piece.create_ladybug('W')), board.Tile(4, 0, 0, piece.create_ladybug('W')), board.Tile(4, 1, 0, piece.create_ladybug('W')), board.Tile(4, 2, 0, piece.create_ladybug('W')), board.Tile(3, 3, 0, piece.create_ladybug('W')), board.Tile(2, 3, 0, piece.create_ladybug('W')), board.Tile(1, 3, 0, piece.create_ladybug('W')), board.Tile(1, 2, 0, piece.create_ladybug('W')) ] p = board.Tile(1, 0, 0, piece.create_ant('W')) self.assertTrue(hiveutil.space_crawable(p , hexutil.Point(3, 1), t, hiveutil.generate_hive_movement_cloud(t, p)))
def on_init(self): pygame.init() self._display_surface = pygame.display.set_mode( self.size, pygame.HWSURFACE | pygame.DOUBLEBUF) self._running = True self._sys_font = pygame.font.get_default_font() self._background = pygame.Surface(self._display_surface.get_size()) self._background.fill((219, 210, 127)) self._board_surface = pygame.Surface( (int(self.width * self.board_x_split_percentage), self.height)) self.origin = hexutil.Point( self.width * self.board_x_split_percentage // 2, self.height // 2) self._board_surface.fill((219, 210, 127)) self._player_surface = pygame.Surface( (self.width - int(self.width * self.board_x_split_percentage), self.height)) self._player_surface.fill((200, 200, 200)) return True
def test_movement_cloud_one_piece(self): t = [ board.Tile(1, 1, 0, piece.create_ladybug('W')) ] cloud = [ hexutil.Point(2, 0), hexutil.Point(2, 1), hexutil.Point(2, 2), hexutil.Point(1, 2), hexutil.Point(0, 1), hexutil.Point(1, 0), ] self.assertCountEqual(hiveutil.generate_hive_movement_cloud(t, t[0]), cloud)
def can_start_hop(start: hexutil.Point, tiles: List[board.Tile], z): neighbors = hexutil.touching_hexagons(hexutil.Point(start.x, start.y)) touches = 0 max_continuous_touches = 0 continuous_touches = 0 for n in neighbors: match = next((i for i in range(0, len(tiles)) if tiles[i].x == n.x and tiles[i].y == n.y and tiles[i].z == z), None) if match is not None: touches += 1 continuous_touches += 1 max_continuous_touches = max(max_continuous_touches, continuous_touches) else: continuous_touches = 0 if touches == max_continuous_touches: return True else: return False
def test_movement_cloud_six_piece_ring(self): t = [ board.Tile(2, 0, 0, piece.create_ladybug('W')), board.Tile(2, 1, 0, piece.create_ladybug('W')), board.Tile(2, 2, 0, piece.create_ladybug('W')), board.Tile(1, 2, 0, piece.create_ladybug('W')), board.Tile(0, 1, 0, piece.create_ladybug('W')), board.Tile(1, 0, 0, piece.create_ladybug('W')), ] cloud = [ hexutil.Point(1, 1), hexutil.Point(3, 1), hexutil.Point(3, 2), hexutil.Point(2, 3), hexutil.Point(1, 3), hexutil.Point(0, 3), hexutil.Point(0, 2), hexutil.Point(-1, 1), hexutil.Point(0, 0), hexutil.Point(0, -1), hexutil.Point(1, -1), hexutil.Point(2, -1), hexutil.Point(3, 0), ] self.assertCountEqual(hiveutil.generate_hive_movement_cloud(t, t[0]), cloud)
def test_can_not_hop_hopper_simple_off_direction(self): t = [ board.Tile(1, 0, 0, piece.create_ladybug('W')), ] p = board.Tile(0, 0, 0, piece.create_hopper('B')) self.assertFalse(hiveutil.space_hopable(p, hexutil.Point(1, 1), t, hiveutil.generate_hive_movement_cloud(t, p)))
def __init__(self): self._running = True self._display_surface = None self._sys_font = None self._background = None self._board_surface = None self.board_x_split_percentage = 0.8 self._player_surface = None self.board = board.Board() self.size = self.width, self.height = 900, 600 self.origin = hexutil.Point(self.width // 2, self.height // 2) self.scale = 50 self.selected_piece = None self.drag = False self.last_coord = None self.player_piece_layout = dict( W={ 'Q': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(1)), 'A': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(2)), 'S': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(3)), 'H': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(4)), 'B': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(5)), 'M': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(6)), 'L': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(7)), 'P': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(8)) }, B={ 'Q': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(9)), 'A': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(10)), 'S': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(11)), 'H': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(12)), 'B': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(13)), 'M': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(14)), 'L': hexutil.Point(self.player_piece_offset(1), self.player_piece_offset(15)), 'P': hexutil.Point( self.player_piece_offset(1) * 3, self.player_piece_offset(16)) }) self.species_color_map = { "A": (17, 99, 207), "B": (182, 20, 196), "H": (45, 153, 41), "L": (179, 13, 7), "M": (156, 156, 156), "P": (86, 219, 184), "Q": (227, 219, 59), "S": (115, 85, 46), }