b471c625135d4aea0d58af975a86e0a59e81c2a8
[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     def shift_bits_left(self, shift):
53         self.bits <<= shift
54
55     def shift_bits_right(self, shift):
56         self.bits >>= shift
57
58
59 class Player(object):
60     """
61     A representation of the player.
62     """
63
64     def __init__(self, bits, position, movement_mode=None):
65         self.bits = PlayerBits(bits)
66         self.position = position
67         self.movement_mode = movement_mode if movement_mode else MOVES.ADJACENT
68
69     @classmethod
70     def import_player(cls, definition):
71         return cls(
72             definition['bits'],
73             tuple(definition['position']),
74             definition['movement_mode'])
75
76     def export(self):
77         return {
78             'bits': self.bits.bits,
79             'position': list(self.position),
80             'movement_mode': self.movement_mode,
81         }
82
83     def get_adjacent_positions(self):
84         positions = [self.position]
85
86         x, y = self.position
87
88         if self.bits.check_bit(BITS.NORTH) and y > 0:
89             positions.append((x, y - 1))
90         if self.bits.check_bit(BITS.SOUTH) and y < 4:
91             positions.append((x, y + 1))
92         if self.bits.check_bit(BITS.EAST) and x < 4:
93             positions.append((x + 1, y))
94         if self.bits.check_bit(BITS.WEST) and x > 0:
95             positions.append((x - 1, y))
96
97         return positions
98
99     def get_knight_positions(self):
100         positions = set([self.position])
101
102         x, y = self.position
103
104         for a in (2, -2):
105             for b in (1, -1):
106                 i, j = x + a, y + b
107                 if 0 <= i < 5 and 0 <= j < 5:
108                     positions.add((i, j))
109
110                 i, j = x + b, y + a
111                 if 0 <= i < 5 and 0 <= j < 5:
112                     positions.add((i, j))
113
114         return sorted(list(positions))
115
116     def get_bishop_positions(self):
117         positions = set()
118
119         x, y = self.position
120
121         for i in range(5):
122             j = i + y - x
123             if 0 <= j < 5:
124                 positions.add((i, j))
125
126             j = x + y - i
127             if 0 <= j < 5:
128                 positions.add((i, j))
129
130         return sorted(list(positions))
131
132     def get_castle_positions(self):
133         positions = set()
134
135         x, y = self.position
136
137         for i in range(5):
138             positions.add((x, i))
139             positions.add((i, y))
140
141         return sorted(list(positions))
142
143     def set_position(self, new_position):
144         if new_position in self.legal_moves():
145             self.position = new_position
146             self.movement_mode = MOVES.ADJACENT
147             return True
148         return False
149
150     def legal_moves(self):
151         POSITION_FUNCTION = {
152             MOVES.ADJACENT: self.get_adjacent_positions,
153             MOVES.KNIGHT: self.get_knight_positions,
154             MOVES.BISHOP: self.get_bishop_positions,
155             MOVES.CASTLE: self.get_castle_positions,
156         }
157         return POSITION_FUNCTION[self.movement_mode]()
158
159     def allow_chess_move(self, chesspiece):
160         self.movement_mode = chesspiece