forked from dberg258/2048-AI
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAI.py
120 lines (93 loc) · 3.75 KB
/
AI.py
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import time
import math
U, D, L, R = (-1, 0), (1, 0), (0, -1), (0, 1)
smoothness_calculations = dict()
smoothness_calculations[(0, 0)] = (R, D)
smoothness_calculations[(3, 3)] = (L, U)
smoothness_calculations[(1, 1)] = (U, D, L, R)
smoothness_calculations[(2, 2)] = (U, D, L, R)
smoothness_calculations[(2, 0)] = (U, D, R)
smoothness_calculations[(0, 2)] = (L, D, R)
smoothness_calculations[(1, 3)] = (U, D, L)
smoothness_calculations[(3, 1)] = (L, U, R)
weighted_matrix = [
[4 ** 15, 4 ** 14, 4 ** 13, 4 ** 12],
[4 ** 8, 4 ** 9, 4 ** 10, 4 ** 11],
[4 ** 7, 4 ** 6, 4 ** 5, 4 ** 4],
[4 ** 0, 4 ** 1, 4 ** 2, 4 ** 13]
]
class IntelligentAgent:
def get_move(self, grid):
return self.search(grid)
def search(self, grid):
start = time.process_time()
return self.max_value(grid, -10, 10, 0, start)
def max_value(self, state, alpha, beta, depth, start):
utility = self.get_utility(state)
if self.is_terminal(state, depth) or time.process_time()-start > .15:
return utility
best_val = -10
best_move = None
for move in state.getAvailableMoves():
result = self.chance(move[1], alpha, beta, depth + 1, start)
if result > best_val:
best_val = result
best_move = move[0]
if best_val >= beta:
return best_val
alpha = max(alpha, best_val)
if depth == 0:
return best_move
return best_val
def chance(self, state, alpha, beta, depth, start):
return .9 * self.min_value(state, alpha, beta, depth, 2, start) + .1 * self.min_value(state, alpha, beta, depth, 4, start)
def min_value(self, state, alpha, beta, depth, tile, start):
utility = self.get_utility(state)
if self.is_terminal(state, depth) or time.process_time()-start > .15:
return utility
best_val = 10
for spawn in state.getAvailableCells():
state.setCellValue(spawn, tile)
result = self.max_value(state, alpha, beta, depth + 1, start)
state.setCellValue(spawn, 0)
best_val = min(best_val, result)
if best_val <= alpha:
return best_val
beta = min(beta, best_val)
return best_val
@staticmethod
def is_terminal(state, depth):
if depth == 4:
return True
return not state.canMove()
def get_utility(self, state):
board = state.map
tile_weight_score = self.get_weighted_tile_score(board)
smoothness_score = self.get_smoothness_score(board)
empty_cell_score = self.get_empty_cell_score(state)
tile_weight_score = math.log2(tile_weight_score)/15
smoothness_score = math.log(smoothness_score + 1)/3
empty_cell_score = math.log2(empty_cell_score + 1)
return 0.4*tile_weight_score - 0.9*smoothness_score + 0.1*empty_cell_score
@staticmethod
def get_weighted_tile_score(board):
tile_weight_sum = 0
for i in range(4):
for j in range(4):
tile_weight_sum += board[i][j] * weighted_matrix[i][j]
return tile_weight_sum
@staticmethod
def get_smoothness_score(board):
score = 0
for cell, directions in smoothness_calculations.items():
cell_value = board[cell[0]][cell[1]]
if cell_value:
for direction in directions:
neighbor_value = board[cell[0] + direction[0]][cell[1] + direction[1]]
difference = abs(cell_value - neighbor_value)
if neighbor_value:
score += difference
return score
@staticmethod
def get_empty_cell_score(state):
return len(state.getAvailableCells())