""" Blackjack simulator; see http://thewizardofodds.com/ for rules and analysis """from random import *;from simlog import *;from casino import *;from simulator import *;from cards import *;class CommandHit( Command ):	def __init__( self ):		Command.__init__( self, "hit" );	def do( self, game ):		game.stand= 0;class CommandStand( Command ):	def __init__( self ):		Command.__init__( self, "stand" );	def do( self, game ):		game.stand= 1;class BlackjackTable( GameTable ):	"Blackjack Table: Keeps all bets"	def __init__( self ):		GameTable.__init__( self );		self.bets = {			"ante":				Bet( "ante", { 0: (1,1), 21:(3,2) } ),			"split":			Bet( "split",{ 0:(1,1) } ),			"double":			Bet( "double", { 0:(1,1) } )			#"hit":				Bet( "hit",  { 0:(1,1) } ),			#"stand":			Bet( "stand", { 0:(1,1) } )			};	def __str__( self ):		return string.join( map( lambda x: '%s %s'%x, self.bets.items() ), ", " );class BlackjackGame ( CasinoGame ):	"The Blackjack Game - contains one BlackjackTable per split"	def __init__( self ):		CasinoGame.__init__( self );		self.tableSplit= [ BlackjackTable() ];	# to handle splits		self.shoe= BJShoe( 6 );		self.shoe.shuffle().burn();		self.dealerCards= BJHand();		self.playerCards= [ BJHand() ];			# to handle splits		self.currentHand= 0;		self.stand= 0;	def seatPlayer( self, player ):		self.player= player;	def state( self ):		return (self.dealerCards.card(0),self.playerCards[self.currentHand]);	def stateStr( self ):		return "showing:%s; player:%s" % ( self.dealerCards.card(0), str(self.playerCards[self.currentHand]) );	def place( self, bet, amount ):		self.log.msg( "game.place", "hand=%d" % self.currentHand, bet, str(amount) );		p= self.tableSplit[self.currentHand].place( bet, amount );		#self.log.msg( "game.place.bets", "hand=%d" % self.currentHand, str(self.tableSplit[self.currentHand]) );		return p;	def stand( self ):		self.stand= 1;	def working( self, bet ):		self.log.msg( "game.working", "hand=%d" % self.currentHand );		return self.tableSplit[self.currentHand].working( bet );	def payout( self, hand ):		"Basic Blackjack payout"		self.log.msg( "game.hands", "dealer:" + str(self.dealerCards), "player:" + str(self.playerCards[hand]) );		if( self.dealerCards.points() > 21 or self.dealerCards.points() < self.playerCards[hand].points() ):			self.tableSplit[hand].win( "ante", self.playerCards[hand].points() );		if( self.dealerCards.points() == self.playerCards[hand].points() ):			self.tableSplit[hand].push( "ante" );		for b in self.tableSplit[hand].bets.keys():			self.tableSplit[hand].lose( b );	def bustedOrBlackjack( self, hand ):		"check for immediate payouts (busted or blackjack), irrespective of dealer's cards"		if( self.playerCards[hand].points() > 21 ): return 1;		if( self.playerCards[hand].points() == 21 and self.playerCards[hand].count() == 2 ): return 1;		return 0;	def payoutImmediate( self, hand ):		"perform immediate payouts (busted or blackjack), irrespective of dealer"		self.log.msg( "game.hands", "dealer:" + str(self.dealerCards) + ", player:" + str(self.playerCards[hand]) );		if( self.playerCards[hand].points() > 21 ): self.tableSplit[hand].lose( "ante" );		if( self.playerCards[hand].points() == 21 and self.playerCards[hand].count() == 2 ): self.tableSplit[0].win( "ante", 21 );	def playHand( self ):		"play self.currentHand; one of possibly several splits"		self.stand= 0;		while( self.playerCards[self.currentHand].points() < 21 and not self.stand ):			cmd= self.player.inRound( self );			Simlog().instance().msg( "game.inround", str(cmd) );			cmd.do( self );			self.log.msg( "game.bets", "hand=%d" % self.currentHand, str(self.tableSplit[self.currentHand]) );			# if bet is hit, add a card;			if( cmd.name == "hit" ): 				self.log.msg( "game.hit", self.stateStr() );				self.playerCards[self.currentHand].addCard( self.shoe.next() );			# if bet is double, add one more card and stand			elif( cmd.name == "double"  ):				self.log.msg( "game.double", self.stateStr() );				self.playerCards[self.currentHand].addCard( self.shoe.next() );				ante= self.tableSplit[self.currentHand].bets["ante"];				ante.amount = ante.amount * 2;				self.tableSplit[self.currentHand].bets["double"].clear();				self.stand= 1;			# if bet is split AND both cards have same rank, we have one more hand to play			elif( cmd.name == "split" ):				self.log.msg( "game.split", self.stateStr() );				# create a new hand from one card in this hand; append to the hands				self.playerCards.append( self.playerCards[self.currentHand].split() );				# create a new table to hold the bet				# take the amount of the split bet and create an ante bet on the new table				self.tableSplit.append( BlackjackTable() );				self.tableSplit[-1].clearAll(self.player);						self.tableSplit[-1].place( "ante", self.tableSplit[self.currentHand].bets["split"].amount );				self.tableSplit[self.currentHand].bets["split"].clear();			# if bet is stand, done			elif( cmd.name == "stand" ):				self.log.msg( "game.stand", self.stateStr() );				self.stand= 1;			else:				self.log.msg( "game.betError", str(self.tableSplit[self.currentHand]), self.stateStr() );				self.stand= 1;	def play( self ):		"play 1 round of Blackjack"		self.dealerCards.reset();		self.playerCards= [ BJHand() ];				# to handle splits		self.tableSplit= [ BlackjackTable() ];		# to handle splits		for t in self.tableSplit:			t.clearAll(self.player);				# each split must be associated with the player		self.currentHand= 0;		cmd= self.player.startRound( self );		Simlog().instance().msg( "game.start", str(cmd) );		cmd.do( self );				self.dealerCards.addCard( self.shoe.next() ).addCard( self.shoe.next() );		self.playerCards[0].addCard( self.shoe.next() ).addCard( self.shoe.next() );				self.log.msg( "game.dealer", str(self.dealerCards.card(0)) );		# handle appearance of subsequent hands due to splits		while( self.currentHand != len(self.playerCards) ):			self.playHand();			if( self.bustedOrBlackjack( self.currentHand ) ):				self.payoutImmediate( self.currentHand );			self.currentHand= self.currentHand + 1;		# play out dealer's hand		while( self.dealerCards.points() <= 16 ):			self.dealerCards.addCard( self.shoe.next() );		# resolve bets		for h in range( len(self.playerCards) ):			if( self.tableSplit[h].working( "ante" ) ):				self.payout( h );		self.player.endRound( self );class BlackjackPlayer ( Player ):	"bet until 17, splitting A's and 8's"	def __init__( self, limit ):		Player.__init__( self, limit );	def startRound( self, game ):		"Place start-of-round bet"		Player.startRound( self, game );		return CommandBet( "ante", 1 );	def canSplit( self, hand ):		"Exactly 2 cards of same point level"		return len(hand.cards) == 2 and hand.cards[0].points == hand.cards[1].points;	def soft( self, hand ):		"Ace and misc. cards totalling 2-8 (total 13-19 with one or more aces)."		return hand.points() in [ 13, 14, 15, 16, 17, 18, 19 ] and hand.contains(11);	def inRound( self, game ):		"hit, stand, double or split"		Player.inRound( self, game );		(dealer,hand)= game.state();		if( self.canSplit( hand ) and hand.cards[1].points in [ 8, 11 ] ):			self.log.msg( "player.split", str(hand) );			return CommandBet( "split", 1 );		if( hand.points() < 17 ):			self.log.msg( "player.hit", str(hand) );			return CommandHit()		else:			self.log.msg( "player.stand", str(hand) );			return CommandStand()class WizOddsBlackjackPlayer ( BlackjackPlayer ):	"wizodds strategy"	def inRound( self, game ):		"hit, stand, double or split"		Player.inRound( self, game );		(dealer,hand)= game.state();		# splits		if( self.canSplit( hand ) and hand.cards[1].points in [ 8, 11 ] ):			return CommandBet( "split", 1 );		elif( self.canSplit( hand ) and hand.cards[1].points in [ 2, 3 ] ):			if( dealer.points in [2,3,4,5,6,7] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 4 ):			if( dealer.points in [5,6] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 5 ):			if( dealer.points in [2,3,4,5,6,7,8,9] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 6 ):			if( dealer.points in [2,3,4,5,6] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 7 ):			if( dealer.points in [2,3,4,5,6,7] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 9 ):			if( dealer.points in [2,3,4,5,6,8,9] ):				return CommandBet( "split", 1 );			else:				return CommandStand()		elif( self.canSplit( hand ) and hand.cards[1].points == 10 ):			return CommandStand()		# soft		elif( self.soft( hand ) and hand.points() in [ 13, 14 ] ):			if( dealer.points in [ 5, 6 ] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() in [ 15, 16 ] ):			if( dealer.points in [ 4, 5, 6 ] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() == 17 ):			if( dealer.points in [ 3, 4, 5, 6 ] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() == 18 ):			if( dealer.points in [ 3, 4, 5, 6 ] ):				return CommandBet( "double", 1 );			elif( dealer.points in [ 2, 7, 8 ] ):				return CommandStand()			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() >= 19 ):				return CommandStand()		# hard		elif( hand.points() <= 8 ):			return CommandHit()		elif( hand.points() == 9 ):			if( dealer.points in [3,4,5,6] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( hand.points() == 10 ):			if( dealer.points in [2,3,4,5,6,7,8,9] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( hand.points() == 11 ):			if( dealer.points in [2,3,4,5,6,7,8,9,10] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( hand.points() == 12 ):			if( dealer.points in [4,5,6] ):				return CommandStand();			else:				return CommandHit()		elif( hand.points() in [13,14,15,16] ):			if( dealer.points in [2,3,4,5,6] ):				return CommandStand();			else:				return CommandHit()		elif( hand.points() >= 17 ):				return CommandStand();		else:			return CommandStand()			self.log.msg( "player.default", str(hand) );			class ESBlackjackPlayer ( BlackjackPlayer ):	"Edwin Silberstang strategy"	def inRound( self, game ):		"hit, stand, double or split"		Player.inRound( self, game );		(dealer,hand)= game.state();		if( self.canSplit( hand ) and hand.cards[1].points in [ 8, 11 ] ):			return CommandBet( "split", 1 );		elif( self.canSplit( hand ) and hand.cards[1].points == 2 ):			if( dealer.points in [3,4,5,6,7] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 3 ):			if( dealer.points in [4,5,6,7] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 4 ):			if( dealer.points in [5,6] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 6 ):			if( dealer.points in [2,3,4,5,6] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 7 ):			if( dealer.points in [2,3,4,5,6,7] ):				return CommandBet( "split", 1 );			else:				return CommandHit()		elif( self.canSplit( hand ) and hand.cards[1].points == 9 ):			if( dealer.points in [2,3,4,5,6,8,9] ):				return CommandBet( "split", 1 );			else:				return CommandStand()		elif( self.canSplit( hand ) and hand.cards[1].points == 10 ):			return CommandStand()		# soft		elif( self.soft( hand ) and hand.points() in [ 13, 14, 15, 16 ] ):			if( dealer.points in [ 4, 5, 6 ] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() == 17 ):			if( dealer.points in [ 2, 3, 4, 5, 6 ] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() == 18 ):			if( dealer.points in [ 2, 7, 8, 11 ] ):				return CommandStand()			elif( dealer.points in [ 3, 4, 5, 6 ] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( self.soft( hand ) and hand.points() >= 19 ):				return CommandStand()		# hard		elif( hand.points() <= 8 ):			return CommandHit()		elif( hand.points() == 9 ):			if( dealer.points in [2,3,4,5,6] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( hand.points() == 10 ):			if( dealer.points in [2,3,4,5,6,7,8,9] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( hand.points() == 11 ):			if( dealer.points in [2,3,4,5,6,7,8,9,10] ):				return CommandBet( "double", 1 );			else:				return CommandHit()		elif( hand.points() == 12 ):			if( dealer.points in [4,5,6] ):				return CommandStand();			else:				return CommandHit()		elif( hand.points() in [13,14,15,16] ):			if( dealer.points in [2,3,4,5,6] ):				return CommandStand();			else:				return CommandHit()		elif( hand.points() >= 17 ):				return CommandStand();		else:			return CommandStand()			self.log.msg( "player.default", str(hand) );class BlackjackPlayerFactory ( PlayerFactory ):	def __init__( self ):		PlayerFactory.__init__( self );	def newPlayer( self, name ):		if( name == "simple" ): return BlackjackPlayer( 100 );		if( name == "wizodds" ): return WizOddsBlackjackPlayer( 100 );		if( name == "silberstang" ): return ESBlackjackPlayer( 100 );		else: print "unknown", name;def main():	#(log,n)= ( Simlog().instance().summary(), 100 );	#log.watch( ["player.default"] );	(log,n)= ( Simlog().instance().detail(), 1 );	#(log,n)= ( Simlog().instance().watch(["player.*","game.split","game.betError"]), 2 );	a= Analyzer();	s= Simulator( BlackjackPlayerFactory().playerSet( ["simple","wizodds","silberstang"] ), BlackjackGame(), a );	s.generate( n, 100 );	a.report();if( __name__ == "__main__" ):	main();