Update new game screen to use new goodies.
[naja.git] / naja / player.py
1 from naja.constants import BITS, MOVES
2
3
4 class PlayerBits(object):
5     """
6     A glorified byte.
7     """
8
9     def __init__(self, bits):
10         self.bits = bits
11
12     @property
13     def bits(self):
14         return self._bits
15
16     @bits.setter
17     def bits(self, value):
18         assert 0 <= value <= 0xff
19         self._bits = value
20
21     # Operate on individual bits
22
23     def check_bit(self, bit):
24         return bool(self.bits & (1 << bit))
25
26     def set_bit(self, bit):
27         self.bits |= (1 << bit)
28
29     def clear_bit(self, bit):
30         self.bits &= (0xff ^ (1 << bit))
31
32     def toggle_bit(self, bit):
33         self.bits ^= (1 << bit)
34
35     # Operate on sets of bits
36
37     def check_bits(self, bits):
38         return all(self.check_bit(bit) for bit in bits)
39
40     def set_bits(self, bits):
41         for bit in bits:
42             self.set_bit(bit)
43
44     def clear_bits(self, bits):
45         for bit in bits:
46             self.clear_bit(bit)
47
48     def toggle_bits(self, bits):
49         for bit in bits:
50             self.toggle_bit(bit)
51
52
53 class Player(object):
54     """
55     A representation of the player.
56     """
57
58     def __init__(self, bits, position, movement_mode=None):
59         self.bits = PlayerBits(bits)
60         self.position = position
61         self.movement_mode = movement_mode if movement_mode else MOVES.ADJACENT
62
63     @classmethod
64     def import_player(cls, definition):
65         return cls(
66             definition['bits'],
67             tuple(definition['position']),
68             definition['movement_mode'])
69
70     def export(self):
71         return {
72             'bits': self.bits.bits,
73             'position': list(self.position),
74             'movement_mode': self.movement_mode,
75         }
76
77     def get_adjacent_positions(self):
78         positions = [self.position]
79
80         x, y = self.position
81
82         if self.bits.check_bit(BITS.NORTH) and y > 0:
83             positions.append((x, y - 1))
84         if self.bits.check_bit(BITS.SOUTH) and y < 4:
85             positions.append((x, y + 1))
86         if self.bits.check_bit(BITS.EAST) and x < 4:
87             positions.append((x + 1, y))
88         if self.bits.check_bit(BITS.WEST) and x > 0:
89             positions.append((x - 1, y))
90
91         return positions
92
93     def get_knight_positions(self):
94         positions = set([self.position])
95
96         x, y = self.position
97
98         for a in (2, -2):
99             for b in (1, -1):
100                 i, j = x + a, y + b
101                 if 0 <= i < 5 and 0 <= j < 5:
102                     positions.add((i, j))
103
104                 i, j = x + b, y + a
105                 if 0 <= i < 5 and 0 <= j < 5:
106                     positions.add((i, j))
107
108         return sorted(list(positions))
109
110     def get_bishop_positions(self):
111         positions = set()
112
113         x, y = self.position
114
115         for i in range(5):
116             j = i + y - x
117             if 0 <= j < 5:
118                 positions.add((i, j))
119
120             j = x + y - i
121             if 0 <= j < 5:
122                 positions.add((i, j))
123
124         return sorted(list(positions))
125
126     def get_castle_positions(self):
127         positions = set()
128
129         x, y = self.position
130
131         for i in range(5):
132             positions.add((x, i))
133             positions.add((i, y))
134
135         return sorted(list(positions))
136
137     def set_position(self, new_position):
138         if new_position in self.legal_moves():
139             self.position = new_position
140             self.movement_mode = MOVES.ADJACENT
141             return True
142         return False
143
144     def legal_moves(self):
145         POSITION_FUNCTION = {
146             MOVES.ADJACENT: self.get_adjacent_positions,
147             MOVES.KNIGHT: self.get_knight_positions,
148             MOVES.BISHOP: self.get_bishop_positions,
149             MOVES.CASTLE: self.get_castle_positions,
150         }
151         return POSITION_FUNCTION[self.movement_mode]()
152
153     def allow_chess_move(self, chesspiece):
154         self.movement_mode = chesspiece