def opposite_edge(cls, road_position: CoordinateWithSide): if road_position.side == Side.TOP: return CoordinateWithSide( Coordinate(road_position.coordinate.row - 1, road_position.coordinate.column), Side.BOTTOM) elif road_position.side == Side.RIGHT: return CoordinateWithSide( Coordinate(road_position.coordinate.row, road_position.coordinate.column + 1), Side.LEFT) elif road_position.side == Side.BOTTOM: return CoordinateWithSide( Coordinate(road_position.coordinate.row + 1, road_position.coordinate.column), Side.TOP) elif road_position.side == Side.LEFT: return CoordinateWithSide( Coordinate(road_position.coordinate.row, road_position.coordinate.column - 1), Side.RIGHT)
def find_meeples(cls, game_state: CarcassonneGameState, farm: Farm) -> [[MeeplePosition]]: meeples: [[MeeplePosition]] = [[] for _ in range(game_state.players)] farmer_connection_with_coordinate: FarmerConnectionWithCoordinate for farmer_connection_with_coordinate in farm.farmer_connections_with_coordinate: farmer_position: CoordinateWithSide = CoordinateWithSide(farmer_connection_with_coordinate.coordinate, farmer_connection_with_coordinate.farmer_connection.farmer_positions[0]) for player in range(game_state.players): meeple_position: MeeplePosition for meeple_position in game_state.placed_meeples[player]: if farmer_position == meeple_position.coordinate_with_side: meeples[player].append(meeple_position) return meeples
def outgoing_roads_for_position( cls, game_state: CarcassonneGameState, road_position: CoordinateWithSide) -> [CoordinateWithSide]: tile: Tile = game_state.get_tile(road_position.coordinate.row, road_position.coordinate.column) if tile is None: return [] roads: [CoordinateWithSide] = [] connection: Connection for connection in tile.road: if connection.a == road_position.side or connection.b == road_position.side: if connection.a != Side.CENTER: roads.append( CoordinateWithSide(coordinate=road_position.coordinate, side=connection.a)) if connection.b != Side.CENTER: roads.append( CoordinateWithSide(coordinate=road_position.coordinate, side=connection.b)) return roads
def apply_action2(cls, game_state: CarcassonneGameState, a: int) -> CarcassonneGameState: new_game_state: CarcassonneGameState = copy.deepcopy(game_state) action = Action.game_phase(a) if action is not game_state.phase: raise Exception("invalid action") if action == GamePhase.TILES: new_game_state.phase = GamePhase.ROTATION # skip TYPE column = a % board_size row = int((a - column) / board_size) new_game_state.position = (row, column) new_game_state.next_tile_action = TileAction( new_game_state.next_tile, Coordinate(row, column), 0) if action == GamePhase.TYPE: # skip -> not needed because it is inferred by the previous drawn card new_game_state.phase = GamePhase.ROTATION pass if action == GamePhase.ROTATION: new_game_state.phase = GamePhase.MEEPLES rotation = a - ((board_size * board_size) + tile_types) new_game_state.next_tile_action.tile_rotations = rotation cls.play_tile(game_state=new_game_state, tile_action=new_game_state.next_tile_action) if action == GamePhase.MEEPLES: pos = a - ((board_size * board_size) + tile_types + rotations) # pos 5 = place no meeple if pos != 5: coordinate_with_side = CoordinateWithSide( new_game_state.next_tile_action.coordinate, Side.from_number(pos)) meeple = MeepleAction(MeepleType.NORMAL, coordinate_with_side) cls.play_meeple(game_state=new_game_state, meeple_action=meeple) cls.remove_meeples_and_update_score(game_state=new_game_state) cls.draw_tile(game_state=new_game_state) # tiles phase set by next_player cls.next_player(game_state=new_game_state) if new_game_state.is_terminated(): PointsCollector.count_final_scores(game_state=new_game_state) return new_game_state
def cities_for_position(cls, game_state: CarcassonneGameState, city_position: CoordinateWithSide): try: tile: Tile = game_state.board[city_position.coordinate.row][ city_position.coordinate.column] except IndexError: tile = None cities = [] if tile is None: return cities for city_group in tile.city: if city_position.side in city_group: city_group_side: Side for city_group_side in city_group: city_position: CoordinateWithSide = CoordinateWithSide( city_position.coordinate, city_group_side) cities.append(city_position) return cities
def find_roads(cls, game_state: CarcassonneGameState, coordinate: Coordinate): roads: Set[Road] = set() tile: Tile = game_state.board[coordinate.row][coordinate.column] if tile is None: return roads side: Side for side in [Side.TOP, Side.RIGHT, Side.BOTTOM, Side.LEFT]: if tile.get_type(side) == TerrainType.ROAD: road: Road = cls.find_road(game_state=game_state, road_position=CoordinateWithSide( coordinate=coordinate, side=side)) roads.add(road) return list(roads)
def find_cities(cls, game_state: CarcassonneGameState, coordinate: Coordinate, sides: [Side] = (Side.TOP, Side.RIGHT, Side.BOTTOM, Side.LEFT)): cities: Set[City] = set() tile: Tile = game_state.board[coordinate.row][coordinate.column] if tile is None: return cities side: Side for side in sides: if tile.get_type(side) == TerrainType.CITY: city: City = cls.find_city(game_state=game_state, city_position=CoordinateWithSide( coordinate=coordinate, side=side)) cities.add(city) return list(cities)
def __possible_farmer_position( cls, game_state: CarcassonneGameState) -> [CoordinateWithSide]: playing_positions: [CoordinateWithSide] = [] last_tile_action: TileAction = game_state.last_tile_action last_played_tile: Tile = last_tile_action.tile last_played_position: Coordinate = last_tile_action.coordinate farmer_connection: FarmerConnection for farmer_connection in last_played_tile.farms: farm: Farm = FarmUtil.find_farm(game_state=game_state, farmer_connection_with_coordinate= FarmerConnectionWithCoordinate( farmer_connection, last_played_position)) if FarmUtil.has_meeples(game_state, farm): continue else: farmer_position: Side = farmer_connection.farmer_positions[0] playing_positions.append( CoordinateWithSide(last_played_position, farmer_position)) return playing_positions
def __possible_meeple_positions( game_state: CarcassonneGameState) -> [CoordinateWithSide]: playing_positions: [CoordinateWithSide] = [] last_tile_action: TileAction = game_state.last_tile_action last_played_tile: Tile = last_tile_action.tile last_played_position: Coordinate = last_tile_action.coordinate if last_played_tile.chapel: playing_positions.append( CoordinateWithSide(coordinate=last_played_position, side=Side.CENTER)) if last_played_tile.flowers \ and SupplementaryRule.NORMAL_MEEPLES_CAN_USE_FLOWERS in game_state.supplementary_rules: playing_positions.append( CoordinateWithSide(coordinate=last_played_position, side=Side.CENTER)) for side in [Side.TOP, Side.RIGHT, Side.BOTTOM, Side.LEFT]: if last_played_tile.get_type(side) == TerrainType.CITY: connected_cities = CityUtil.find_city( game_state, CoordinateWithSide(coordinate=last_played_position, side=side)) if CityUtil.city_contains_meeples(game_state, connected_cities): continue else: playing_positions.append( CoordinateWithSide(coordinate=last_played_position, side=side)) if last_played_tile.get_type(side) == TerrainType.ROAD: connected_roads = RoadUtil.find_road( game_state, CoordinateWithSide(coordinate=last_played_position, side=side)) if RoadUtil.road_contains_meeples(game_state, connected_roads): continue else: playing_positions.append( CoordinateWithSide(coordinate=last_played_position, side=side)) return playing_positions
def remove_meeples_and_collect_points(cls, game_state: CarcassonneGameState, coordinate: Coordinate): # Points for finished cities cities: [City] = CityUtil.find_cities(game_state=game_state, coordinate=coordinate) for city in cities: if city.finished: meeples: [[MeeplePosition]] = CityUtil.find_meeples(game_state=game_state, city=city) meeple_counts_per_player = cls.get_meeple_counts_per_player(meeples) # print("City finished. Meeples:", json.dumps(meeple_counts_per_player)) if sum(meeple_counts_per_player) == 0: continue winning_player = cls.get_winning_player(meeple_counts_per_player) if winning_player is not None: points = cls.count_city_points(game_state=game_state, city=city) # print(points, "points for player", winning_player) game_state.scores[winning_player] += points MeepleUtil.remove_meeples(game_state=game_state, meeples=meeples) # Points for finished roads roads: [Road] = RoadUtil.find_roads(game_state=game_state, coordinate=coordinate) for road in roads: if road.finished: meeples: [[MeeplePosition]] = RoadUtil.find_meeples(game_state=game_state, road=road) meeple_counts_per_player = cls.get_meeple_counts_per_player(meeples) # print("Road finished. Meeples:", json.dumps(meeple_counts_per_player)) if sum(meeple_counts_per_player) == 0: continue winning_player = cls.get_winning_player(meeple_counts_per_player) if winning_player is not None: points = cls.count_road_points(game_state=game_state, road=road) # print(points, "points for player", winning_player) game_state.scores[winning_player] += points MeepleUtil.remove_meeples(game_state=game_state, meeples=meeples) # Points for finished chapels for row in range(coordinate.row - 1, coordinate.row + 2): for column in range(coordinate.column - 1, coordinate.column + 2): tile: Tile = game_state.get_tile(row, column) if tile is None: continue coordinate = Coordinate(row=row, column=column) coordinate_with_side = CoordinateWithSide(coordinate=coordinate, side=Side.CENTER) meeple_of_player = MeepleUtil.position_contains_meeple(game_state=game_state, coordinate_with_side=coordinate_with_side) if (tile.chapel or tile.flowers) and meeple_of_player is not None: points = cls.chapel_or_flowers_points(game_state=game_state, coordinate=coordinate) if points == 9: # print("Chapel or flowers finished for player", str(meeple_of_player)) # print(points, "points for player", meeple_of_player) game_state.scores[meeple_of_player] += points meeples_per_player = [] for _ in range(game_state.players): meeples_per_player.append([]) for meeple_position in game_state.placed_meeples[meeple_of_player]: if coordinate_with_side == meeple_position.coordinate_with_side: meeples_per_player[meeple_of_player].append(meeple_position) MeepleUtil.remove_meeples(game_state=game_state, meeples=meeples_per_player)
def possible_meeple_actions( cls, game_state: CarcassonneGameState) -> [MeepleAction]: current_player = game_state.current_player last_tile_action: TileAction = game_state.last_tile_action last_played_tile: Tile = last_tile_action.tile last_played_position: Coordinate = last_tile_action.coordinate possible_actions: [MeepleAction] = [] meeple_positions = cls.__possible_meeple_positions( game_state=game_state) if SupplementaryRule.FARMERS in game_state.supplementary_rules: farmer_positions = cls.__possible_farmer_position( game_state=game_state) else: farmer_positions = () if game_state.meeples[current_player] > 0: possible_actions.extend( list( map( lambda x: MeepleAction(meeple_type=MeepleType.NORMAL, coordinate_with_side=x), meeple_positions))) possible_actions.extend( list( map( lambda x: MeepleAction(meeple_type=MeepleType.FARMER, coordinate_with_side=x), farmer_positions))) if game_state.big_meeples[current_player] > 0: possible_actions.extend( list( map( lambda x: MeepleAction(meeple_type=MeepleType.BIG, coordinate_with_side=x), meeple_positions))) possible_actions.extend( list( map( lambda x: MeepleAction( meeple_type=MeepleType.BIG_FARMER, coordinate_with_side=x), farmer_positions))) if game_state.abbots[current_player] > 0: if last_played_tile.chapel or last_played_tile.flowers: possible_actions.append( MeepleAction(meeple_type=MeepleType.ABBOT, coordinate_with_side=CoordinateWithSide( coordinate=last_played_position, side=Side.CENTER))) placed_meeple: MeeplePosition for placed_meeple in game_state.placed_meeples[current_player]: if placed_meeple.meeple_type == MeepleType.ABBOT: possible_actions.append( MeepleAction(meeple_type=MeepleType.ABBOT, coordinate_with_side=placed_meeple. coordinate_with_side, remove=True)) return possible_actions