Spaces:
Running
Running
import numpy as np | |
import random | |
class Game2048: | |
def __init__(self, size=4): | |
self.size = size | |
self.reset() | |
def reset(self): | |
"""重置游戏状态""" | |
self.board = np.zeros((self.size, self.size), dtype=np.int32) | |
self.score = 0 | |
self.add_tile() | |
self.add_tile() | |
self.game_over = False | |
return self.board.copy() | |
def add_tile(self): | |
"""在随机空位置添加新方块(90%概率为2,10%概率为4)""" | |
empty_cells = [] | |
for i in range(self.size): | |
for j in range(self.size): | |
if self.board[i][j] == 0: | |
empty_cells.append((i, j)) | |
if empty_cells: | |
i, j = random.choice(empty_cells) | |
self.board[i][j] = 2 if random.random() < 0.9 else 4 | |
def move(self, direction): | |
""" | |
执行移动操作 | |
0: 上, 1: 右, 2: 下, 3: 左 | |
返回: (新棋盘状态, 游戏是否结束) | |
""" | |
moved = False | |
# 根据方向执行移动 | |
if direction == 0: # 上 | |
for j in range(self.size): | |
column = self.board[:, j].copy() | |
new_column, moved_col = self.slide(column) | |
if moved_col: | |
moved = True | |
self.board[:, j] = new_column | |
elif direction == 1: # 右 | |
for i in range(self.size): | |
row = self.board[i, :].copy()[::-1] | |
new_row, moved_row = self.slide(row) | |
if moved_row: | |
moved = True | |
self.board[i, :] = new_row[::-1] | |
elif direction == 2: # 下 | |
for j in range(self.size): | |
column = self.board[::-1, j].copy() | |
new_column, moved_col = self.slide(column) | |
if moved_col: | |
moved = True | |
self.board[:, j] = new_column[::-1] | |
elif direction == 3: # 左 | |
for i in range(self.size): | |
row = self.board[i, :].copy() | |
new_row, moved_row = self.slide(row) | |
if moved_row: | |
moved = True | |
self.board[i, :] = new_row | |
# 如果发生了移动,添加新方块并检查游戏结束 | |
if moved: | |
self.add_tile() | |
self.check_game_over() | |
return self.board.copy(), self.game_over | |
def slide(self, line): | |
"""处理单行/列的移动和合并逻辑""" | |
non_zero = line[line != 0] | |
new_line = np.zeros_like(line) | |
idx = 0 | |
score_inc = 0 | |
moved = False | |
# 检查是否移动 | |
if not np.array_equal(non_zero, line[:len(non_zero)]): | |
moved = True | |
# 合并相同数字 | |
i = 0 | |
while i < len(non_zero): | |
if i + 1 < len(non_zero) and non_zero[i] == non_zero[i+1]: | |
new_val = non_zero[i] * 2 | |
new_line[idx] = new_val | |
score_inc += new_val | |
i += 2 | |
idx += 1 | |
else: | |
new_line[idx] = non_zero[i] | |
i += 1 | |
idx += 1 | |
self.score += score_inc | |
return new_line, moved or (score_inc > 0) | |
def check_game_over(self): | |
"""检查游戏是否结束""" | |
# 检查是否还有空格子 | |
if np.any(self.board == 0): | |
self.game_over = False | |
return | |
# 检查水平和垂直方向是否有可合并的方块 | |
for i in range(self.size): | |
for j in range(self.size - 1): | |
if self.board[i][j] == self.board[i][j+1]: | |
self.game_over = False | |
return | |
for j in range(self.size): | |
for i in range(self.size - 1): | |
if self.board[i][j] == self.board[i+1][j]: | |
self.game_over = False | |
return | |
self.game_over = True | |
def get_valid_moves(self): | |
"""获取当前所有有效移动方向""" | |
valid_moves = [] | |
# 检查上移是否有效 | |
for j in range(self.size): | |
column = self.board[:, j].copy() | |
new_column, _ = self.slide(column) | |
if not np.array_equal(new_column, self.board[:, j]): | |
valid_moves.append(0) | |
break | |
# 检查右移是否有效 | |
for i in range(self.size): | |
row = self.board[i, :].copy()[::-1] | |
new_row, _ = self.slide(row) | |
if not np.array_equal(new_row[::-1], self.board[i, :]): | |
valid_moves.append(1) | |
break | |
# 检查下移是否有效 | |
for j in range(self.size): | |
column = self.board[::-1, j].copy() | |
new_column, _ = self.slide(column) | |
if not np.array_equal(new_column[::-1], self.board[:, j]): | |
valid_moves.append(2) | |
break | |
# 检查左移是否有效 | |
for i in range(self.size): | |
row = self.board[i, :].copy() | |
new_row, _ = self.slide(row) | |
if not np.array_equal(new_row, self.board[i, :]): | |
valid_moves.append(3) | |
break | |
return valid_moves | |
def get_state(self): | |
"""获取当前游戏状态表示(用于AI模型)""" | |
# 创建4个通道的状态表示 | |
state = np.zeros((4, self.size, self.size), dtype=np.float32) | |
# 通道0: 当前方块值的对数(归一化) | |
for i in range(self.size): | |
for j in range(self.size): | |
if self.board[i][j] > 0: | |
state[0, i, j] = np.log2(self.board[i][j]) / 16.0 # 支持到65536 (2^16) | |
# 通道1: 空格子指示器 | |
state[1] = (self.board == 0).astype(np.float32) | |
# 通道2: 可合并的邻居指示器 | |
for i in range(self.size): | |
for j in range(self.size): | |
if self.board[i][j] > 0: | |
# 检查右侧 | |
if j < self.size - 1 and self.board[i][j] == self.board[i][j+1]: | |
state[2, i, j] = 1.0 | |
state[2, i, j+1] = 1.0 | |
# 检查下方 | |
if i < self.size - 1 and self.board[i][j] == self.board[i+1][j]: | |
state[2, i, j] = 1.0 | |
state[2, i+1, j] = 1.0 | |
# 通道3: 最大值位置(归一化) | |
max_value = np.max(self.board) | |
if max_value > 0: | |
max_positions = np.argwhere(self.board == max_value) | |
for pos in max_positions: | |
state[3, pos[0], pos[1]] = 1.0 | |
return state |