class Monopoly(): def __init__(self, pnames): """ :param pnames: An Array. Name of the players as Strings """ self.board = Board() self.players = tuple([Player(pn, self.board, self) for pn in pnames]) self.plookup = {p.getId(): p for p in self.players} self.lastRoll = None self.p = None self.getFirstPlayer() self.state = 0 def getFirstPlayer(self): """ Set the current player index if not already """ if not self.p: self.p = rd.randrange(0, len(self.players)) def getCurPlayer(self): """ Get current player's object method Return the current player's Player object based on the current player index (self.p) :return: A Player object. The current player """ return self.players[self.p] def roll(self): """ Dice roll method Roll a double dice, and return the result :return: A Tuple contains the sum of the dice and a subtuple containing the value of two dices """ d1 = rd.randint(1, 6) d2 = rd.randint(1, 6) self.lastRoll = d1 + d2 signal(SIG_ROLL, (d1 + d2, (d1, d2))) return d1 + d2, (d1, d2) def getBoard(self): """ Get board method Return the board associated with the game :return: a Board object """ return self.board def setState(self, new_state): """ Set game state method Set the game state to new_state :param new_state: An Integer. The new state value to be set as the game's state """ self.state = new_state def isState(self, test): """ Check game state method Return True if the current state is equal to test, False if otherwise :param test: An Integer. The state value to be compared with the game's state :return: A Boolean value """ return self.state == test def move(self, x, index=False): """ "Smart" move method This method takes in either a step, index or Slot object as x and move the current player to the appropriate slot. :param x: An Integer or Slot object :param index: A Boolean value. True if x: Integer is an index value, False if a step value. Unused if x is a Slot object """ board = self.getBoard() player = self.getCurPlayer() if type(x) == int: if not index: newIdx, oldIdx = board.move(player, x) if x <= 0: return else: newIdx, oldIdx = board.moveToIndex(player, x) else: newIdx, oldIdx = board.moveTo(player, x) # Pay salary if newIdx < oldIdx: pay(BANK, SALARY, player) def updateNextPlayer(self): """ Player switching method Update the next player on the list as the current player. """ self.p = (self.p + 1) % self.getPlayerCount() def sendToJail(self): """ Send to jail method Send the current player to Jail, and set the status as "in jail". """ player = self.getCurPlayer() signal(SIG_GOTOJAIL, (player.hasJFC(), )) self.board.moveTo(player, self.board.getJail()) if not player.hasJFC(): player.setInJail(True) player.resetJTL() else: player.popJFC() def cardExec(self, card): """ Card action execute method Execute a card based on its intents. Intents are set flags bit in accordance to the intent flags in flags.py Once an intent has been establish, it will execute appropriate functions. Is this the best way to process cards? Probably not Does it work? Yes! :param card: A Card object. The card to be executed """ tups = card.getAction() player = self.getCurPlayer() board = self.getBoard() for t in tups: intent, param = t if MOVE & intent: if NEAREST_RAIL & intent or NEAREST_UTIL & intent: indices = [ idx for _, idx in (RAILROAD if NEAREST_RAIL & intent else UTILITY) ] indices.sort() curIdx = player.getSlot().getIndex() if curIdx > max(indices): self.move(indices[0], index=True) else: res = next(i for i in indices if i > curIdx) self.move(res, index=True) elif BACK & intent: self.move(-3) elif JAIL & intent: self.sendToJail() else: self.move(board[param]) elif CHECK & intent: if ROLL & intent: self.roll( ) # TODO: Optimize. This is redundant if util is owned by current player. self.check(param) elif PAY & intent: if OTHERS & intent: for payee in self.players: if payee != player: pay(player, param, payee) if not SELF & intent else pay( payee, param, player) elif SELF & intent: pay(BANK, param, player) elif PROP & intent: numHouses, numHotels = player.getBuildingCount() feeHouse, feeHotel = param total = numHouses * feeHouse + numHotels * feeHotel pay(player, total, BANK) else: pay(player, param, BANK) def whoNext(self): """ Get current player's name method Return the current player's name. :return: A String. The current player's name """ return self.getCurPlayer().getName() def getPlayerCount(self): """ Get number of player method Return the number of players in the game :return: An Integer. The number of players """ return len(self.players) def getState(self): """ Get game's state method Return the current state of the game :return: An Integer. The current state of the game """ return self.state def getCurrentPlayerData(self): """ Get data of the current player method Return a snapshot of the current player's data :return: A dict object. The data of the current player """ player = self.getCurPlayer() return player.getData() def getAllPlayerData(self, by_uuid=False): """ Get data of all players method Return snapshots of all players' data :param by_uuid: A Boolean value. If True, the key of the return dict object will be the player's UUID :return: A dict object. The data of all player. Key will be the names of players by default """ ret = {} for p in self.players: next_p = p.getData() if by_uuid: ret[next_p["id"]] = next_p else: ret[next_p["name"]] = next_p return ret def getData(self): """ Return a snapshot of the game's data :return: A dict object. The data of the board """ ret = self.getBoard().getData() ret["players"] = {} for p in self.players: ret["players"][p.getName()] = p.getData() return ret