# HG changeset patch # User Larry Hastings # Date 1328866966 0 # Node ID ae91125bf1eef19e313c3508e87b80ee213190a6 # Parent 25b5e29cea81247f1966bb7fcff8f982bd1bbbda Norman is nicer about playing his safeties sooner when the draw pile is empty. diff --git a/mille.py b/mille.py --- a/mille.py +++ b/mille.py @@ -25,34 +25,8 @@ # 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 @@ -548,7 +522,12 @@ 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) @@ -559,7 +538,6 @@ 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() @@ -634,7 +612,7 @@ "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() } @@ -1064,15 +1042,29 @@ 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) @@ -1570,18 +1562,15 @@ # 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. @@ -1679,8 +1668,8 @@ 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.) @@ -2240,6 +2229,7 @@ self.p2.illegal = p2_illegal series.start() + game.start() # put cards back hand.draw_pile = list(hand.original_deck) @@ -2583,6 +2573,35 @@ 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