""" Casino game simulator and analysis	The Game has a Player (all players are independent as in roulette and blackjack, unlike poker).	The Game creates a Table to hold bets, some of which are persistent between rounds.	While the Game is running, it goes back to the Table for each betting opportunity.	Roulette is stateless (round=game), Craps is stateful (game as 1-many rounds),		Blackjack is stateful and has player interaction (hit, stand, split, double, insure).""""""	Some hacks:	- A game may have multiple "tables", usually used to handle splits in blackjack."""from simlog import *;import string;class Bet:	"A specific bet or play (bet with an amount of zero) by a specific player"	def __init__( self, name, odds ):		"The name of the bet and the payout rules"		self.name= name;		self.odds= odds;		self.log= Simlog().instance();		self.player= None;		self.amount= 0;	def place( self, player, amount ):		"Place a specific amount on this bet"		self.player= player;		self.amount= amount;		self.log.msg( "bet.place." + self.name, '"%s" %d' % ( self.player.__doc__, self.amount ) );	def wins( self, roll ):		"The bet is a winner, pay it"		if( self.player == None or self.amount == 0 ): return;		# get odds for this payout; if no specific odds, use the "0" for generic odds;		# otherwise this is an error		odds= self.odds.get( roll, self.odds.get(0,(0,0)) );		if( odds[0] == 0 ): print self.name, roll, str(self.odds), odds;		win= self.amount*odds[0]/odds[1];		self.log.msg( "bet.win." + self.name, "win " + str(win) + " on " + str(self.amount) );		self.player.wins( win );		self.clear();	def lose( self ):		"The bet is a loser, reduce the player's stake"		if( self.player == None or self.amount == 0 ): return;		self.log.msg( "bet.lose." + self.name, "lose " + str(self.amount) );		self.player.lose( self.amount );		self.clear();	def push( self ):		"The bet is a push - the money is returned to the player"		self.log.msg( "bet.push." + self.name, "push " + str(self.amount) );		self.clear();	def working( self ):		"Is this bet working?"		return self.player != None;	def clear( self ):		"A bet is cleared, this is part of moving a bet"		self.amount= 0;		self.player= None;	def __str__( self ):		if( self.player == None ): return str(self.odds);		return "%s placed %d at %s" % ( self.player.__doc__, self.amount, str(self.odds) );		class GameTable:	"Game Table: Keeps all bets; some bets persist between rounds"	def __init__( self ):		self.log= Simlog().instance();		self.bets = { };	# the domain of all possible bets		self.player= None;	def clearAll( self, player ):		self.log.msg( "table.clearAll", player.__doc__ );		for b in self.bets.values():			b.clear();		self.player= player;	def place( self, betName, amount ):		"Place a specific bet with a specific amount"		bet= self.bets.get( betName, None );		if( bet == None ): print "unknown bet", betName;		bet.place( self.player, amount );		self.log.msg( "table.bet." + betName, "betting " + str(amount) );	def move( self, fromName, toName ):		"Move bet from one place to another, typically used in Craps to move Come Bet to a Point"		if( self.bets[fromName].working() ):			self.bets[toName].amount= self.bets[fromName].amount;			self.bets[toName].player= self.bets[fromName].player;			self.bets[fromName].clear();			self.log.msg( "table.bet." + fromName, "moving " + str(self.bets[toName].amount) + " to " + toName );	def combine( self, fromName, toName ):		"Combine one bet's amount into abother, typically used in Blackjack to handle a double-down"		if( self.bets[fromName].working() ):			self.bets[toName].amount += self.bets[fromName].amount;			self.bets[fromName].clear();			self.log.msg( "table.bet." + fromName, "total " + str(self.bets[toName].amount) + " on " + toName );				def win( self, betName, roll ):		bet= self.bets.get( betName, None );		if( bet == None ): print "unknown bet", betName;		bet.wins( roll );	def lose( self, betName ):		bet= self.bets.get( betName, None );		if( bet == None ): print "unknown bet", betName;		bet.lose();	def push( self, betName ):		bet= self.bets.get( betName, None );		if( bet == None ): print "unknown bet", betName;		bet.push();	def working( self, betName ):		"Determine if a bet is currently working"		bet= self.bets.get( betName, None );		if( bet == None ): return 0;		else: return bet.working();	def state( self ):		"return the current table state: bets working, bets allowed"		w= [];		for b in self.bets:			if( self.bets.working() ): w.append( b );		return str(w);class Command:	"A player's command, either place a bet or make another kind of play."	def __init__( self, name ):		self.name= name;	def __str__( self ):		return "Command %s" % self.name;	def do( self, game ):		pass;		class CommandBet( Command ):	def __init__( self, bet, amount ):		Command.__init__( self, "bet" );		self.bet= bet;		self.amount= amount;	def __str__( self ):		return "Command %s %f on %s" % ( self.name, self.amount, self.bet );	def do( self, game ):		game.place( self.bet, self.amount );class CommandSequence( Command ):	def __init__( self ):		Command.__init__( self, "sequence" );		self.sequence= [];	def __str__( self ):		return "Command { " + string.join( map( str, self.sequence ), ", " ) + " }";	def addCommand( self, command ):		self.sequence.append( command );	def do( self, game ):		for c in self.sequence:			c.do( game );class CasinoGame:	"A Casino Game - uses a GameTable to retain bets"	def __init__( self ):		self.table= GameTable();		self.log= Simlog().instance();		self.player= None;	def seatPlayer( self, player ):		self.log.msg( "game.seatPlayer", player.__doc__ );		self.player= player;		self.table.clearAll(player);	def state( self ):		return ( "default", 0 );	def place( self, bet, amount ):		return self.table.place( bet, amount );	def working( self, bet ):		return self.table.working( bet );	def play( self ):		"play 1 round of this game, offering betting opportunities to player"		self.player.startRound( self );		# determine payouts 		self.player.endRound( self );class Player:	"short description of the betting strategy"	def __init__( self, limit ):		self.limit= limit;		self.rounds= 0;		self.stake= self.limit;		self.log= Simlog().instance();		self.minStake= self.stake;		self.maxStake= 0;	def start( self, rounds ):		self.rounds= rounds;		self.stake= self.limit;		self.minStake= self.stake;		self.maxStake= self.limit;	def status( self ):		self.log.msg( "player", "stake = %d" % (self.stake) );	def name( self ):		return self.__doc__;	def canPlay( self ):		return self.stake > 0 and self.rounds > 0;	def startRound( self, game ):		"place start-of-round bets"		self.rounds -= 1;		return Command( "pass" );	def inRound( self, game ):		"return in-round command or bet"		return Command( "pass" );	def endRound( self, game ):		"check table state or game state if the player that keeps history"		pass;	def wins( self, win ):		self.stake += win;		self.log.msg( "player.win", "win %d: stake %d" % ( win, self.stake ) );		if( self.stake > self.maxStake ): self.maxStake= self.stake;	def lose( self, amount ):		self.stake -= amount;		self.log.msg( "player.lose", "lose %d: stake %d" % ( amount, self.stake ) );		if( self.stake < self.minStake ): self.minStake= self.stake;	def finalNet( self ):		return self.stake - self.limit;	def minNet( self ):		return self.minStake - self.limit;	def maxNet( self ):		return self.maxStake - self.limit;class PlayerFactory:	"create and initialize individual player objects using just a name"	def __init__( self ):		pass;	def newPlayer( self, name ):		if( name == "simple" ): return Player( 100 );		else: print "unknown player name", name;		return None;	def playerSet( self, names ):		return map( self.newPlayer, names );def testGame():		log= Simlog().instance();		log.watch( ["sim.*"] );		game= CasinoGame();		playerFactory= PlayerFactory();		player= playerFactory.newPlayer( "simple" );		game.seatPlayer( player );		player.start( 5 );		round= 0;		while( player.canPlay() ):			round += 1;			log.msg( "sim.round", "round %d, stake %d" % (round,player.stake) );			game.play();		log.msg( "sim.final", "final %d" % (player.stake) );if( __name__ == "__main__" ):	testGame();	log= Simlog().instance();	pf= PlayerFactory();	print str( pf.playerSet( ["simple","another"] ) );