...

Commits (2)
 ... ... @@ -34,8 +34,7 @@ class Player: self.__chips = int(0) self.__cards = [] self.__chipsOnTable = int(0) self.__position = int(0) self.__hasHadAction = bool(False) self.__hasHadAction = False self.__playerState = PlayerState.PLAYING self.__name = name self.__potId = -1 ... ... @@ -170,12 +169,15 @@ class Player: self.__chips += chips def yieldBet(self): # Moves the money that was on the table to the pot self.__hasHadAction = False temp = self.__chipsOnTable self.__chipsOnTable = 0 return temp def yieldPartialBet(self, value): ## Moves part of the money that was on the table to the pot, used for # split pots self.__hasHadAction = False chips = min(value, self.__chipsOnTable) self.__chipsOnTable -= chips ... ... @@ -187,10 +189,8 @@ class Player: def giveBB(self, bigBlind): return self.__putMoneyOnTable(bigBlind) # Actions, called by Table. # By calling these methods, Table authorises the transaction. def bet(self, amount): # amount is the money to be added # Returns the amount actually added ... ...
 # Game engine ## class Table # This class is pretty much the game engine, it receives orders from the # players connected to the front end, handles the money, controls the rules, # and generates the json for the front end. # One instance represents one table of poker, several instances may work in # parallel in order to allow for more players. # # Tests : # t = Table() # t.addPlayer(Player()) # t.addPlayer(Player()) # t.startHand() # t.call(1) # t.call(0) # Then repeat the last 2 at will import time from math import exp ... ... @@ -39,6 +36,7 @@ class PlayingMode(Enum): def defaultCallBack(): # Function used as default for objects that require a callback function print("Default CallBack function.") return ... ... @@ -116,12 +114,13 @@ class Table(object): self.__waitingPlayers.append(p) self.__mainMutex.release() # Return the player at required position, 0 is button def __playerAtPos(self, pos): # Return the player index located 'pos' sits after the button return (self.__button + pos) % len(self.__players) # Passes action to the next player in the hand def __nextPos(self): ## Called when a player successfully had an action, changes the active # player to the next one available playerFound = False while not playerFound: self.__activePlayer = (self.__activePlayer + 1) % len(self.__players) ... ... @@ -142,6 +141,9 @@ class Table(object): return self.__activePlayer def startHand(self): ## Start a new hand (checks if enough players are still in the game) # This method resets variable, passes the button, and checks if waiting # players need to be added and/or cleans busted ones. self.__mainMutex.acquire() print(self.__playingMode) if self.__playingMode == PlayingMode.TOURNOI: ... ... @@ -190,10 +192,14 @@ class Table(object): self.__mainMutex.release() def __nextStep(self): ## Method called when a turn of bet has ended, and the next step of the # hand is to begin. # It will check if the hand has already ended (everybody folded, # all-ins, etc), or simply gather the money bet by the player and go on # to the next stage of the hand. self.__currentBet = 0 self.__minRaise = self.__blind self.__pickUpBets() # Check if hand has already ended if self.__checkEndOfHand(): self.__givePot() self.__button = self.__playerAtPos(1) ... ... @@ -226,6 +232,9 @@ class Table(object): self.__nextPos() def __pickUpBets(self): ## Gather the money that was bet by the player and gives it to the pot. # This method is quite tricky as it must handle all-ins and side pots, # so many turns of gathering may be required. pickUpDone = False minAllInValue = 0 while not pickUpDone: ... ... @@ -250,6 +259,9 @@ class Table(object): return def __checkEndOfHand(self): ## This method determines if the hand has ended, either by finishing # the last round of bets, or premature folds/all-ins. # Returns true if the hand has ended. handEnded = False if self.__gameState == GameState.RIVER or handEnded == True: handEnded = True ... ... @@ -262,8 +274,16 @@ class Table(object): return handEnded def __givePot(self): # Only one player left ## This method is called when the hand has ended, and the money # gathered in the pot needs to be redistributed. # If the hand ended before the river with all-ins, additionnal cards # must first be drawn to complete the board cards. # Each side pot is shared between the players allowed to, starting # the player or players with the best hand down to the players with the # the lesser hands (thus satisfying both situations of split and side # pots). if self.__nbrPlayersInHand == 1: # Only one player left winningPlayer = self.__players[0] for (i, p) in enumerate(self.__players): if p.isPlaying: ... ... @@ -299,7 +319,8 @@ class Table(object): print("Player {} ({}{}) : {}".format(p.name, p.holeCards[0].cardStr(), p.holeCards[1].cardStr(), p.hand.handStr())) remainingPlayers.sort() remainingPlayers.reverse() # class players by equivalent classes # Class players by equivalent classes (all players in the class # have a hand of identical value, thus needing a split pot) classedPlayers = [[remainingPlayers[0]]] for i in range(1, len(remainingPlayers)): if not remainingPlayers[i] < classedPlayers[-1][0]: ... ... @@ -307,13 +328,17 @@ class Table(object): else: classedPlayers.append([remainingPlayers[i]]) for l in classedPlayers: # Start with the players with the best hand for k in range(len(self.__pots)): # Give pots from the lesser valued on onwards nbrSharing = 0 for i in range(len(l)): # Count number of players that share the pot if l[i].potId == -1 or l[i].potId >= k: nbrSharing += 1 if nbrSharing and self.__pots[k]: for i in range(len(l)): # Give away the pot to the players who desserve it if l[i].potId == -1 or l[i].potId >= k: l[i].receiveChip(int(self.__pots[k] / nbrSharing)) print("Player {} receives {} from pot {}.".format(l[i].name, int(self.__pots[k] / nbrSharing), k)) ... ... @@ -324,6 +349,7 @@ class Table(object): return def __isBettingDone(self): ## Check wether to go to the next player or next stage of the hand nbrInHand = 0 for p in self.__players: if p.isInHand: ... ... @@ -336,7 +362,9 @@ class Table(object): return True def playerAction(self, action, playerId, amount): # timer management ## Methode called from the front end. # Checks if the action is globally valid, then call the specific method # that will check if the precise action is valid. self.__mainMutex.acquire() self.__timer.cancel() p = self.__players[playerId] ... ... @@ -368,6 +396,9 @@ class Table(object): self.__mainMutex.release() return True ## Specific action functions, they check the validity of the demand then # transfers it to the player instance and returns wether the action # succeded or not. def __fold(self, p): if not p.fold(): print("Player {} cant fold, fold failed (unexpected).".format(p.name)) ... ... @@ -412,9 +443,8 @@ class Table(object): return True # State Table def state(self, userId): # Json state for the front end. rtn = json.dumps({ "pot": self.__pots[0], "activePlayer": self.__activePlayer, ... ... @@ -442,12 +472,14 @@ class Table(object): return rtn def getPlayerIndex(self, userId): # Transforms a usedId into the position in the player list. for (i, p) in enumerate(self.__players): if p.uniqueId == userId: return i return -1 def __getHoleCards(self, userId): # Returns hole cards to be sent with the json. rtn = [] userIndex = self.getPlayerIndex(userId) if userIndex == -1: ... ... @@ -471,6 +503,7 @@ class Table(object): return rtn def __getBettingRange(self, userId): # Returns betting range to be sent with the json. userIndex = self.getPlayerIndex(userId) if userIndex == -1: return [] ... ... @@ -478,6 +511,7 @@ class Table(object): return [self.__currentBet + self.__minRaise - p.chipsOnTable, p.remainingChips] def __availableActions(self, userId): # Returns all of the actions that a player may do (sent with the json). userIndex = self.getPlayerIndex(userId) if userIndex == -1: return [] ... ... @@ -495,6 +529,7 @@ class Table(object): return rslt def __getAmountToCall(self, userId): # Returns the amount required to call (sent with the json). userIndex = self.getPlayerIndex(userId) if userIndex == -1: return 0 ... ... @@ -502,6 +537,8 @@ class Table(object): return self.__currentBet - p.chipsOnTable def __defaultActionCallback(self): ## Callback for the player time-out timer, performs the best action # available without putting money out. activePlayerId = self.__players[self.__activePlayer].uniqueId if "check" in self.__availableActions(activePlayerId): self.playerAction("check", self.__activePlayer, 0) ... ... @@ -512,6 +549,8 @@ class Table(object): self.__bCastCB() def __calculateBlinds(self): ## Sets blind depending on time elapsed since the first hand # (tournament mode only) now = time.time() elapsed = now - self.__startTime val = int(exp(elapsed/60/7)+1) ... ...