bastard merge
authorDavid Sharpe <decoydavid@gmail.com>
Sat, 17 May 2014 12:50:42 +0000 (14:50 +0200)
committerDavid Sharpe <decoydavid@gmail.com>
Sat, 17 May 2014 12:50:42 +0000 (14:50 +0200)
1  2 
naja/gameboard.py

diff --combined naja/gameboard.py
index ed8d74f4657974079b3978ece51edbb0a30b3499,79e565e9a67bbbd380ef6e7f8cc41d6475fbfbe3..ad692e716e8ee414371c72152f04ea0672233403
@@@ -7,7 -7,6 +7,7 @@@ from naja.options import option
  from naja.player import Player
  from naja import actions
  from naja.sound import sound
 +import random
  
  
  class GameBoard(object):
@@@ -26,8 -25,6 +26,8 @@@
          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)
  
      @classmethod
      def new_game(cls, deck,
@@@ -44,8 -41,6 +44,8 @@@
              'wins': 0,
              'locations': deck['cards'],
              'puzzle': deck.get('puzzle', False),
 +            'clock_count': 0,
 +            'replacement_params': deck.get('replacement_params', None),
          }
          player = Player(initial_bits, initial_pos)
          board_locations = cls.import_board_locations(
@@@ -71,8 -66,6 +71,8 @@@
              '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,
          }
          if self.has_cheated:
              data['cheater'] = True
      @classmethod
      def generate_puzzle_board(cls, deck):
          assert len(deck['cards']) == 5 * 5
 +        replacement_params = deck.get('replacement_params', None)
          board_locations = [
              [(i % 5, i // 5),
 -             LocationCard.new_location(card.copy()).export()]
 +             LocationCard.new_location(
 +                 card.copy(), replacement_params).export()]
              for i, card in enumerate(deck['cards'])
          ]
          return board_locations
      @classmethod
      def generate_random_board(cls, deck):
          board_locations = []
 +        replacement_params = deck.get('replacement_params', None)
          for x in range(5):
              for y in range(5):
 -                board_location = LocationCard.new_location(new_choice.copy())
+                 new_choice = cls.choose_card(deck['cards'], board_locations)
-                     choice(deck['cards']).copy(), replacement_params)
 +                board_location = LocationCard.new_location(
++                    new_choice.copy(), replacement_params)
                  board_locations.append([(x, y), board_location.export()])
          return board_locations
  
              self.replace_card(position)
  
      def replace_card(self, position):
-         location = LocationCard.new_location(choice(self.locations).copy(),
+         new_choice = self.choose_card(self.locations,
+                                       self.board_locations.items(),
+                                       position)
 -        location = LocationCard.new_location(new_choice.copy())
++        location = LocationCard.new_location(new_choice.copy(),
 +                                             self.replacement_params)
          self.board_locations[position] = location
  
+     @classmethod
+     def choose_card(cls, cards, board_locations, position=None):
+         # Find which cards are at their maximum and exclude them from
+         # the choice list
+         counts = {}
+         choices = {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
+                 continue
+             if isinstance(card, LocationCard):
+                 key = card.card_name
+                 max_num = card.max_number
+             else:
+                 key = card['card_name']
+                 max_num = card.get('max_number', 25)
+             counts.setdefault(key, 0)
+             counts[key] += 1
+             if counts[key] >= max_num:
+                 if key in choices:
+                     del choices[key]
+         return choice(choices.values())
      def shift_location_row(self, change, is_vertical):
          px, py = self.player.position
          shifted_locations = {}
                                 % 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():
 +            location.timer_action(position, self)
 +
      def end_game(self, win):
          # TODO: Find a way to not have UI stuff in game logic stuff.
          from naja.events import SceneChangeEvent
@@@ -240,19 -248,19 +267,21 @@@ class LocationCard(object)
      """
  
      def __init__(self, card_name, bitwise_operand, location_actions,
-                  replacement_time):
 -                 max_number=25):
++                 replacement_time, max_number=25):
          self.card_name = card_name
          self.bitwise_operand = bitwise_operand
          self.actions = location_actions
+         self.max_number = max_number
          self.check_actions()
 +        self.replacement_time = replacement_time
  
      @classmethod
      def import_location(cls, state):
          location_actions = [
              cls.build_action(definition) for definition in state['actions']]
          return cls(state['card_name'], state['bitwise_operand'],
-                    location_actions, state['replacement_time'])
 -                   location_actions, state['max_number'])
++                   location_actions, state['replacement_time'],
++                   state['max_number'])
  
      @classmethod
      def build_action(cls, definition):
          return action_class(required_bits, **data)
  
      @classmethod
 -    def new_location(cls, definition):
 +    def new_location(cls, definition, replacement_params):
          if 'bits' in definition:
              bits = cls.parse_bits(definition['bits'])
          else:
              bits = cls.generate_bitwise_operand()
 +
 +        if 'replacement_time' in definition:
 +            replacement_time = definition['replacement_time']
 +        else:
 +            replacement_time = cls.generate_replacement_time(
 +                replacement_params)
 +
+         max_number = definition.get('max_number', 25)
          card_name = definition['card_name']
          return cls.import_location({
              'bitwise_operand': bits,
              'actions': definition['actions'],
+             'max_number': max_number,
              'card_name': card_name,
 +            'replacement_time': replacement_time,
          })
  
      @classmethod
          return {
              'bitwise_operand': sorted(self.bitwise_operand),
              'actions': [action.export() for action in self.actions],
+             'max_number': self.max_number,
              'card_name': self.card_name,
 +            'replacement_time': self.replacement_time,
          }
  
      def check_actions(self):
          if choice(range(3)) == 0:
              bits.add(choice(BITS.values()))
          return frozenset(bits)
 +
 +    @staticmethod
 +    def generate_replacement_time(replacement_params):
 +        if replacement_params is None:
 +            return None
 +        else:
 +            return random.randint(replacement_params[0], replacement_params[1])
 +
 +    def timer_action(self, position, board):
 +        if self.replacement_time is not None:
 +            self.replacement_time -= 1
 +            if self.replacement_time <= 0:
 +                board.replace_card(position)
 +