Commits

larry committed 3e55fba

Redid UI so now you can see anything at any time. Fixed deck-empty oo loop.

Comments (0)

Files changed (1)

 		return len([card for card in self.mileage_pile if card.distance == 200])
 
 	ScoreTuple = namedtuple("ScoreTuple", "score details")
-	def score(self):
+	def score(self, as_if_won=False):
 		array = []
 		total = 0
 		def score(points, *a):
 			array.append(s.rjust(19) + ' ... ' + str(points).rjust(4))
 
 		score(self.mileage(), str(self.mileage()), "miles")
-		if total in {700, 1000}:
-			if total == 1000:
+		if as_if_won or (total in {700, 1000}):
+			if total > 700:
 				score(200, "extension")
 			score(400, "trip complete")
 			if not len(self.hand.deck):
 		self.round = 0
 		self.state = None
 		self.winner = None
+		self.over = False
 
 	def add_player(self, player):
 		self.players.append(player)
 
-	attributes = "round state winner".split()
+	attributes = "over round state winner".split()
 
 	def __getstate__(self):
 		d = {
 		if self.hand:
 			self.hand.game = self
 		for attr in self.attributes:
-			setattr(self, attr, state[attr])
+			setattr(self, attr, state.get(attr))
 
 	def unpickle(self):
 		if self.hand:
 
 		while True:
 			if self.state == "hand end":
-				winner = unpickle_player(self.winner)
-
-				for player in self.original_players:
-					player.hand_end(winner)
-
-				self.round += 1
-				self.hand = None
-
-				self.state = None
-				self.winner = None
-
+				hand_winner = unpickle_player(self.winner)
+
+				game_winner = None
 				if any((p for p in self.players if p.total_score >= 5000)):
 					p1, p2 = self.players
 					if p1.total_score > p2.total_score:
-						return p1
+						game_winner = p1
 					if p1.total_score < p2.total_score:
-						return p2
+						game_winner = p2
 					# a tie game! play another hand!
-					continue
+
+				if game_winner:
+					self.over = True
+
+				for player in self.original_players:
+					player.hand_end(hand_winner)
+
+				self.round += 1
+				self.hand = None
+
+				self.state = None
+				self.winner = None
+
+				if game_winner:
+					return game_winner
 
 				continue
 
 
 			player = self.current_player
 
-			if not player.cards:
-				continue
-			check_cards(player, allow_seven)
-			print()
-			print("[" + player.name + "]")
-			plays = len(self.plays)
-			cards = len(self.deck)
-			while player.cards:
-				player.turn()
-				if isinstance(self.plays[-1].card, Safety):
-					continue
-				break
-
-			# sanity check:
-			# until we've run out of cards, on every turn
-			#	* the player should draw and play at least one card
-			#	* there should be a record of each play
-			#   * the last card played should not be a safety
-			#	* all other cards played should be safeties
-			cards_drawn = cards - len(self.deck)
-			plays_taken = len(self.plays) - plays
-			if self.deck and not allow_seven:
-				assert cards_drawn
-				assert cards_drawn == plays_taken
-				for play in self.plays[-plays_taken:-1]:
-					assert isinstance(play.card, Safety)
-				assert not isinstance(self.plays[-1].card, Safety)
-			allow_seven = False
-
-			if player.won():
-				winner = player
-				break
-			check_cards(player)
+			if player.cards:
+				check_cards(player, allow_seven)
+				print()
+				print("[" + player.name + "]")
+				plays = len(self.plays)
+				cards = len(self.deck)
+				while player.cards:
+					player.turn()
+					if isinstance(self.plays[-1].card, Safety):
+						continue
+					break
+
+				# sanity check:
+				# until we've run out of cards, on every turn
+				#	* the player should draw and play at least one card
+				#	* there should be a record of each play
+				#   * the last card played should not be a safety
+				#	* all other cards played should be safeties
+				cards_drawn = cards - len(self.deck)
+				plays_taken = len(self.plays) - plays
+				if self.deck and not allow_seven:
+					assert cards_drawn
+					assert cards_drawn == plays_taken
+					for play in self.plays[-plays_taken:-1]:
+						assert isinstance(play.card, Safety)
+					assert not isinstance(self.plays[-1].card, Safety)
+				allow_seven = False
+
+				if player.won():
+					winner = player
+					break
+				check_cards(player)
 
 			# go to next player
 			self.current_player = next(players)
 			p.total_score += p.score().score
 
 	ScoresResult = namedtuple("ScoresResult", "winner lost_by_points")
-	def print_scores(self):
+	def print_scores(self, as_if_won=True):
 		scores = {}
 		all_comments = []
 		all_totals = []
 		winner = None
 		divider = "--------------------------------"
 		for p in self.game.original_players:
-			score, comments = p.score()
+			score, comments = p.score(as_if_won)
 			comments = ["    " + x for x in comments]
 			comments.insert(0, divider)
 			comments.insert(0, p.name)
 
 import sys, tty, termios
 
-def getch(prompt=None, valid=None):
+def getch(prompt, valid=None):
 	sys.stdout.flush()
 	fd = sys.stdin.fileno()
 	while True:
 		array.append(s)
 	return ' '.join(array)
 
+def help():
+	print("""
+Keys you can press at any time:
+
+      ! - show all cards visible (on tableau or discarded)
+      s - show current scores
+      S - show current scores as if each player had won the current hand
+      t - redraw the table (discard, piles, etc)
+      ? - display this help
+
+ Ctrl-C - exit, automatically saving state
+
+ At the "discard which card?" prompt, press any invalid key to return
+ to the main play prompt.
+""")
+
+def print_scores(as_if_won=False):
+	global series
+
+	if not (series.game and series.game.hand):
+		print("No current hand being played.")
+		return
+
+	game = series.game
+	game.hand.print_scores(as_if_won)
+
 def played_so_far():
 	global series
+
+	if not (series.game and series.game.hand):
+		print("No current hand being played.")
+		return
+
 	game = series.game
 	game.hand.update_seen()
 
 		self.last_drawn = string_to_card.get(state["last_drawn"])
 		self.state = state["state"]
 
-	def space_to_continue(self):
+	def getch(self, prompt, keys=None):
+		if keys:
+			keys += "!?sSt"
+		global getch
+		while True:
+			c = getch(prompt, keys)
+			if c == '!':
+				played_so_far()
+				continue
+			if c == '?':
+				help()
+				continue
+			if c == 's':
+				print_scores()
+				continue
+			if c == 'S':
+				print_scores(True)
+				continue
+			if c == 't':
+				self.print_table()
+				continue
+			return c
+
+	def space_to_continue(self, prompt=""):
+		if prompt:
+			prompt += "\n"
+		prompt += "Press [space] to continue > "
+		self.getch(prompt, " ")
+
+	def print_table(self):
+		if not (series.game and series.game.hand):
+			print("No hand currently being played.")
+			return
+
+		hand = self.hand
+		game = self.game
+		other = self.other()
+
+		if self.hand.plays and self.hand.plays[-1].playee is self.hand.discard:
+			discard_before = ">"
+			discard_after = "<"
+		else:
+			discard_before = discard_after = "|"
+		print("deck", str(len(hand.deck)).rjust(3) + "/" + str(len(hand.original_deck)), "       discard " + discard_before + pile_repr(hand.discard.pile) + discard_after)
+		print("         total|hand    miles|battlepile|speed pile|safeties")
+		def stats(player):
+			before_miles = " "
+			before_battle = between_piles = after_speed = "|"
+			highlights = set()
+			if self.hand.plays:
+				play = self.hand.plays[-1]
+				last_player = play.player
+				card = play.card
+				if play.playee == player:
+					if isinstance(play.card, Mileage):
+						before_miles = ">"
+						before_battle = "<"
+					elif play.card in {speed_limit, end_of_limit}:
+						between_piles = ">"
+						after_speed = "<"
+					elif isinstance(play.card, (Hazard, Remedy)):
+						before_battle = ">"
+						between_piles = "<"
+				for play in reversed(self.hand.plays):
+					if play.player != last_player:
+						break
+					if isinstance(play.card, Safety):
+						highlights.add(play.card)
+
+			strings = [player.name.ljust(8)[:8]]
+			add = strings.append
+
+			score = player.score()[0]
+
+			add(" ")
+			add(str(score + player.total_score).rjust(5))
+
+			add("|")
+			add(str(score).rjust(4))
+
+			add("   ")
+			add(before_miles)
+			add(str(player.mileage()).rjust(4))
+
+			add(" .:"[player.two_hundreds()])
+
+			add(before_battle)
+			add(pile_repr(player.battle_pile))
+
+			add(between_piles)
+			add(pile_repr(player.speed_pile))
+
+			add(after_speed)
+			add(safeties_repr(player, highlights))
+
+			print("".join(strings))
+
+		stats(other)
+		stats(self)
+
+		highlight = self.last_drawn
+
+		highlight_line = []
+		line1 = []
+		line2 = []
+
+		before = " "
+		after = ""
+
+		card_highlight = ":"
+
+		for i, card in enumerate(self.cards, 1):
+
+			if card == highlight:
+				before = after = card_highlight
+				highlight_line.append(card_highlight * 10)
+				highlight = None
+			else:
+				highlight_line.append(before)
+				highlight_line.append(" " * 9)
+
+			field1 = ("[" + str(i) + "]").rjust(9)
+			field2 = (repr(card)[:9]).rjust(9)
+
+			line1.append(before)
+			line1.append(field1)
+			line2.append(before)
+			line2.append(field2)
+
+			before = after or " "
+			after = ""
+
+		if before:
+			line1.append(before)
+			line2.append(before)
+
 		print()
-		getch("Press [space] to continue > ", " ")
+		print(" ", ''.join(highlight_line))
+		print(" ", ''.join(line1))
+		print(" ", ''.join(line2))
+		print(" ", ''.join(highlight_line))
 
 	def hand_end(self, winner):
 		series.game.hand.print_scores()
+		text = []
+		add = text.append
 		if winner:
-			extra = ""
+			add(winner.name + " wins the hand!")
 			lost_by_points = winner.score() < winner.other().score()
 			if lost_by_points:
-				extra = " Which is weird, because " + winner.other().name + " got more points."
-			print(winner.name, "wins the hand!", extra)
+				add(" Which is weird, because " + winner.other().name + " got more points.")
 			if not winner.other().mileage():
-				print("And it's a shutout, #{} for {}!".format(series.players[series.game.players.index(winner)].shutouts, winner.name))
+				add("And it's a shutout, #{} for {}!".format(series.players[series.game.players.index(winner)].shutouts, winner.name))
 		else:
-			print("Nobody won the hand.")
-
-		self.space_to_continue()
+			add("Nobody won the hand.")
+
+		text = "\n".join(text)
+		if series.game.over:
+			print(text)
+		else:
+			self.space_to_continue(text)
 
 	def game_end(self, winner):
+		text = []
+		add = text.append
 		if not winner:
-			print("Game is a tie!  Golly, that's unusual!")
+			add("Game is a tie!  Golly, that's unusual!")
 		else:
 			screed = """
 Gentlemen!  You've both worked very hard.  And in a way you're both winners.
 But--in another, more accurate way--{winner.name} is the winner.
 			""".format(winner=winner)
-			print(screed)
+			add(screed)
 		for p, game_player in zip(series.players, series.game.players):
-			print(game_player.name, "lifetime games won: {} total score: {}".format(p.games_won, p.total_score))
-		self.space_to_continue()
+			add("{} lifetime games won: {} total score: {}".format(game_player.name, p.games_won, p.total_score))
+		self.space_to_continue("\n".join(text))
 
 	def turn(self):
 		hand = self.hand
 		while True:
 
 			if self.state == "prompt for extend":
-				c = getch("    You reached 700!  Extend? >", "yn!")
-				if c == '!':
-					played_so_far()
-					continue
+				c = self.getch("    You reached 700!  Extend? > ", "yn")
 				hand.extended = c == "y"
 				self.state = None
 				return
 
+			redraw = True
 			while self.cards:
-				if self.hand.plays and self.hand.plays[-1].playee is self.hand.discard:
-					discard_before = ">"
-					discard_after = "<"
-				else:
-					discard_before = discard_after = "|"
-				print("deck", str(len(hand.deck)).rjust(3) + "/" + str(len(hand.original_deck)), "       discard " + discard_before + pile_repr(hand.discard.pile) + discard_after)
-				print("         total|hand    miles|battlepile|speed pile|safeties")
-				def stats(player):
-					before_miles = " "
-					before_battle = between_piles = after_speed = "|"
-					highlights = set()
-					if self.hand.plays:
-						play = self.hand.plays[-1]
-						last_player = play.player
-						card = play.card
-						if play.playee == player:
-							if isinstance(play.card, Mileage):
-								before_miles = ">"
-								before_battle = "<"
-							elif play.card in {speed_limit, end_of_limit}:
-								between_piles = ">"
-								after_speed = "<"
-							elif isinstance(play.card, (Hazard, Remedy)):
-								before_battle = ">"
-								between_piles = "<"
-						for play in reversed(self.hand.plays):
-							if play.player != last_player:
-								break
-							if isinstance(play.card, Safety):
-								highlights.add(play.card)
-
-					strings = [player.name.ljust(8)[:8]]
-					add = strings.append
-
-					score = player.score()[0]
-
-					add(" ")
-					add(str(score + player.total_score).rjust(5))
-
-					add("|")
-					add(str(score).rjust(4))
-
-					add("   ")
-					add(before_miles)
-					add(str(player.mileage()).rjust(4))
-
-					add(" .:"[player.two_hundreds()])
-
-					add(before_battle)
-					add(pile_repr(player.battle_pile))
-
-					add(between_piles)
-					add(pile_repr(player.speed_pile))
-
-					add(after_speed)
-					add(safeties_repr(player, highlights))
-
-					print("".join(strings))
-
-				stats(other)
-				stats(self)
-
-				highlight = self.last_drawn
-				
-				highlight_line = []
-				line1 = []
-				line2 = []
-
-				for i, card in enumerate(self.cards, 1):
-
-					if card == highlight:
-						highlight_line.append(" ")
-						highlight_line.append("/" * 9)
-						highlight = None
-					else:
-						highlight_line.append(" " * 10)
-
-					field1 = ("[" + str(i) + "]").rjust(10)
-					field2 = (repr(card)[:9]).rjust(10)
-
-					line1.append(field1)
-					line2.append(field2)
-
-				print()
-				print(" ", ''.join(highlight_line))
-				print(" ", ''.join(line1))
-				print(" ", ''.join(line2))
-				print(" ", ''.join(highlight_line))
+				if redraw:
+					self.print_table()
+					redraw = False
 
 				c = None
 				valid_cards = "1234567"[:len(self.cards)]
-				valid = valid_cards + '!' + chr(27)
-				prompt = "    play [1-" + str(len(self.cards)) + "]"
+				valid = valid_cards
+				prompt = "    play [" + valid + "]"
 
 				if hand.deck and (len(self.cards) < 7):
-					prompt += " or [d]raw?> "
+					prompt += " or [d]raw? > "
 					valid += "d"
 				else:
-					prompt += " or [.]discard?> "
+					prompt += " or [.]discard? > "
 					valid += "."
-				c = getch(prompt, valid)
-
-				if c == chr(27):
-					sys.exit("ESC")
-				if c == '!':
-					played_so_far()
-					continue
+				c = self.getch(prompt, valid)
+
 				if c == 'd':
 					card = hand.deck.pop()
 					self.cards.append(card)
 					print()
 					print("    drew", card)
 					print()
+					redraw = True
 					continue
 				if c == '.':
-					c2 = getch("    Discard which card? " + valid_cards + " >")
+					c2 = self.getch("    Discard which card? [" + valid_cards + "] > ")
 					if c2 not in valid_cards:
 						continue
 					i = int(c2) - 1