This repository was archived by the owner on Jun 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexpectimax.py
More file actions
71 lines (58 loc) · 2.3 KB
/
expectimax.py
File metadata and controls
71 lines (58 loc) · 2.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import constants
from game import Game, Grid, Direction
import math
def evaluate(game):
if constants.GRID_SIZE != 4:
return game.getScore()
if not game.hasValidMoves() or game.hasEnded():
return 0
if constants.EVAL_FUNC == 'score':
return game.getScore()
elif constants.EVAL_FUNC == 'weighted':
grid = game.getGrid()
score = 0
for i in range(constants.GRID_SIZE):
for j in range(constants.GRID_SIZE):
score += grid[i][j] * constants.WEIGHT_MATRIX[i][j]
return score
def expectimax(game, depth, max_player):
if depth == 0 or not game.hasValidMoves():
return evaluate(game)
if max_player:
# simulate a move in each direction
# and search for max expected state evaluation
max_utility = -math.inf
for move in range(4):
game_copy = Game(game.getGrid(), game.getScore())
# skip invalid moves
if not game_copy.move(Direction(move)):
continue
utility = expectimax(game_copy, depth - 1, False)
max_utility = max(max_utility, utility)
return max_utility
else:
# simulate insertions of new tiles after a move
# in every possible spot, with either 2 or 4 tiles
# and average the evaluation of all possible states
sum_utility = 0
empty_cells = Grid(game.getGrid()).getEmptyCells()
for position in empty_cells:
game_copy_2 = Game(game.getGrid(), game.getScore())
game_copy_4 = Game(game.getGrid(), game.getScore())
game_copy_2.insertTile(2, position)
game_copy_4.insertTile(4, position)
sum_utility += 0.9 * expectimax(game_copy_2, depth - 1, True) + 0.1 * expectimax(game_copy_4, depth - 1, True)
return sum_utility / max(1, len(empty_cells))
def getBestMove(game, depth):
best_move = None
best_utility = -math.inf
for move in range(4):
game_copy = Game(game.getGrid(), game.getScore())
# skip invalid moves
if not game_copy.move(Direction(move)):
continue
utility = expectimax(game_copy, depth, max_player=False)
if utility > best_utility:
best_move = Direction(move)
best_utility = utility
return best_move