Index: main.py
===================================================================
--- main.py	(revision 7)
+++ main.py	(revision 6.1.60)
@@ -1,2 +1,4 @@
+#!/usr/bin/python3
+
 from random import shuffle
 
@@ -32,4 +34,141 @@
     else:
         return 1
+
+
+class Shared(object):
+
+    def __init__(self, player_count, players, role_deck):
+        self.player_count = player_count
+        self.players = players
+        self.role_deck = role_deck
+        self.mission = 0
+        self.captain = 0
+        self.team_failures = 0
+        self.wins = 0
+        self.losses = 0
+
+    def current_mission_size(self):
+        team_size = mission_size(self.player_count, self.mission)
+        print("Mission {} needs {} members".format(self.mission, team_size))
+        return team_size
+
+    def next_mission(self):
+        self.mission += 1
+
+    def get_proposed_team(self, team_size):
+        print("Captain is {}".format(self.captain))
+        team = self.players[self.captain].propose_team(team_size)
+        print("He proposed {}".format(team))
+        return team
+
+    def next_team_captain(self):
+        self.captain = (self.captain + 1) % self.player_count
+
+    def reset_team_failures(self):
+        self.team_failures = 0
+
+    def record_team_failure(self):
+        self.team_failures += 1
+
+    def get_mission_failures(self, team):
+        failures = 0
+        for i in team:
+            if self.role_deck[i] == "SPY":
+                if not self.players[i].perform_mission():
+                    failures = failures + 1
+        return failures
+
+    def required_failures(self):
+        return required_failures(self.player_count, self.mission)
+
+    def mission_succeeded(self):
+        print("Mission succeeded")
+        self.wins += 1
+
+    def mission_failed(self, failures):
+        print("Mission had {} failures".format(failures))
+        self.losses += 1
+
+    def game_over(self):
+        if self.team_failures == 5:
+            print("The spies won!")
+            for p in self.players:
+                p.observe_game(False)
+            return True
+        elif self.wins >= 3:
+            print("The Resistance won!")
+            for p in self.players:
+                p.observe_game(True)
+            return True
+        elif self.losses >= 3:
+            print("The Spies won!")
+            for p in self.players:
+                p.observe_game(False)
+            return True
+        else:
+            return False
+
+
+class AdvanceMission(object):
+
+    def __init__(self, shared):
+        self.shared = shared
+
+    def do(self):
+        self.shared.next_mission()
+        team_size = self.shared.current_mission_size()
+        return AssembleTeam(self.shared, team_size)
+
+
+class AssembleTeam(object):
+
+    def __init__(self, shared, team_size):
+        self.shared = shared
+        self.team_size = team_size
+
+    def do(self):
+        team = self.shared.get_proposed_team(self.team_size)
+
+        votes = [p.approve_team(team) for p in self.shared.players]
+        approvals = sum(votes)
+        approved = approvals > self.shared.player_count / 2
+
+        print("Team {}: {} for, {} against".format(
+            {True: "approved", False: "rejected"}[approved],
+            approvals, self.shared.player_count - approvals
+        ))
+
+        for p in self.shared.players:
+            p.observe_team_vote(approved, votes)
+
+        self.shared.next_team_captain()
+
+        if approved:
+            self.shared.reset_team_failures()
+            return PerformMission(self.shared, team)
+        else:
+            self.shared.record_team_failure()
+            return AssembleTeam(self.shared, self.team_size)
+
+
+class PerformMission(object):
+
+    def __init__(self, shared, team):
+        self.shared = shared
+        self.team = team
+
+    def do(self):
+        failures = self.shared.get_mission_failures(self.team)
+        success = failures < self.shared.required_failures()
+
+        for p in self.shared.players:
+            p.observe_mission(success, failures)
+
+        if success:
+            self.shared.mission_succeeded()
+        else:
+            self.shared.mission_failed(failures)
+
+        return AdvanceMission(self.shared)
 
 
@@ -51,65 +190,9 @@
             players[i].reveal_spies(spy_nums)
 
-    wins = 0
-    losses = 0
-    captain = 0
-    team_failures = 0
-
-    for mission in range(1, 6):
-        team_size = mission_size(player_count, mission)
-        print("Mission {} needs {} members".format(mission, team_size))
-        for attempt in range(5):
-            print("Captain is {}".format(captain))
-            team = players[captain].propose_team(team_size)
-            print("He proposed {}".format(team))
-            votes = [p.approve_team(team) for p in players]
-            approvals = sum(votes)
-            approved = approvals > player_count / 2
-            print("Team {}: {} for, {} against".format(
-                {True: "approved", False: "rejected"}[approved],
-                approvals, player_count - approvals
-            ))
-
-            for p in players:
-                p.observe_team_vote(approved, votes)
-
-            captain = (captain + 1) % player_count
-            if approved:
-                team_failures = 0
-                failures = 0
-                for i in team:
-                    if role_deck[i] == "SPY":
-                        if not players[i].perform_mission():
-                            failures = failures + 1
-                success = failures < required_failures(player_count, mission)
-
-                for p in players:
-                    p.observe_mission(success, failures)
-
-                if success:
-                    print("Mission succeeded")
-                    wins += 1
-                    if wins >= 3:
-                        print("The Resistance won!")
-                        for p in players:
-                            p.observe_game(True)
-                        return
-                else:
-                    print("Mission had {} failures".format(failures))
-                    losses += 1
-                    if losses >= 3:
-                        print("The Spies won!")
-                        for p in players:
-                            p.observe_game(False)
-                        return
-
-                break
-            else:
-                team_failures += 1
-                if team_failures == 5:
-                    print("The spies won!")
-                    for p in players:
-                        p.observe_game(False)
-                    return
+    shared = Shared(player_count, players, role_deck)
+    state = AdvanceMission(shared)
+
+    while not shared.game_over():
+        state = state.do()
 
 
