Fix gamestate setup.
[naja.git] / naja / gameboard.py
1 from random import choice
2
3 from naja.constants import BITS, DIRECTION_BITS, CONDITION_BITS
4 from naja.player import Player
5 from naja import actions
6
7
8 class GameBoard(object):
9     """
10     A representation of the game board.
11     """
12
13     def __init__(self, state, player, board_locations):
14         self.max_health = state['max_health']
15         self.wins_required = state['wins_required']
16         self.health = state['health']
17         self.wins = state['wins']
18         self.locations = self.import_locations(state['locations'])
19         self.player = player
20         self.board_locations = board_locations
21
22     @classmethod
23     def new_game(cls, initial_bits, initial_pos, max_health, wins_required,
24                  locations_definition):
25         state = {
26             'max_health': max_health,
27             'health': max_health,
28             'wins_required': wins_required,
29             'wins': 0,
30             'locations': locations_definition,
31         }
32         player = Player(initial_bits, initial_pos)
33         board_locations = cls.generate_board(locations_definition)
34         return cls(state, player, board_locations)
35
36     @classmethod
37     def import_game(cls, definition):
38         state = definition.copy()
39         player = Player.import_player(state.pop('player'))
40         board_locations = cls.import_board_locations(
41             state.pop('board_locations'))
42         return cls(state, player, board_locations)
43
44     def export(self):
45         return {
46             'max_health': self.max_health,
47             'health': self.health,
48             'wins_required': self.wins_required,
49             'wins': self.wins,
50             'locations': self.export_locations(),
51             'player': self.player.export(),
52             'board_locations': self.export_board_locations(),
53         }
54
55     def export_locations(self):
56         return [location.export() for location in self.locations]
57
58     @classmethod
59     def import_locations(cls, locations_definition):
60         return [
61             LocationCard.import_location(definition)
62             for definition in locations_definition]
63
64     def export_board_locations(self):
65         return dict(
66             (position, location.export())
67             for position, location in self.board_locations)
68
69     @classmethod
70     def import_board_locations(cls, board_locations_definition):
71         return dict(
72             (position, LocationCard.import_location(definition))
73             for position, definition in board_locations_definition.iteritems())
74
75     @classmethod
76     def generate_board(cls, locations_definition):
77         # TODO: Choose some locations.
78         return {}
79
80     def lose_health(self):
81         self.health -= 1
82         # TODO: Check win/lose
83
84
85 class LocationCard(object):
86     """
87     A particular set of options available on a location.
88     """
89
90     def __init__(self, bitwise_operand, location_actions):
91         self.bitwise_operand = bitwise_operand
92         self.actions = location_actions
93
94     @classmethod
95     def import_location(cls, state):
96         # TODO: Import real locations.
97         location_actions = [
98             cls.build_action(definition) for definition in state['actions']]
99         return cls(state['bitwise_operand'], location_actions)
100
101     @classmethod
102     def build_action(cls, definition):
103         action_class = getattr(actions, definition['action_class'])
104         required_bits = definition['required_bits']
105         data = definition.get('data', {})
106         return action_class(required_bits, **data)
107
108     @classmethod
109     def new_location(cls, definition):
110         return cls.import_location({
111             'bitwise_operand': cls.generate_bitwise_operand(),
112             'actions': definition['actions'],
113         })
114
115     def export(self):
116         return {
117             'bitwise_operand': self.bitwise_operand,
118             'actions': [action.export() for action in self.actions],
119         }
120
121     @staticmethod
122     def generate_bitwise_operand():
123         """
124         Generate a set of two or three bits. At least one direction and one
125         condition bit will be included. There is a low probability of choosing
126         a third bit from the complete set.
127         """
128         bits = set()
129         bits.add(choice(DIRECTION_BITS.values()))
130         bits.add(choice(CONDITION_BITS.values()))
131         # One in three chance of adding a third bit, with a further one in four
132         # chance that it will match a bit already chosen.
133         if choice(range(3)) == 0:
134             bits.add(choice(BITS.values()))
135         return frozenset(bits)