1- from Point import Point
1+ from collections import namedtuple
22from enum import Enum
33
4+ from Point import Point
5+ 46
57class Board (object ):
68 """A baghchal board"""
@@ -29,6 +31,29 @@ class Player(Enum):
2931 T = 1
3032 G = 2
3133
34+ class MoveType (Enum ):
35+ P = 1 # Place
36+ M = 2 # Move
37+ C = 3 # Capture
38+ 39+ def __repr__ (self ):
40+ if self .name == "P" :
41+ return "Place"
42+ elif self .name == "M" :
43+ return "Move"
44+ else :
45+ return "Capture"
46+ 47+ def __str__ (self ):
48+ return self .__repr__ ()
49+ 50+ # f = from, t = to, mt = MoveType
51+ nt = namedtuple ('Move' , ['f' , 't' , 'mt' ])
52+ 53+ class Move (nt ):
54+ def __repr__ (self ):
55+ return "%s-%s-%s" % (Point .get_coord (self .f ), Point .get_coord (self .t ), self .mt .name )
56+ 3257 # horizontal (1, -1) # vertical (5, -5) # diagonal (4, -4, 6, -6)
3358 directions = [1 , - 1 , 5 , - 5 , 4 , - 4 , 6 , - 6 ]
3459
@@ -71,11 +96,12 @@ def show(self):
7196 print ("Remaining Goats: %d" % self .goatsToBePlaced )
7297 print ("Dead Goats: %d\n " % self .deadGoats )
7398
74- def _get_full_position (self , pos_string ):
99+ @staticmethod
100+ def _get_full_position (pos_string ):
75101 """
76102 get a full position from a shortened position string:
77103 eg. 1GG1G/1GGGT/GGGGG/1GTGG/GTGTG gives
78- EGGEGEGGGTGGGGGEGTGGGTGTG
104+ list(' EGGEGEGGGTGGGGGEGTGGGTGTG')
79105 """
80106
81107 full_pos = []
@@ -98,7 +124,7 @@ def _get_full_position(self, pos_string):
98124
99125 def parse_position (self , position ):
100126 parts = position .split ()
101- full_pos = self ._get_full_position (parts [0 ])
127+ full_pos = Board ._get_full_position (parts [0 ])
102128 assert len (full_pos ) == 25
103129
104130 for idx , p in enumerate (full_pos ):
@@ -237,3 +263,162 @@ def winner(self):
237263 return self .Player .G
238264
239265 return None
266+ 267+ 268+ # move related
269+ def _placements (self ):
270+ return [
271+ Board .Move (point .index , point .index , Board .MoveType .P )
272+ for point in self .points
273+ if point .get_state () == Point .State .E
274+ ]
275+ 276+ def _movements (self ):
277+ """
278+ Returns the possible movements (excluding captures)
279+ for the board and the turn
280+ """
281+ 282+ # since we don't have goat positions, we just loop to find the goats
283+ if self .turn == Board .Player .G :
284+ pieces = [p .index for p in self .points if p .get_state () == Point .State .G ]
285+ else :
286+ pieces = self .tigerPos
287+ 288+ return [
289+ Board .Move (p , p + d , Board .MoveType .M )
290+ for p in pieces
291+ for d in Board .directions
292+ if self .is_movable (p , p + d )
293+ ]
294+ 295+ def _captures (self ):
296+ return [
297+ Board .Move (t , t + 2 * d , Board .MoveType .C )
298+ for t in self .tigerPos
299+ for d in Board .directions
300+ if self .can_capture (t , t + 2 * d )
301+ ]
302+ 303+ def _movable (self , t_pos ):
304+ """
305+ Returns whether a particular tiger is movable
306+ """
307+ 308+ return any (
309+ self .is_movable (t_pos , t_pos + d ) or self .can_capture (t_pos , t_pos + 2 * d )
310+ for d in Board .directions
311+ )
312+ 313+ def make_move (self , move ):
314+ """
315+ Makes the given move on the board
316+ """
317+ 318+ # placement
319+ if move .mt == Board .MoveType .P :
320+ self .points [move .t ].set_state ("G" )
321+ self .turn = Board .Player .T
322+ self .goatsToBePlaced -= 1
323+ 324+ # movement
325+ elif move .mt == Board .MoveType .M :
326+ if self .turn == Board .Player .G :
327+ self .points [move .t ].set_state ("G" )
328+ self .points [move .f ].set_state ("E" )
329+ self .turn = Board .Player .T
330+ else :
331+ self .points [move .t ].set_state ("T" )
332+ self .points [move .f ].set_state ("E" )
333+ self .turn = Board .Player .G
334+ try :
335+ self ._set_tiger_positions ()
336+ except :
337+ print ("-------------------ERROR-------------------" )
338+ self .show ()
339+ print (self .tigerPos )
340+ raise
341+ 342+ # capture
343+ elif move .mt == Board .MoveType .C :
344+ self .points [move .f ].set_state ("E" )
345+ self .points [(move .t + move .f ) // 2 ].set_state ("E" )
346+ self .points [move .t ].set_state ("T" )
347+ self .turn = Board .Player .G
348+ self .deadGoats += 1
349+ 350+ try :
351+ self ._set_tiger_positions ()
352+ except :
353+ print ("-------------------ERROR-------------------" )
354+ self .show ()
355+ print (self .tigerPos )
356+ raise
357+ # self._set_tiger_positions()
358+ 359+ def revert_move (self , move ):
360+ """
361+ Reverts the given move on the board
362+ """
363+ 364+ # placement
365+ if move .mt == Board .MoveType .P :
366+ self .points [move .t ].set_state ("E" )
367+ self .turn = Board .Player .G
368+ self .goatsToBePlaced += 1
369+ 370+ # movement
371+ elif move .mt == Board .MoveType .M :
372+ if self .turn == Board .Player .G :
373+ self .points [move .f ].set_state ("T" )
374+ self .points [move .t ].set_state ("E" )
375+ self .turn = Board .Player .T
376+ self ._set_tiger_positions ()
377+ else :
378+ self .points [move .f ].set_state ("G" )
379+ self .points [move .t ].set_state ("E" )
380+ self .turn = Board .Player .G
381+ 382+ # capture
383+ elif move .mt == Board .MoveType .C :
384+ self .points [move .f ].set_state ("T" )
385+ self .points [(move .t + move .f ) // 2 ].set_state ("G" )
386+ self .points [move .t ].set_state ("E" )
387+ self .turn = Board .Player .T
388+ self .deadGoats -= 1
389+ self ._set_tiger_positions ()
390+ 391+ def movable_tigers (self ):
392+ """
393+ Returns the number of movable tigers on the board
394+ """
395+ 396+ return sum (int (self ._movable (t )) for t in self .tigerPos )
397+ 398+ def generate_move_list (self , rdm = True ):
399+ """
400+ Generate a list of all moves for the board and turn
401+ """
402+ 403+ move_list = []
404+ 405+ # turn = Goat
406+ if self .turn == Board .Player .G :
407+ # placement phase
408+ if self .goatsToBePlaced > 0 :
409+ move_list .extend (self ._placements ())
410+ # movement phase
411+ else :
412+ move_list .extend (self ._movements ())
413+ 414+ # turn = Tiger
415+ else :
416+ # captures
417+ # captures are kept before movements
418+ # to improve the efficiency of ab pruning
419+ move_list .extend (self ._captures ())
420+ 421+ # movements
422+ move_list .extend (self ._movements ())
423+ 424+ return move_list
0 commit comments