...
 
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)
......