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