larry avatar larry committed fe59add

Wrote new scoring mechanism, not using it yet.

Comments (0)

Files changed (1)

 # todo:
 #
 #
-# prompt "are you sure?" when doing something redundant
-#	* playing hazard on a hazard
-#	* playing speed limit on a speed limit
+# 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
 #	  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
 #
 		top = playee.hand.normalized_battle_top()
 		if top is None:
 			self.illegal(", playee has not started rolling")
-
-		if (isinstance(top, Remedy)
+		elif (isinstance(top, Remedy)
 			and (top is not roll)
 			):
 			self.illegal(", can't play a hazard on a remedy (" + str(top) + ") (unless they've played the safety)")
+		elif isinstance(top, Hazard):
+			player.double_check(self, top)
+
 
 
 class Stop(Hazard):
 	def is_legal(self, player, playee):
 		self.is_legal_base(player, playee)
 
+		if self.on_discard(playee):
+			return
+
+		top = playee.hand.normalized_speed_top()
+		if isinstance(top, SpeedLimit):
+			player.double_check(self, top)
+
 
 class Remedy(Card):
 	def __init__(self):
 card_to_string.update({value: value for value in card_to_string.values()})
 card_to_int.update(   {value: value for value in card_to_int.values()})
 
+
+
+cardinal = "zero one two three four five".split().__getitem__
+
+class Score:
+
+	miles = 0
+	safeties = 0
+	coups_fourres = 0
+
+	trip_complete = False
+	delayed_action = False
+	safe_trip = False
+	shutout = False
+
+	def __init__(self, player, as_if_won=False):
+		self.mileage = player.hand.mileage()
+
+		self.safeties = len(player.hand.safeties)
+		self.coups_fourres = len(player.hand.coups_fourres)
+
+		if (
+			  as_if_won
+			  or ((mileage == 700) and (not hand.extended))
+			  or (mileage == 1000)
+			):
+			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()
+
+	def render(self):
+		array = []
+		def score(points, *a):
+			nonlocal array
+			if not points:
+				return
+			s = ' '.join(str(x) for x in a)
+			array.append(s.rjust(19) + ' ... ' + str(points).rjust(4))
+
+		score(self.miles, self.miles, "miles")
+
+		score(self.safeties * 100, cardinal(self.safeties), "safet" + ("y" if safeties == 1 else "ies"))
+		if safeties == 4:
+			score(300, "all four safeties")
+		score(self.coupes_fourres * 300, cardinal(self.coupes_fourres), "coup{s} fourré{s}".format(s="" if coupes == 1 else "s"))
+
+		if self.trip_complete:
+			score(400, "trip complete")
+			if self.miles == 1000:
+				score(200, "extension")
+			if self.delayed_action:
+				score(300, "delayed action")
+			if self.safe_trip:
+				score(300, "safe trip (no 200s)")
+			if self.shutout:
+				score(500, "shut-out")
+
+		return array
+
+	@property
+	def total(self):
+		return sum((x[0] for x in self.render()))
+
+	def __str__(self):
+		return "\n".join(self.render())
+
+	@property
+	def statistics(self):
+		return {
+			"miles": self.miles,
+			"0 safeties": self.safeties == 0,
+			"1 safeties": self.safeties == 1,
+			"2 safeties": self.safeties == 2,
+			"3 safeties": self.safeties == 3,
+			"4 safeties": self.safeties == 4,
+			"0 coups fourrés": self.coups_fourres == 0,
+			"1 coups fourrés": self.coups_fourres == 1,
+			"2 coups fourrés": self.coups_fourres == 2,
+			"3 coups fourrés": self.coups_fourres == 3,
+			"4 coups fourrés": self.coups_fourres == 4,
+			"trips_completed": self.trip_complete,
+			"extensions": self.trip_complete and (self.miles == 1000),
+			"delayed_actions": self.delayed_action,
+			"safe_trips": self.safe_trip,
+			"shutouts": self.shutout,
+			"perfect_games": self.total == 4600,
+		}
+		return { k: int(v) for k, v in d.items() }
+
+
 class Play:
 
 	def __init__(self, card, player, playee):
 		self.games_won = 0
 		self.hands_won = 0
 		self.highest_score = 0
-		self.shutouts = 0
 
 		self.drawn = CardCounter()
 		self.played = CardCounter()
 	def rolling(self):
 		return self.normalized_battle_top() == roll
 
+	def normalized_speed_top(self):
+		"""
+		Same idea as normalized_battle_top but for the speed pile.
+		"""
+		row = right_of_way in self.safeties
+		if (not self.speed_pile) or row:
+			return end_of_limit
+		return self.speed_pile[-1]
+
 	def autobahn(self):
 		row = right_of_way in self.safeties
 		if (not self.speed_pile) or row:
 				score(300, "delayed action")
 			if not self.two_hundreds():
 				score(300, "safe trip (no 200s)")
-			if not self.player.other().hand.mileage():
+			if not self.player.others[0].hand.mileage():
 				score(500, "shut-out")
 
 		safeties = len(self.safeties)
 			self.hand.unplayed.add(card)
 		self.update_statistics(self.hand, self.game)
 
-	def other(self):
-		return series.players[1 - int(series.players[1] == self)]
+	@property
+	def others(self):
+		l = list(series.players)
+		l.remove(self)
+		return l
 
 	def turn(self):
 		pass
 		card.played(player, self)
 		hand.plays.append(Play(card, player, self))
 
+	def double_check(self, card, top):
+		pass
 
 
 
 
 	name = "Rosey"
 
+	def double_check(self, card, top):
+		raise IllegalMove("du-uh, nevermind")
+
 	def turn(self):
 		self.hand.draw()
 		cards = list(self.hand.hand)
 		random.shuffle(cards)
 		for card in cards:
 			try:
-				if not isinstance(card, Hazard):
-					playee = self
-				else:
-					playee = self.other()
-					if card is speed_limit:
-						if not playee.hand.autobahn():
-							continue
-					else:
-						if not playee.hand.rolling():
-							continue
 				self.hand.play(card, playee)
 				print("    du-uh, played", card, "on", "herself" if playee == self else "you")
 				if self.hand.won() and self.hand.mileage() == 700:
 	def _decide_to_extend(self):
 		# shutout?  hot damn!
 		# don't chance the extend, take the points!
-		if not self.other().hand.mileage():
+		other = self.others[0]
+		if not other.hand.mileage():
 			return True
 
-		other_safeties = self.other().hand.safeties
+		other_safeties = other.hand.safeties
 		extend_if = (
 			# we have half or more of the cards remining in the draw pile
 			(len(hand.draw_pile) > (len(hand.original_deck) // 2))
 		
 		return None
 
+	def double_check(self, card, top):
+		raise RuntimeError("hmm, Norman should never do this")
+
 	def play(self, card, playee=None):
 		addendum = ""
 		if playee == series.discard:
 			verb = "discarding"
 		else:
 			verb = "playing"
-			if playee == self.other():
+			if playee == self.others[0]:
 				addendum = "on you"
 		print("    hmm,", verb, card, addendum)
 		self.hand.play(card, playee)
 					return True
 			return False
 
-		other = self.other()
+		other = self.others[0]
 
 		all_my_safeties = set(self.hand.safeties) | set((x for x in self.hand.hand if isinstance(x, Safety)))
 
 		if not remaining:
 			return 0
 
-		hand_size = len(self.other().hand.hand) + bool(hand.draw_pile and on_next_turn)
+		hand_size = len(self.others[0].hand.hand) + bool(hand.draw_pile and on_next_turn)
 		all_cards = len(hand.draw_pile) + hand_size + 1  # the + 1 fixes off-by-ones for range
 
 		# special case: if we only care about one card, the math is easy.
 
 	def update_scan(self):
 		hand.update_scan()
-		other = self.other
+		other = self.others[0]
 		discard = series.discard
 		for play in hand.plays[self.scanned:]:
 			if play.player is self:
 			self.maximums[roll] -= 1
 		return self.play_obsolete_safeties(card)
 
+	def double_check(self, card, top):
+		raise RuntimeError("ah!  Moriarty has made an error.")
+
 	def turn(self):
 		self.update_scan()
 
 				for player in series.players:
 					if playable:
 						break
-					other = player.other()
+					other = player.others[0]
 					for card in player.hand.hand:
 						playee = other if isinstance(card, Hazard) else player
 						try:
 			# go to next player
 			self.current_player = next(players)
 
-		if winner and (not winner.other().hand.mileage()):
+		if winner and (not winner.others[0].hand.mileage()):
 			winner.series.shutouts += 1
 
 		for p in series.players:
 				print(s1.ljust(40), s2)
 
 		print()
-		lost_by_points = scores[winner] < scores[winner.other()] if winner else False
+		lost_by_points = scores[winner] < scores[winner.others[0]] if winner else False
 		return self.ScoresResult(winner, lost_by_points)
 
 class TestBase(unittest.TestCase):
 			print("No hand currently being played.")
 			return
 
-		other = self.other()
+		other = self.others[0]
 
 		if hand.plays and hand.plays[-1].playee is series.discard:
 			discard_before = ">"
 		add = text.append
 		if winner:
 			add(winner.name + " wins the hand!")
-			lost_by_points = winner.hand.calculate_score() < winner.other().hand.calculate_score()
+			loser = winner.others[0]
+			lost_by_points = winner.hand.calculate_score() < loser.hand.calculate_score()
 			if lost_by_points:
-				add(" Which is weird, because " + winner.other().name + " got more points.")
-			if not winner.other().hand.mileage():
+				add("(Which is weird, because " + loser.name + " got more points.)")
+			if not loser.hand.mileage():
 				add("And it's a shutout, #{} for {}!".format(winner.series.shutouts, winner.name))
 		else:
 			add("Nobody won the hand.")
 			add("{} lifetime games won: {} total score: {}".format(game_player.name, p.series.games_won, p.series.score))
 		self.space_to_continue("\n".join(text))
 
+	def double_check(self, card, top):
+		c = self.getch("You're playing a {} on a {}.\nAre you sure that's what you want?  [yn] >".format(str(card), str(top)), "yn")
+		if c == 'n':
+			raise IllegalMove("I thought as much!")
+
 	def turn(self):
-		other = self.other()
+		other = self.others[0]
 
 		while True:
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.