Commits

larry committed ae91125

Norman is nicer about playing his safeties sooner when the draw pile is empty.

Comments (0)

Files changed (1)

 # todo:
 #
 #
-# write test for
-#	* p1 draw/play Go
-#	* p2 draw/play Accident
-#	* p1 draw/play ExtraTank
-#	* p2 draw/play OutOfGas
-#	* p1 coup fourres with ExtraTank
-#	* p1 should not be running! battle top is Accident!
-#
-# statistics to track:
-#	* each card
-#		* played
-#		* discarded
-#		* still in hand at end of game
-#   * all scorable things (delayed, all four, extension)
-#		* make object that represents a scorable thing,
-#		  then use Counter to count them just like cards
-#		* these objects then add themselves to your score!
-#		  self-assembly!
-#	* # of perfect games
-#
 # norman:
-#	* if he can't play any of his cards but safeties,
-#	  then at the end of the game he sees you discard,
-#	  he'll play all his safeties.
 #   * when deciding to continue,
-#		* if by not continuing we will win the entire game, DON'T CONTINUE
-#	    * if by not continuing they will win the entire game, CONTINUE
-#		* consider how many safeties we/they have, Right Of Way counts 2x (3x?)
 #		* consider the delta in miles
 #
 # smart computer player
 	safe_trip = False
 	shutout = False
 
+	perfect_hand = 4600
+
 	def __init__(self, player, as_if_won=False):
+		if player == None:
+			return
+
 		self.mileage = player.hand.mileage()
 
 		self.safeties = len(player.hand.safeties)
 			  or (self.mileage == (1000 if hand.extended else 700))
 			):
 			self.trip_complete = True
-			self.extension = self.mileage == 1000
 			self.delayed_action = not len(hand.draw_pile)
 			self.safe_trip = not player.hand.two_hundreds()
 			self.shutout = not player.others[0].hand.mileage()
 			"delayed_actions": self.delayed_action,
 			"safe_trips": self.safe_trip,
 			"shutouts": self.shutout,
-			"perfect_hands": self.total == 4600,
+			"perfect_hands": self.total == self.perfect_hand,
 		}
 		return { k: int(v) for k, v in d.items() }
 
 		return roll
 
 	def _decide_to_extend(self):
+		other = self.others[0]
+		their_score = Score(other, True)
+		their_total = their_score.total + other.game.score
+
+		our_score = Score(self, True)
+		our_total = our_score.total + self.game.score
+
+		# if by not extending we would immediately lose, extend.
+		if (their_total > our_total) and (their_total > 5000):
+			return True
+
+		# if by not extending we would immediately win, don't extend.
+		if (our_total > their_total) and (our_total > 5000):
+			return False
+
 		# shutout?  hot damn!
 		# don't chance the extend, take the points!
-		other = self.others[0]
-		if not other.hand.mileage():
+		if our_score.shutout:
 			return True
 
 		other_safeties = other.hand.safeties
 		extend_if = (
-			# we have half or more of the cards remining in the draw pile
+			# half or more of the cards remain in the draw pile
 			(len(hand.draw_pile) > (len(hand.original_deck) // 2))
 			# and they don't have right_of_way
 			and (right_of_way not in other_safeties)
 
 		# I give up!
 
-		# if all we have left is safeties, play them all.
-		# (don't compare them directly, the ordering may
-		# be different.)
-		if len(safeties) == len(self.hand.hand):
-			# note: if all we we have is safeties,
-			# then we have 4 or fewer cards in our hand,
-			# which means the draw pile is empty,
-			# which means I don't bother drawing.
-			assert not hand.draw_pile
+		# if the draw pile is empty, play all our safeties.
+		# if we're at this point in the card-playing decider,
+		# the odds we could get a coup fourre are vanishingly
+		# slim, and this is a kindness to the human player.
+		if not hand.draw_pile:
 			for card in safeties:
 				debug("P9.a")
 				self.play(card)
+			if not self.hand.hand:
 				return
 
 		# discard *something* none-safety-ish.
 class SmartComputerPlayer(AverageComputerPlayer):
 	"""
 	Moriarty is as smart as I can make him.
-	His main achievement over Norman is that he counts cards
-	and calculates odds.
+	His main achievement over AverageComputerPlayer is that
+	he counts cards and calculates odds.
 
 	(Moriarty is named for the machine intelligence that escaped
 	from the holodeck on the NCC 1701-D.)
 		self.p2.illegal = p2_illegal
 
 		series.start()
+		game.start()
 
 		# put cards back
 		hand.draw_pile = list(hand.original_deck)
 	def test_can_play_out_of_gas_when_other_player_uses_safety_and_has_right_of_way(self):
 		self.can_play_hazard_on_hazard_when_other_player_uses_safety_and_has_right_of_way(accident, out_of_gas)
 
+	def if_play_hazard_on_hazard_and_coup_fourre_first_hazard_still_counts(self, hazard1, hazard2):
+		self.p1.draw_and_play(roll)
+		self.p2.draw_and_play(hazard1, self.p1)
+
+		self.p1.draw(hazard2.safety)
+		self.p1.discard(100)
+
+		self.p2.draw_and_play(hazard2, self.p1)
+		self.p1.play(hazard2.safety)
+
+		self.assertFalse(self.p1.hand.rolling())
+		self.assertTrue(self.p1.hand.normalized_battle_top() == hazard1)
+
+	def test_coup_fourre_on_accident_leaves_out_of_gas(self):
+		self.if_play_hazard_on_hazard_and_coup_fourre_first_hazard_still_counts(out_of_gas, accident)
+
+	def test_coup_fourre_on_out_of_gas_leaves_flat_tire(self):
+		self.if_play_hazard_on_hazard_and_coup_fourre_first_hazard_still_counts(flat_tire, out_of_gas)
+
+	def test_coup_fourre_on_flat_tire_leaves_accident(self):
+		self.if_play_hazard_on_hazard_and_coup_fourre_first_hazard_still_counts(accident, flat_tire)
+
+	def test_scoring(self):
+		score = Score(None)
+		score.mileage = 1000
+		score.trip_complete = score.shutout = score.delayed_action = score.safe_trip = True
+		score.safeties = score.coups_fourres = 4
+		self.assertEqual(score.total, Score.perfect_hand)
+
 
 import sys, tty, termios