Skip to content

Commit ed728e2

Browse files
authored
2048.py
1 parent c0b68a7 commit ed728e2

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

AI-driven 2048(New game)/2048.py

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
import random
2+
import copy
3+
4+
class AI2048:
5+
def __init__(self, board):
6+
self.board = board
7+
8+
def get_empty_cells(self):
9+
empty_cells = []
10+
for i in range(len(self.board)):
11+
for j in range(len(self.board[i])):
12+
if self.board[i][j] == 0:
13+
empty_cells.append((i, j))
14+
return empty_cells
15+
16+
def get_max_tile(self):
17+
return max(map(max, self.board))
18+
19+
def move_left(self):
20+
merged = []
21+
for row in self.board:
22+
new_row = [tile for tile in row if tile != 0]
23+
for i in range(len(new_row) - 1):
24+
if new_row[i] == new_row[i + 1]:
25+
new_row[i] *= 2
26+
new_row[i + 1] = 0
27+
merged.append(new_row[i])
28+
new_row = [tile for tile in new_row if tile != 0]
29+
new_row.extend([0] * (len(row) - len(new_row)))
30+
self.board[self.board.index(row)] = new_row
31+
return merged
32+
33+
def move_right(self):
34+
self.flip_board()
35+
merged = self.move_left()
36+
self.flip_board()
37+
return merged
38+
39+
def move_up(self):
40+
self.transpose_board()
41+
merged = self.move_left()
42+
self.transpose_board()
43+
return merged
44+
45+
def move_down(self):
46+
self.transpose_board()
47+
merged = self.move_right()
48+
self.transpose_board()
49+
return merged
50+
51+
def get_score(self):
52+
return sum(map(sum, self.board))
53+
54+
def get_heuristic_score(self):
55+
max_tile = self.get_max_tile()
56+
empty_cells = len(self.get_empty_cells())
57+
smoothness = self.calculate_smoothness()
58+
monotonicity = self.calculate_monotonicity()
59+
60+
# Weighted sum of different heuristics
61+
return 0.1 * self.get_score() + 2.7 * empty_cells + 1.0 * smoothness + 1.5 * monotonicity + 3.0 * max_tile
62+
63+
def calculate_smoothness(self):
64+
smoothness = 0
65+
for i in range(len(self.board)):
66+
for j in range(len(self.board[i])):
67+
if self.board[i][j] != 0:
68+
value = math.log2(self.board[i][j])
69+
for (dx, dy) in [(-1, 0), (0, -1), (1, 0), (0, 1)]:
70+
x, y = i + dx, j + dy
71+
if 0 <= x < len(self.board) and 0 <= y < len(self.board[i]) and self.board[x][y] != 0:
72+
neighbor_value = math.log2(self.board[x][y])
73+
smoothness -= abs(value - neighbor_value)
74+
return smoothness
75+
76+
def calculate_monotonicity(self):
77+
monotonicity = [0, 0, 0, 0]
78+
for i in range(len(self.board)):
79+
current = 0
80+
next = current + 1
81+
while next < 4:
82+
while next < 4 and self.board[i][next] == 0:
83+
next += 1
84+
if next >= 4:
85+
next -= 1
86+
current_value = 0 if self.board[i][current] == 0 else math.log2(self.board[i][current])
87+
next_value = 0 if self.board[i][next] == 0 else math.log2(self.board[i][next])
88+
if current_value > next_value:
89+
monotonicity[0] += next_value - current_value
90+
elif next_value > current_value:
91+
monotonicity[1] += current_value - next_value
92+
current = next
93+
next += 1
94+
95+
for j in range(len(self.board[0])):
96+
current = 0
97+
next = current + 1
98+
while next < 4:
99+
while next < 4 and self.board[next][j] == 0:
100+
next += 1
101+
if next >= 4:
102+
next -= 1
103+
current_value = 0 if self.board[current][j] == 0 else math.log2(self.board[current][j])
104+
next_value = 0 if self.board[next][j] == 0 else math.log2(self.board[next][j])
105+
if current_value > next_value:
106+
monotonicity[2] += next_value - current_value
107+
elif next_value > current_value:
108+
monotonicity[3] += current_value - next_value
109+
current = next
110+
next += 1
111+
112+
return max(monotonicity)
113+
114+
def get_children_states(self):
115+
children = []
116+
for move in ['left', 'right', 'up', 'down']:
117+
new_board = copy.deepcopy(self.board)
118+
merged = self.move(new_board, move)
119+
if merged:
120+
children.append((new_board, merged))
121+
return children
122+
123+
def get_best_move(self, depth=3):
124+
moves = ['left', 'right', 'up', 'down']
125+
best_score = float('-inf')
126+
best_move = random.choice(moves)
127+
for move in moves:
128+
new_board = copy.deepcopy(self.board)
129+
merged = self.move(new_board, move)
130+
if merged:
131+
child = AI2048(new_board)
132+
score = self.expectimax(child, depth - 1, False)
133+
if score > best_score:
134+
best_score = score
135+
best_move = move
136+
return best_move
137+
138+
def expectimax(self, node, depth, is_maximizing):
139+
if depth == 0:
140+
return node.get_heuristic_score()
141+
142+
if is_maximizing:
143+
max_eval = float('-inf')
144+
children = node.get_children_states()
145+
for child_board, merged in children:
146+
child = AI2048(child_board)
147+
eval = self.expectimax(child, depth - 1, False)
148+
max_eval = max(max_eval, eval)
149+
return max_eval
150+
else:
151+
total_score = 0
152+
empty_cells = node.get_empty_cells()
153+
for cell in empty_cells:
154+
child_board = copy.deepcopy(node.board)
155+
child_board[cell[0]][cell[1]] = 2
156+
child = AI2048(child_board)
157+
eval = self.expectimax(child, depth - 1, True)
158+
total_score += eval
159+
return total_score / len(empty_cells)
160+
161+
def move(self, board, direction):
162+
merged = []
163+
if direction == 'left':
164+
merged = self.move_left(board)
165+
elif direction == 'right':
166+
merged = self.move_right(board)
167+
elif direction == 'up':
168+
merged = self.move_up(board)
169+
elif direction == 'down':
170+
merged = self.move_down(board)
171+
return merged
172+
173+
def move_left(self, board):
174+
merged = []
175+
for row in board:
176+
new_row = [tile for tile in row if tile != 0]
177+
for i in range(len(new_row) - 1):
178+
if new_row[i] == new_row[i + 1]:
179+
new_row[i] *= 2
180+
new_row[i + 1] = 0
181+
merged.append(new_row[i])
182+
new_row = [tile for tile in new_row if tile != 0]
183+
new_row.extend([0] * (len(row) - len(new_row)))
184+
board[board.index(row)] = new_row
185+
return merged
186+
187+
def move_right(self, board):
188+
self.flip_board(board)
189+
merged = self.move_left(board)
190+
self.flip_board(board)
191+
return merged
192+
193+
def move_up(self, board):
194+
self.transpose_board(board)
195+
merged = self.move_left(board)
196+
self.transpose_board(board)
197+
return merged
198+
199+
def move_down(self, board):
200+
self.transpose_board(board)
201+
merged = self.move_right(board)
202+
self.transpose_board(board)
203+
return merged
204+
205+
def flip_board(self, board):
206+
for row in board:
207+
row.reverse()
208+
209+
def transpose_board(self, board):
210+
board[:] = [list(i) for i in zip(*board)]
211+
212+
def print_board(board):
213+
for row in board:
214+
print(row)
215+
print()
216+
217+
if __name__ == "__main__":
218+
board = [
219+
[0, 2, 2, 4],
220+
[0, 2, 0, 4],
221+
[0, 4, 2, 0],
222+
[0, 0, 2, 2]
223+
]
224+
225+
ai = AI2048(board)
226+
print("Initial Board:")
227+
print_board(ai.board)
228+
229+
while True:
230+
if ai.get_empty_cells():
231+
move = ai.get_best_move()
232+
print(f"Moving {move}")
233+
ai.move(move)
234+
print_board(ai.board)
235+
else:
236+
print("No more moves possible.")
237+
break

0 commit comments

Comments
 (0)