Remove player_mode from game state.
[naja.git] / naja / gameboard.py
index 9855546c905a0a96019ecd60861132400cbbe7b6..0c0d922c959899cf809b45ca483e9137ffe5f763 100644 (file)
@@ -2,11 +2,12 @@ from random import choice
 
 from naja.constants import(
     BITS, DIRECTION_BITS, CONDITION_BITS, PLAYER_DEFAULTS,
-    ACT, EXAMINE, ROTATION)
+    ROTATION)
 from naja.options import options
 from naja.player import Player
 from naja import actions
 from naja.sound import sound
+from naja.utils import parse_bits
 import random
 
 
@@ -24,7 +25,6 @@ class GameBoard(object):
         self.puzzle = state.get('puzzle', False)
         self.player = player
         self.board_locations = board_locations
-        self.player_mode = state.get('player_mode', EXAMINE)
         self.has_cheated = state.get('cheater', options.cheat_enabled)
         self.clock_count = state.get('clock_count', 0)
         self.replacement_params = state.get('replacement_params', None)
@@ -40,14 +40,11 @@ class GameBoard(object):
             'wins_required': PLAYER_DEFAULTS.WINS_REQUIRED,
         }
 
-        puzzle = deck.get('puzzle', False)
-
-        if puzzle:
-            puzzle_defaults = deck.get('defaults', {})
-            for k, v in puzzle_defaults.iteritems():
-                if isinstance(v, list):
-                    puzzle_defaults[k] = tuple(v)
-            defaults.update(puzzle_defaults)
+        deck_defaults = deck.get('defaults', {})
+        for k, v in deck_defaults.iteritems():
+            if isinstance(v, list):
+                deck_defaults[k] = tuple(v)
+        defaults.update(deck_defaults)
 
         if initial_bits is None:
             initial_bits = defaults['initial_bits']
@@ -58,6 +55,8 @@ class GameBoard(object):
         if wins_required is None:
             wins_required = defaults['wins_required']
 
+        assert wins_required + max_health == 8
+
         # Overriden by command line
         if options.initial_bits:
             initial_bits = options.initial_bits
@@ -68,7 +67,7 @@ class GameBoard(object):
             'wins_required': wins_required,
             'wins': 0,
             'locations': deck['cards'],
-            'puzzle': puzzle,
+            'puzzle': deck.get('puzzle', False),
             'clock_count': 0,
             'replacement_params': deck.get('replacement_params', None),
         }
@@ -85,7 +84,9 @@ class GameBoard(object):
         player = Player.import_player(state.pop('player'))
         board_locations = cls.import_board_locations(
             state.pop('board_locations'))
-        return cls(state, player, board_locations)
+        board = cls(state, player, board_locations)
+        player.set_gameboard(board)
+        return board
 
     def export(self):
         data = {
@@ -97,7 +98,6 @@ class GameBoard(object):
             'puzzle': self.puzzle,
             'player': self.player.export(),
             'board_locations': self.export_board_locations(),
-            'player_mode': self.player_mode,
             'clock_count': self.clock_count,
             'replacement_params': self.replacement_params,
         }
@@ -186,7 +186,7 @@ class GameBoard(object):
         # Find which cards are at their maximum and exclude them from
         # the choice list
         counts = {}
-        choices = {card['card_name']: card for card in cards}
+        choices = dict((card['card_name'], card) for card in cards)
         for pos, card in board_locations:
             if pos == position:
                 # skip the card we're replacing if appropriate
@@ -204,30 +204,34 @@ class GameBoard(object):
                     del choices[key]
         return choice(choices.values())
 
-    def shift_location_row(self, change, is_vertical):
+    def shift_location_row(self, change, is_vertical, skip_player=True):
         px, py = self.player.position
         shifted_locations = {}
         mkpos = lambda i: (px, i) if is_vertical else (i, py)
 
         for i in range(5):
-            if (px, py) == mkpos(i):
+            if skip_player and (px, py) == mkpos(i):
                 continue
             new_i = (i + change) % 5
-            if (px, py) == mkpos(new_i):
+            if skip_player and (px, py) == mkpos(new_i):
                 new_i = (new_i + change) % 5
             shifted_locations[mkpos(new_i)] = self.board_locations[mkpos(i)]
 
         self.board_locations.update(shifted_locations)
 
-    def shift_locations(self, direction):
+    def shift_locations(self, direction, skip_player=True):
         if BITS[direction] == BITS.NORTH:
-            self.shift_location_row(-1, is_vertical=True)
+            self.shift_location_row(-1, is_vertical=True,
+                                    skip_player=skip_player)
         elif BITS[direction] == BITS.SOUTH:
-            self.shift_location_row(1, is_vertical=True)
+            self.shift_location_row(1, is_vertical=True,
+                                    skip_player=skip_player)
         elif BITS[direction] == BITS.EAST:
-            self.shift_location_row(1, is_vertical=False)
+            self.shift_location_row(1, is_vertical=False,
+                                    skip_player=skip_player)
         elif BITS[direction] == BITS.WEST:
-            self.shift_location_row(-1, is_vertical=False)
+            self.shift_location_row(-1, is_vertical=False,
+                                    skip_player=skip_player)
 
     def rotate_locations(self, direction):
         px, py = self.player.position
@@ -262,18 +266,6 @@ class GameBoard(object):
     def allow_chess_move(self, chesspiece):
         self.player.allow_chess_move(chesspiece)
 
-    def change_mode(self, new_mode):
-        """Advance to the next mode"""
-        if new_mode == self.player_mode:
-            raise RuntimeError("Inconsistent state. Setting mode %s to itself"
-                               % self.player_mode)
-        elif new_mode in (ACT, EXAMINE):
-            self.player_mode = new_mode
-            if new_mode is EXAMINE:
-                self.board_update()
-        else:
-            raise RuntimeError("Illegal player mode %s" % self.player_mode)
-
     def board_update(self):
         self.clock_count += 1
         for position, location in self.board_locations.iteritems():
@@ -303,6 +295,9 @@ class LocationCard(object):
         self.actions = location_actions
         self.max_number = max_number
         self.replacement_time = replacement_time
+        if options.debug:
+            for action in self.actions:
+                action.sanity_check(self)
 
     @classmethod
     def import_location(cls, state):
@@ -315,14 +310,14 @@ class LocationCard(object):
     @classmethod
     def build_action(cls, definition):
         action_class = getattr(actions, definition['action_class'])
-        required_bits = cls.parse_bits(definition['required_bits'])
+        required_bits = parse_bits(definition['required_bits'])
         data = definition.get('data', {})
         return action_class(required_bits, **data)
 
     @classmethod
     def new_location(cls, definition, replacement_params=None, puzzle=False):
         if 'bits' in definition:
-            bits = cls.parse_bits(definition['bits'])
+            bits = parse_bits(definition['bits'])
         else:
             bits = cls.generate_bitwise_operand()
 
@@ -345,11 +340,6 @@ class LocationCard(object):
             location.check_actions()
         return location
 
-    @classmethod
-    def parse_bits(self, bit_list):
-        # Convert names to numbers if applicable.
-        return frozenset(BITS.get(bit, bit) for bit in bit_list)
-
     def export(self):
         return {
             'bitwise_operand': sorted(self.bitwise_operand),