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):
108 """Advance to the next mode"""
109 if self.player_mode == MOVE:
110 self.player_mode = ACT
111 elif self.player_mode == ACT:
112 self.player_mode = MOVE
114 raise RuntimeError("Illegal player mode %s" % self.player_mode)
116 def end_game(self, win):
117 # TODO: Find a way to not have UI stuff in game logic stuff.
118 from naja.events import SceneChangeEvent
119 from naja.scenes.lose import LoseScene
120 from naja.scenes.win import WinScene
122 SceneChangeEvent.post(WinScene)
124 SceneChangeEvent.post(LoseScene)
127 class LocationCard(object):
129 A particular set of options available on a location.
132 def __init__(self, bitwise_operand, location_actions):
133 self.bitwise_operand = bitwise_operand
134 self.actions = location_actions
138 def import_location(cls, state):
140 cls.build_action(definition) for definition in state['actions']]
141 return cls(state['bitwise_operand'], location_actions)
144 def build_action(cls, definition):
145 action_class = getattr(actions, definition['action_class'])
146 required_bits = definition['required_bits']
147 data = definition.get('data', {})
148 return action_class(required_bits, **data)
151 def new_location(cls, definition):
152 return cls.import_location({
153 'bitwise_operand': cls.generate_bitwise_operand(),
154 'actions': definition['actions'],
159 'bitwise_operand': self.bitwise_operand,
160 'actions': [action.export() for action in self.actions],
163 def check_actions(self):
165 print "Warning: Location has no actions."
166 self.insert_default_default_action()
167 if self.actions[0].required_bits:
168 self.insert_default_default_action()
170 def insert_default_default_action(self):
171 self.actions.insert(0, self.build_action({
172 'action_class': 'DoNothing',
177 def generate_bitwise_operand():
179 Generate a set of two or three bits. At least one direction and one
180 condition bit will be included. There is a low probability of choosing
181 a third bit from the complete set.
184 bits.add(choice(DIRECTION_BITS.values()))
185 bits.add(choice(CONDITION_BITS.values()))
186 # One in three chance of adding a third bit, with a further one in four
187 # chance that it will match a bit already chosen.
188 if choice(range(3)) == 0:
189 bits.add(choice(BITS.values()))
190 return frozenset(bits)