1 from random import choice
3 from naja.constants import(
4 BITS, DIRECTION_BITS, CONDITION_BITS, PLAYER_DEFAULTS,
6 from naja.player import Player
7 from naja import actions
10 class GameBoard(object):
12 A representation of the game board.
15 def __init__(self, state, player, board_locations):
16 self.max_health = state['max_health']
17 self.wins_required = state['wins_required']
18 self.health = state['health']
19 self.wins = state['wins']
20 self.locations = [item.copy() for item in state['locations']]
22 self.board_locations = board_locations
23 self.player_mode = MOVE
26 def new_game(cls, locations_definition,
27 initial_bits=PLAYER_DEFAULTS.INITIAL_BITS,
28 initial_pos=PLAYER_DEFAULTS.INITIAL_POS,
29 max_health=PLAYER_DEFAULTS.MAX_HEALTH,
30 wins_required=PLAYER_DEFAULTS.WINS_REQUIRED):
32 'max_health': max_health,
34 'wins_required': wins_required,
36 'locations': locations_definition,
38 player = Player(initial_bits, initial_pos)
39 board_locations = cls.import_board_locations(
40 cls.generate_board(locations_definition))
41 return cls(state, player, board_locations)
44 def import_game(cls, definition):
45 state = definition.copy()
46 player = Player.import_player(state.pop('player'))
47 board_locations = cls.import_board_locations(
48 state.pop('board_locations'))
49 return cls(state, player, board_locations)
53 'max_health': self.max_health,
54 'health': self.health,
55 'wins_required': self.wins_required,
57 'locations': [item.copy() for item in self.locations],
58 'player': self.player.export(),
59 'board_locations': self.export_board_locations(),
63 def import_locations(cls, locations_definition):
65 LocationCard.import_location(definition)
66 for definition in locations_definition]
68 def export_board_locations(self):
70 (position, location.export())
71 for position, location in self.board_locations.iteritems())
74 def import_board_locations(cls, board_locations_definition):
76 (position, LocationCard.import_location(definition))
77 for position, definition in board_locations_definition.iteritems())
80 def generate_board(cls, locations_definition):
84 board_location = LocationCard.new_location(
85 choice(locations_definition).copy())
86 board_locations[(x, y)] = board_location.export()
87 return board_locations
89 def lose_health(self):
92 self.end_game(win=False)
94 def gain_health(self):
95 if self.health < self.max_health:
98 def acquire_win_token(self):
100 if self.wins >= self.wins_required:
101 self.end_game(win=True)
103 def replace_card(self, position):
104 location = LocationCard.new_location(choice(self.locations).copy())
105 self.board_locations[position] = location
107 def change_mode(self, new_mode):
108 """Advance to the next mode"""
109 if new_mode == self.player_mode:
110 raise RuntimeError("Inconsistent state. Setting mode %s to itself"
112 elif new_mode in (MOVE, ACT, EXAMINE):
113 self.player_mode = new_mode
115 raise RuntimeError("Illegal player mode %s" % self.player_mode)
117 def end_game(self, win):
118 # TODO: Find a way to not have UI stuff in game logic stuff.
119 from naja.events import SceneChangeEvent
120 from naja.scenes.lose import LoseScene
121 from naja.scenes.win import WinScene
123 SceneChangeEvent.post(WinScene)
125 SceneChangeEvent.post(LoseScene)
128 class LocationCard(object):
130 A particular set of options available on a location.
133 def __init__(self, bitwise_operand, location_actions):
134 self.bitwise_operand = bitwise_operand
135 self.actions = location_actions
139 def import_location(cls, state):
141 cls.build_action(definition) for definition in state['actions']]
142 return cls(state['bitwise_operand'], location_actions)
145 def build_action(cls, definition):
146 action_class = getattr(actions, definition['action_class'])
147 required_bits = definition['required_bits']
148 data = definition.get('data', {})
149 return action_class(required_bits, **data)
152 def new_location(cls, definition):
153 return cls.import_location({
154 'bitwise_operand': cls.generate_bitwise_operand(),
155 'actions': definition['actions'],
160 'bitwise_operand': self.bitwise_operand,
161 'actions': [action.export() for action in self.actions],
164 def check_actions(self):
166 print "Warning: Location has no actions."
167 self.insert_default_default_action()
168 if self.actions[0].required_bits:
169 self.insert_default_default_action()
171 def insert_default_default_action(self):
172 self.actions.insert(0, self.build_action({
173 'action_class': 'DoNothing',
178 def generate_bitwise_operand():
180 Generate a set of two or three bits. At least one direction and one
181 condition bit will be included. There is a low probability of choosing
182 a third bit from the complete set.
185 bits.add(choice(DIRECTION_BITS.values()))
186 bits.add(choice(CONDITION_BITS.values()))
187 # One in three chance of adding a third bit, with a further one in four
188 # chance that it will match a bit already chosen.
189 if choice(range(3)) == 0:
190 bits.add(choice(BITS.values()))
191 return frozenset(bits)