# mille

Wrote new scoring mechanism, not using it yet.

# mille.py

` # 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.