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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy