Commits

larry committed 5fb5b3b

Norman needed to reset his anxiety counter at the start of every hand.
Also, retooled the display a little more; safeties get more characters.

Comments (0)

Files changed (1)

 #
 # todo:
 #
+# 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.
+#
 # smart computer player
 #	* calculates odds on whether a card is still in the deck / in opponent's hand
 #		* if both hazards have been seen, discard the remedy
 #  * reimport when you start up
 #
 
-import collections
+from collections import Counter, namedtuple
 import getpass
 import itertools
 import random
 	def pile(self, playee):
 		return playee.safeties
 
-	def __init__(self):
+	def __init__(self, abbreviation):
 		super().__init__()
+		self.abbreviation = abbreviation
 		self.categories = self.__class__.__bases__[1:]
 
 	def is_coup_fouree(self, playee):
 		super().played(player, playee)
 
  
-right_of_way   = type("RightOfWay",    (Safety,), {})()
-puncture_proof = type("PunctureProof", (Safety,), {})()
-extra_tank     = type("ExtraTank",     (Safety,), {})()
-driving_ace    = type("DrivingAce",    (Safety,), {})()
+right_of_way   = type("RightOfWay",    (Safety,), {})("ROfWay")
+puncture_proof = type("PunctureProof", (Safety,), {})("PProof")
+extra_tank     = type("ExtraTank",     (Safety,), {})("ExTank")
+driving_ace    = type("DrivingAce",    (Safety,), {})("DriAce")
 
 speed_limit    = SpeedLimit(right_of_way)
 stop           = type("Stop",          (Hazard,), {})(right_of_way)
 	def __repr__(self):
 		return "".join(("<", self.__class__.__name__ ," ", self.name, ">"))
 
-	def start_hand(self, hand):
+	def hand_start(self, hand):
 		self.hand = hand
 
 	def is_legal(self, card, playee=None):
 	def turn(self):
 		pass
 	
-	def hand_over(self):
+	def hand_end(self):
 		pass
 
-Play = collections.namedtuple('Play', 'card player playee')
+Play = namedtuple('Play', 'card player playee')
 
 
 class Discard(PlayerBase):
 
-	def start_hand(self, hand):
-		super().start_hand(hand)
+	def hand_start(self, hand):
+		super().hand_start(hand)
 		self.pile = []
 
 	def __init__(self):
 
 class Player(PlayerBase):
 
-	def start_hand(self, hand):
-		super().start_hand(hand)
+	def hand_start(self, hand):
+		super().hand_start(hand)
 		self.cards = []
 		self.mileage_pile = []
 		self.battle_pile = []
 	def two_hundreds(self):
 		return len([card for card in self.mileage_pile if card.distance == 200])
 
+	ScoreTuple = namedtuple("ScoreTuple", "score details")
 	def score(self):
 		array = []
 		total = 0
 			count = len(self.coup_fourres)
 			if count:
 					score(count * 300, count, "coup{s} fourré{s}".format(s="" if count == 1 else "s"))
-		return (total, array)
+		return self.ScoreTuple(total, array)
 
 
 class DumbComputerPlayer(Player):
 
 	def __init__(self, game, *args, **kwargs):
 		super().__init__(game, "Norman", *args, **kwargs)
-		self.safety_countdown = 0
-		self.play_safety = False
 		def print_fn(*a):
 			print("    hmm,", *a)
 		self.print = print_fn
 			pass
 		# self.debug = silent
 
+	def hand_start(self, hand):
+		super().hand_start(hand)
+		self.safety_countdown = 0
+		self.play_safety = False
+
 	def check_for_coup_fouree(self, debug):
 		if not self.hand.plays:
 			return False
 			if not cards:
 				return False
 			random.shuffle(cards)
-			for card, count in collections.Counter(cards).items():
+			for card, count in Counter(cards).items():
 				if count >= 2:
 					debug(debug_step)
 					self.discard(card)
 		debug("no possible plays.")
 
 
+class SmartComputerPlayer(Player):
+	"""
+	Moriarty is as smart as I can make him.
+	His main achievement over Norman is that he counts cards
+	and calculates odds.
+	"""
+
+	def __init__(self, game):
+		super().__init__(game, "Moriarty")
+
+	def hand_start(self, hand):
+		super().hand_start(hand)
+		self.scanned = 0
+		self.discards = set()
+
+	def update_scan(self):
+		for play in self.hand.plays[self.scanned:]:
+			if play.playee is self.hand.discard:
+				self.discards.add(play.card)
+		self.scanned = len(self.hand.plays)
+
+	def odds_that_opponent_has_card(self, card, on_next_turn=True):
+		# If there are D cards in the deck,
+		# and the opponent has C cards in their hand (normally 5),
+		# and there are N of this particular card yet unplayed,
+		# then the odds that the opponent has at least one of that card are
+		#   1 - 
+		pass
+
+
 class Game:
 	def __init__(self):
 		self.players = []
 	def play(self):
 		round = 1
 		self.original_players = list(self.players)
+
 		while True:
 			print("Hand #" + str(round) + ",", self.players[0].name, "goes first.")
 			round += 1
 			if any((p for p in self.players if p.total_score >= 5000)):
 				break
 			for player in self.original_players:
-				player.hand_over()
+				player.hand_end()
 
 			# alternate who starts first
 			self.players = self.players[1:] + [self.players[0]]
 		random.shuffle(self.deck)
 
 		for player in self.game.players:
-			player.start_hand(self)
+			player.hand_start(self)
 			player.cards = self.deck[0:5]
 			player.cards.sort(reverse=True)
 			del self.deck[0:5]
 		
-		self.discard.start_hand(self)
-
-			# player.mileage_pile = [mileage_100]*6
+		self.discard.hand_start(self)
 
 		self.plays = []
 
+		self.plays_scanned = 0
+		self.cards_seen = Counter()
+		self.cards_total = Counter(self.original_deck)
+
+	def update_seen(self):
+		for play in self.plays[self.plays_scanned:]:
+			self.cards_seen[play.card] += 1
+		self.plays_scanned = len(self.plays)
+
 	def play(self):
 		p1, p2 = self.game.players
 
 			check_cards(player)
 
 		winner, lost_by_points = self.print_scores()
+		for p in self.game.original_players:
+			p.total_score += p.score().score
+
 		if winner:
 			extra = ""
 			if lost_by_points:
 		else:
 			print("Nobody won the hand.")
 
+	ScoresResult = namedtuple("ScoresResult", "winner lost_by_points")
 	def print_scores(self):
 		scores = {}
 		all_comments = []
 			totals = []
 			totals.append("  hand total " + str(score))
 			scores[p] = score
-			p.total_score += score
-			totals.append("  game total " + str(p.total_score))
+			totals.append("  game total " + str(p.total_score + score))
 			all_comments.append(comments)
 			all_totals.append(totals)
 			if p.won():
 
 		print()
 		lost_by_points = scores[winner] < scores[winner.other()] if winner else False
-		return winner, lost_by_points
+		return self.ScoresResult(winner, lost_by_points)
 
 class TestBase(unittest.TestCase):
 
 def safeties_repr(player):
 	array = []
 	for card in sorted(player.safeties, reverse=True):
-		s = "".join([x for x in repr(card) if x.isupper()])
-		if card not in player.coup_fourres:
-			s = s.lower()
+		s = card.abbreviation
+		if card in player.coup_fourres:
+			s = s.upper()
 		array.append(s)
 	return ' '.join(array)
 
 def played_so_far():
-	cards = list(game.hand.discard.pile)
-	for p in game.players:
-		cards.extend(p.battle_pile)
-		cards.extend(p.speed_pile)
-		cards.extend(p.mileage_pile)
-		cards.extend(list(p.safeties))
+	game.hand.update_seen()
 
 	print()
 
 		):
 		result = []
 		for card in line:
-			count = len([x for x in cards if x == card])
-			total = len([x for x in game.hand.original_deck if x == card])
+			count = game.hand.cards_seen[card]
+			total = game.hand.cards_total[card]
 			s = repr(card).rjust(width) + " " + str(count).rjust(2)
 			if not isinstance(card, Safety):
 				s += "/" + str(total).ljust(2)
 	def keep_playing(self):
 		return yes_no("Keep playing?")
 
-	def hand_over(self):
+	def hand_end(self):
 		print()
 		print()
 		getch("Press [space] to continue > ", " ")
 		other = self.other()
 
 		while self.cards:
-			print("    deck", str(len(hand.deck)).rjust(3) + "/" + str(len(hand.original_deck)), "   discard |" + pile_repr(hand.discard.pile) + "|")
-			print("             total|hand     miles|battlepile|speed pile|safeties")
+			print("deck", str(len(hand.deck)).rjust(3) + "/" + str(len(hand.original_deck)), "       discard |" + pile_repr(hand.discard.pile) + "|")
+			print("         total|hand    miles|battlepile|speed pile|safeties")
 			def stats(player):
 				strings = [player.name.ljust(8)[:8]]
 				add = strings.append
 				add("|")
 				add(str(score).rjust(4))
 
-				add("     ")
+				add("    ")
 				add(str(player.mileage()).rjust(4))
 
 				add(" .:"[player.two_hundreds()])
 				add("|")
 				add(safeties_repr(player))
 
-				print("   ", "".join(strings))
+				print("".join(strings))
 
 			stats(other)
 			stats(self)