コード例 #1
0
class Game:
    # Fields
    # players: list of Players in the game
    # board: Board object
    # bank: Bank object
    # other game vars as needed

    # STATE:
    # Active Player
    # Roll: (0 at start of turn)
    # Board
    # Resource, roll of each tile
    # Status of each path and plot of land
    # Position of Robber
    # Bank
    # Number of each card available
    # Number of dev cards in deck
    # Players
    # Number of cards in hand
    # Number of pieces remaining
    # Number of VPs
    # Game states
    # START_TURN, player_id
    # Actions: ROLL (IN_TURN if not 7, SEVEN if players need to give away cards, otherwise ROB_TILE)
    # IN_TURN, player_id
    # Actions: BUILD structure,
    #          TRADE p1_id, p1_giving_hand, p2_id, p2_giving_hand
    #          PLAY_DEV card, SEA_TRADE ..., PASS
    # BUILDING, player_id, structure
    # Actions: LOCATION location_id, CANCEL
    # Options: list of location_ids
    # PLAYING_DEV_CARD, player_id
    # Monopoly: Actions: RESOURCE resource, CANCEL (to IN_TURN)
    # Year of plenty Actions: RESOURCE resource1, resource2, CANCEL (to IN_TURN)
    # Road building: LOCATION location1, location2 (to IN_TURN) CANCEL (to IN_TURN)
    # Options: list of location_ids
    # ROBBING_TILE, player_id
    # Actions: ROB_TILE tile_id (to ROBBING_PLAYER if players to rob, else to IN_TURN), CANCEL (only if not 7)
    # Options: list of tile_ids
    # ROBBING_PLAYER, player_id, tile_id
    # Actions: ROB_PLAYER player_id (to IN_TURN, CANCEL (back to ROBBING_TILE)
    # Options: list of player_id
    # SEVEN, player_id, players_over_seven
    # Actions: GIVE_AWAY hand (go to ROBBING_TILE if players_over_7 empty, else SEVEN again)
    # Options: any valid hand
    # Client should handle trade UI, only come with a proposal
    # API
    # Roll
    # Trade
    # Build
    # Play Dev Card
    # Pass
    # Start/Quit
    def __init__(self, num_players, dice):
        self.num_players = num_players
        self.players = [Player(i) for i in range(num_players)]
        self.board = Board()
        self.bank = Bank()
        self.dice = dice
        self.turn = 1
        self.starting_id = 0
        self.active_id = 0
        self.last_roll = (0, 0)
        self.longest_road_size = 4
        self.longest_road_player = None
        self.largest_army_size = 2
        self.largest_army_player = None
        self.state = GameState("NEW_GAME")

    def __repr__(self):
        return "Board:\n{0}\n".format(self.board)

    def get_state(self):
        return {
            "active_player": self.active_id,
            "roll": self.last_roll,
            "board": self.board.get_state(),
            "bank": self.bank.get_state(),
            "players": [p.get_state() for p in self.players],
            "state": self.state
        }

    def active_player(self):
        return self.players[self.active_id]

    def roll_dice(self):
        self.last_roll = self.dice.roll()

    def players_over_max(self):
        return [player.id for player in self.players if player.num_cards() > 7]

    def distribute_cards(self, roll):
        tr = self.board.transactions(roll)
        hands = self.bank.apply_transactions(tr)
        for player_id, new_cards in hands.items():
            self.players[player_id].hand.add_hand(new_cards)

    def update_longest_road(self, most_recent_road):
        longest = self.board.longest_road(self.active_id, most_recent_road)
        if longest > self.longest_road_size:
            self.longest_road_size = longest
            if self.longest_road_player is not None:
                self.players[self.longest_road_player].longest_road = False
            self.active_player().longest_road = True
            self.longest_road_player = self.active_id

    def update_largest_army(self):
        army = self.active_player().army_size()
        if army > self.largest_army_size:
            self.largest_army_size = army
            if self.largest_army_player is not None:
                self.players[self.largest_army_player].largest_army = False
            self.active_player().largest_army = True
            self.largest_army_player = self.active_id

    # public API
    def roll_to_start(self):
        if self.state.name != "NEW_GAME":
            return False
        self.roll_dice()
        r = sum(self.last_roll)
        self.state.attributes[self.active_id] = r
        if len(self.state.attributes) == self.num_players:
            self.starting_id = max(self.state.attributes,
                                   key=self.state.attributes.get)
            self.active_id = self.starting_id
            self.state = GameState("PLACE_PIECES", {
                "settlements_placed": 0,
                "structure": SETTLEMENT
            })
        else:
            self.active_id = (self.active_id + 1) % self.num_players
        return True

    def place_starting_piece(self, location):
        structure = self.state.attributes["structure"]
        if not self.board.can_build(
                self.active_id, structure, location, game_start=True):
            return False
        self.board.build(self.active_id, structure, location)
        placed = self.state.attributes["settlements_placed"]
        if structure == SETTLEMENT:
            self.state.attributes["settlements_placed"] += 1
            self.state.attributes["structure"] = ROAD
        elif placed == self.num_players * 2:
            self.state = GameState("START_TURN")
        else:
            self.state.attributes["structure"] = SETTLEMENT
            if placed > self.num_players:
                self.active_id = (self.active_id - 1) % self.num_players
            elif placed < self.num_players:
                self.active_id = (self.active_id + 1) % self.num_players
        return True

    def start_turn(self):
        if self.state.name != "START_TURN":
            return False
        self.roll_dice()
        r = sum(self.last_roll)
        if r == 7:
            p_over = self.players_over_max()
            if p_over:
                self.state = GameState("GIVE_AWAY", {"players_over": p_over})
            else:
                self.state = GameState("ROBBING")
        else:
            self.distribute_cards(r)
            self.state = GameState("IN_TURN")

    def give_away(self, p_id, hand):
        if self.state.name != "GIVE_AWAY" or p_id not in self.state.attributes[
                "players_over"]:
            return False
        p = self.players[p_id]
        if hand.size() != p.hand.size() // 2 or not p.hand.has_hand(hand):
            return False
        p.hand.subtract(hand)
        p_over = self.players_over_max()
        if p_over:
            self.state.attributes["players_over"] = p_over
        else:
            self.state = GameState("ROBBING")
        return True

    def buy_dev_card(self):
        if self.state.name != "IN_TURN":
            return False
        cost = h.required_hand(DEV_CARD)
        if self.active_player().can_buy(cost):
            self.active_player().buy(cost)
            self.bank.deposit(cost)
            d = self.bank.get_dev_card()
            self.active_player().development.add(d)
            return True
        return False

    def build_structure(self, structure):
        if self.state.name != "IN_TURN":
            return False
        cost = h.required_hand(structure)
        if self.active_player().can_build(structure, cost):
            self.state = GameState("BUILDING", {"structure": structure})
            return True
        return False

    def build_location(self, location):
        if self.state.name != "BUILDING":
            return False
        structure = self.state.attributes["structure"]
        cost = h.required_hand(structure)
        if not self.board.can_build(self.active_id, structure, location):
            return False
        self.board.build(self.active_id, structure, location)
        self.active_player().build(structure, cost)
        self.bank.deposit(cost)
        if structure == ROAD:
            self.update_longest_road(location)
        self.state = GameState("IN_TURN")
        return True

    def trade(self, other_id, active_res, other_res):
        if self.state.name != "IN_TURN":
            return False
        p1 = self.active_player()
        p2 = self.players[other_id]
        if not (p1.hand.has(active_res) and p2.hand.has(other_res)):
            return False
        p1.hand.subtract(active_res)
        p1.hand.add(other_res)
        p2.hand.subtract(other_res)
        p2.hand.add(active_res)
        return True

    def pass_turn(self):
        if self.state.name != "IN_TURN":
            return False
        self.active_id = (self.active_id + 1) % self.num_players
        if self.active_id == self.starting_id:
            self.turn += 1
        self.state = GameState("START_TURN")
        return True

    def rob_tile(self, tile_id):
        if self.state.name != "ROBBING" or not self.board.can_rob_tile(
                tile_id):
            return False
        if not self.board.players_on_tile(tile_id):
            self.state = GameState("IN_TURN")
        else:
            self.state = GameState("ROBBING_PLAYER", {"tile": tile_id})
        return True

    def rob_player(self, player_id):
        if self.state.name != "ROBBING_PLAYER":
            return False
        tile_id = self.state.attributes["tile"]
        if player_id not in self.board.players_on_tile(tile_id):
            return False
        self.board.rob_tile(tile_id)
        active_player = self.active_player()
        other_player = self.players[player_id]
        if other_player.num_cards():
            res = other_player.hand.random_card()
            other_player.hand.subtract(res)
            active_player.hand.add(res)
        self.state = GameState("IN_TURN")
        return True

    def play_dev_card(self, card):
        if self.state.name != "IN_TURN" or not self.active_player(
        ).development.has(card) or card == VICTORY_POINT:
            return False
        self.active_player().play_dev(card)
        if card == ROAD_BUILDING and not self.active_player().can_place_piece(
                ROAD):
            return False
        elif card == ROAD_BUILDING:
            self.state = GameState("ROAD_BUILDING", {"remaining_roads": 2})
        elif card == MONOPOLY:
            self.state = GameState("MONOPOLY")
        elif card == YEAR_OF_PLENTY:
            self.state = GameState("YEAR_OF_PLENTY")
        else:
            self.update_largest_army()
            self.state = GameState("ROBBING")
        return True

    def year_of_plenty(self, res1, res2):
        if self.state.name != "YEAR_OF_PLENTY":
            return False
        self.active_player().hand.add(res1)
        self.active_player().hand.add(res2)
        self.state = GameState("IN_TURN")
        return True

    def road_building(self, location):
        if self.state.name != "ROAD_BUILDING" or not self.board.can_build(
                self.active_id, ROAD, location):
            return False
        self.board.build(self.active_id, ROAD, location)
        self.active_player().place_piece(ROAD)
        self.update_longest_road(location)
        self.state.attributes["remaining_roads"] -= 1
        if self.state.attributes[
                "remaining_roads"] <= 0 or not self.active_player(
                ).can_place_piece(ROAD):
            self.state = GameState("IN_TURN")
        return True

    def monopoly(self, res):
        if self.state.name != "MONOPOLY":
            return False
        active = self.active_player()
        other_players = [p for p in self.players if p.id != active.id]
        for p in other_players:
            amt = p.hand.amt(res)
            p.hand.subtract(res, amt)
            active.hand.add(res, amt)
        self.state = GameState("IN_TURN")
        return True