Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9c6eb1c

Browse files
AdityaDaflapurkarnorvig
authored andcommitted
Fix various issues in backgammon and expectiminimax (aimacode#849)
* Fix expectiminimax and utility issues * Correct result function * Fix issue with dice roll in different states * Refactor code
1 parent 2f6ee0b commit 9c6eb1c

File tree

1 file changed

+22
-16
lines changed

1 file changed

+22
-16
lines changed

games.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,39 +42,40 @@ def min_value(state):
4242
# ______________________________________________________________________________
4343

4444
def expectiminimax(state, game):
45-
"""Returns the best move for a player after dice are thrown. The game tree
45+
"""Return the best move for a player after dice are thrown. The game tree
4646
includes chance nodes along with min and max nodes. [Figure 5.11]"""
4747
player = game.to_move(state)
4848

49-
def max_value(state):
50-
if game.terminal_test(state):
51-
return game.utility(state, player)
49+
def max_value(state, dice_roll):
5250
v = -infinity
5351
for a in game.actions(state):
5452
v = max(v, chance_node(state, a))
53+
game.dice_roll = dice_roll
5554
return v
5655

57-
def min_value(state):
58-
if game.terminal_test(state):
59-
return game.utility(state, player)
56+
def min_value(state, dice_roll):
6057
v = infinity
6158
for a in game.actions(state):
6259
v = min(v, chance_node(state, a))
60+
game.dice_roll = dice_roll
6361
return v
6462

6563
def chance_node(state, action):
6664
res_state = game.result(state, action)
65+
if game.terminal_test(res_state):
66+
return game.utility(res_state, player)
6767
sum_chances = 0
6868
num_chances = 21
6969
dice_rolls = list(itertools.combinations_with_replacement([1, 2, 3, 4, 5, 6], 2))
7070
if res_state.to_move == 'W':
7171
for val in dice_rolls:
7272
game.dice_roll = (-val[0], -val[1])
73-
sum_chances += max_value(res_state) * (1/36 if val[0] == val[1] else 1/18)
73+
sum_chances += max_value(res_state,
74+
(-val[0], -val[1])) * (1/36 if val[0] == val[1] else 1/18)
7475
elif res_state.to_move == 'B':
7576
for val in dice_rolls:
7677
game.dice_roll = val
77-
sum_chances += min_value(res_state) * (1/36 if val[0] == val[1] else 1/18)
78+
sum_chances += min_value(res_state, val) * (1/36 if val[0] == val[1] else 1/18)
7879
return sum_chances / num_chances
7980

8081
# Body of expectiminimax:
@@ -403,6 +404,8 @@ def actions(self, state):
403404
"""Returns a list of legal moves for a state."""
404405
player = state.to_move
405406
moves = state.moves
407+
if len(moves) == 1 and len(moves[0]) == 1:
408+
return moves
406409
legal_moves = []
407410
for move in moves:
408411
board = copy.deepcopy(state.board)
@@ -414,10 +417,11 @@ def result(self, state, move):
414417
board = copy.deepcopy(state.board)
415418
player = state.to_move
416419
board.move_checker(move[0], self.dice_roll[0], player)
417-
board.move_checker(move[1], self.dice_roll[1], player)
420+
if len(move) == 2:
421+
board.move_checker(move[1], self.dice_roll[1], player)
418422
to_move = ('W' if player == 'B' else 'B')
419423
return GameState(to_move=to_move,
420-
utility=self.compute_utility(board, move, to_move),
424+
utility=self.compute_utility(board, move, player),
421425
board=board,
422426
moves=self.get_all_moves(board, to_move))
423427

@@ -437,6 +441,8 @@ def get_all_moves(self, board, player):
437441
all_points = board.points
438442
taken_points = [index for index, point in enumerate(all_points)
439443
if point[player] > 0]
444+
if board.checkers_at_home(player) == 1:
445+
return [(taken_points[0], )]
440446
moves = list(itertools.permutations(taken_points, 2))
441447
moves = moves + [(index, index) for index, point in enumerate(all_points)
442448
if point[player] >= 2]
@@ -446,11 +452,11 @@ def display(self, state):
446452
"""Display state of the game."""
447453
board = state.board
448454
player = state.to_move
455+
print("Current State : ")
449456
for index, point in enumerate(board.points):
450457
if point['W'] != 0 or point['B'] != 0:
451-
print("Point : ", index, " W : ", point['W'], " B : ", point['B'])
452-
print("player : ", player)
453-
458+
print("Point : ", index, " W : ", point['W'], " B : ", point['B'])
459+
print("To play : ", player)
454460

455461
def compute_utility(self, board, move, player):
456462
"""If 'W' wins with this move, return 1; if 'B' wins return -1; else return 0."""
@@ -482,7 +488,7 @@ def __init__(self):
482488
self.allow_bear_off = {'W': False, 'B': False}
483489

484490
def checkers_at_home(self, player):
485-
"""Returns the no. of checkers at home for a player."""
491+
"""Return the no. of checkers at home for a player."""
486492
sum_range = range(0, 7) if player == 'W' else range(17, 24)
487493
count = 0
488494
for idx in sum_range:
@@ -516,7 +522,7 @@ def is_legal_move(self, start, steps, player):
516522
return move1_legal and move2_legal
517523

518524
def move_checker(self, start, steps, player):
519-
"""Moves a checker from starting point by a given number of steps"""
525+
"""Move a checker from starting point by a given number of steps"""
520526
dest = start + steps
521527
dest_range = range(0, 24)
522528
self.points[start][player] -= 1

0 commit comments

Comments
 (0)