1+ from typing import Callable
2+
3+ from clobber.types cimport move
4+
5+ cdef class Board:
6+
7+ def __init__ (self , int n , int m , list state , int turn = 0 ):
8+ self .n = n
9+ self .m = m
10+ self .state = list (state)
11+ self .turn = turn
12+
13+ @staticmethod
14+ def initialize_board (int n , int m ):
15+ state_py = []
16+ cdef int y
17+ cdef bint white_py
18+
19+ for y in range (n):
20+ line_py = []
21+ white_py = (y % 2 == 0 )
22+ for _ in range (m):
23+ line_py.append(" W" if white_py else " B" )
24+ white_py = not white_py
25+ state_py.append(" " .join(line_py))
26+
27+ board = Board(n, m, state_py)
28+ return board
29+
30+ def copy (self ):
31+ return Board(self .n, self .m, [row for row in self .state], self .turn)
32+
33+ def __str__ (self ):
34+ return " \n " .join(self .state)
35+
36+ def pretty (self ):
37+ result_py = [" " + " " .join(map (str , range (self .m)))]
38+ cdef int y
39+ for y in range (self .n):
40+ result_py.append(str (y) + " " + str (self .state[y]))
41+ return " \n " .join(result_py)
42+
43+ cpdef get_piece_at(self , tuple position):
44+ cdef int x, y
45+ x, y = position
46+
47+ if x < 0 or y < 0 or y >= self .n or x >= self .m:
48+ return ' outside'
49+
50+ row_str = < str > self .state[y]
51+ pieces = row_str.split(' ' )
52+ return pieces[x]
53+
54+
55+ cpdef replace_piece_at(self , tuple position, object new_piece):
56+ cdef int x, y
57+ x, y = position
58+
59+ if x < 0 or y < 0 or y >= self .n or x >= self .m:
60+ return ' outside'
61+
62+ row_str = < str > self .state[y]
63+ pieces = row_str.split(' ' )
64+ if x < len (pieces):
65+ pieces[x] = < str > new_piece
66+ self .state[y] = " " .join(pieces)
67+ return new_piece
68+ else :
69+ return ' error_index'
70+
71+ def get_neighbours_positions (self , tuple position ):
72+ return self .get_neighbours_positions_filtered(position, lambda p : p in [" B" , " W" ])
73+
74+ cpdef list get_neighbours_positions_filtered(self , tuple position, object piece_filter):
75+ cdef int x, y
76+ cdef list neighbours_positions_py = []
77+ cdef tuple check_pos
78+ cdef object piece
79+
80+ x, y = position
81+
82+ positions_to_check = [(x - 1 , y), (x + 1 , y), (x, y - 1 ), (x, y + 1 )]
83+ for check_pos in positions_to_check:
84+ piece = self .get_piece_at(check_pos)
85+ if piece_filter(piece):
86+ neighbours_positions_py.append(check_pos)
87+
88+ return neighbours_positions_py
89+
90+ def make_move (self , object new_color , move m ):
91+ cdef tuple position_from, position_to
92+ position_from, position_to = m
93+ if self .get_piece_at(position_from) != new_color:
94+ raise Exception (" make_move check failed: piece is not player's color" )
95+ opponent_color = < object > (" B" if new_color == ' W' else ' W' )
96+ if self .get_piece_at(position_to) != opponent_color:
97+ raise Exception (" make_move check failed: target is not opponent" )
98+
99+ self .replace_piece_at(position_from, ' _' )
100+ self .replace_piece_at(position_to, new_color)
101+ self .turn += 1
102+
103+ def move_assuming_correct (self , object new_color , move m ):
104+ cdef tuple position_from, position_to
105+ position_from, position_to = m
106+ self .replace_piece_at(position_from, ' _' )
107+ self .replace_piece_at(position_to, new_color)
108+ self .turn += 1
109+
110+ cpdef list get_all_pieces(self , object piece):
111+ cdef list positions_py = []
112+ cdef int x, y
113+ for y in range (self .n):
114+ for x in range (self .m):
115+ if self .get_piece_at((x, y)) == piece:
116+ positions_py.append((x, y))
117+ return positions_py
118+
119+ def generate_moves (self , bint for_white ):
120+ cdef dict possible_moves_py = {}
121+ cdef object opponent, current_piece
122+ cdef int x, y
123+ cdef list neighbouring_opponents_py
124+
125+ opponent = < object > (' B' if for_white else ' W' )
126+ current_piece = < object > (' W' if for_white else ' B' )
127+
128+ for y in range (self .n):
129+ for x in range (self .m):
130+ if self .get_piece_at((x, y)) != current_piece:
131+ continue
132+
133+ def opponent_filter (p ):
134+ return p == opponent
135+
136+ neighbouring_opponents_py = self .get_neighbours_positions_filtered(
137+ (x, y), opponent_filter)
138+
139+ if not neighbouring_opponents_py:
140+ continue
141+
142+ possible_moves_py[(x, y)] = neighbouring_opponents_py
143+
144+ return possible_moves_py
145+
146+ cpdef bint has_moves(self , bint white):
147+ return len (self .generate_moves(white)) > 0
0 commit comments