为2048编写脚本。豆包出现上下文数量限制,使用copilot完成后续。
copilot
import pygame
import random
# 初始化Pygame
pygame.init()
# 设置屏幕大小
screen = pygame.display.set_mode((400, 550))
pygame.display.set_caption("2048 Game with Auto Play")
# 定义颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
LIGHT_GRAY = (220, 220, 220)
DARK_GRAY = (180, 180, 180)
TILE_COLORS = {
0: (205, 193, 180),
2: (238, 228, 218),
4: (237, 224, 200),
8: (242, 177, 121),
16: (245, 149, 99),
32: (246, 124, 95),
64: (246, 94, 59),
128: (237, 207, 114),
256: (237, 204, 97),
512: (237, 200, 80),
1024: (237, 197, 63),
2048: (237, 194, 46)
}
# 定义字体
font = pygame.font.Font(None, 36)
score_font = pygame.font.Font(None, 48)
flying_score_font = pygame.font.Font(None, 24)
# 初始化游戏板
board = [[0] * 4 for _ in range(4)]
score = 0
flying_scores = []
def add_new_tile():
empty_tiles = [(r, c) for r in range(4) for c in range(4) if board[r][c] == 0]
if empty_tiles:
r, c = random.choice(empty_tiles)
board[r][c] = 2 if random.random() < 0.9 else 4
def new_game():
global board, score, auto_playing
board = [[0] * 4 for _ in range(4)]
score = 0
auto_playing = False
add_new_tile()
add_new_tile()
def draw_board():
screen.fill(WHITE)
for r in range(4):
for c in range(4):
value = board[r][c]
color = TILE_COLORS.get(value, (0, 0, 0))
pygame.draw.rect(screen, color, (c * 100, r * 100, 98, 98))
pygame.draw.rect(screen, DARK_GRAY, (c * 100, r * 100, 98, 98), 2)
if value:
text = font.render(str(value), True, BLACK)
screen.blit(text, (c * 100 + 50 - text.get_width() // 2, r * 100 + 50 - text.get_height() // 2))
# 绘制自动播放按钮
mouse_pos = pygame.mouse.get_pos()
auto_play_button_rect = pygame.Rect(150, 420, 100, 50)
if auto_play_button_rect.collidepoint(mouse_pos):
button_color = LIGHT_GRAY
else:
button_color = GRAY
pygame.draw.rect(screen, button_color, auto_play_button_rect)
text = font.render("Auto Play", True, BLACK)
screen.blit(text, (150 + 50 - text.get_width() // 2, 420 + 25 - text.get_height() // 2))
# 绘制新游戏按钮
new_game_button_rect = pygame.Rect(150, 480, 100, 50)
if new_game_button_rect.collidepoint(mouse_pos):
button_color = LIGHT_GRAY
else:
button_color = GRAY
pygame.draw.rect(screen, button_color, new_game_button_rect)
text = font.render("New Game", True, BLACK)
screen.blit(text, (150 + 50 - text.get_width() // 2, 480 + 25 - text.get_height() // 2))
# 显示分数
score_text = score_font.render(f"Score: {score}", True, BLACK)
screen.blit(score_text, (10, 10))
# 绘制飞入的分数特效
new_flying_scores = []
for flying_score in flying_scores:
value, x, y, alpha = flying_score
alpha -= 5
if alpha > 0:
text = flying_score_font.render(f"+{value}", True, (255, 0, 0, alpha))
text.set_alpha(alpha)
screen.blit(text, (x, y))
new_flying_scores.append((value, x, y - 1, alpha))
flying_scores[:] = new_flying_scores
pygame.display.flip()
def evaluate_board(current_board):
"""
评估游戏板的得分,考虑合并次数和大数字的集中程度
"""
merge_count = 0
max_value = 0
for r in range(4):
for c in range(4):
if r < 3 and current_board[r][c] == current_board[r + 1][c]:
merge_count += 1
if c < 3 and current_board[r][c] == current_board[r][c + 1]:
merge_count += 1
max_value = max(max_value, current_board[r][c])
# 合并次数权重较高,大数字权重次之
return merge_count * 10 + max_value
def auto_play():
directions = ["up", "down", "left", "right"]
best_score = -1
best_direction = None
for direction in directions:
new_board = [row[:] for row in board] # 复制当前游戏板
temp_score = score
move_board(direction, new_board) # 尝试移动
new_score = evaluate_board(new_board) + temp_score # 计算新得分
# 检查移动是否有效(即新游戏板与原游戏板不同)
if new_board != board and new_score > best_score:
best_score = new_score
best_direction = direction
if best_direction:
move_board(best_direction)
else:
# 如果所有方向都无法移动,游戏结束
global auto_playing
auto_playing = False
print(f"Game Over! Final Score: {score}")
def move_board(direction, current_board=None):
global score, flying_scores
if current_board is None:
current_board = board
def slide(row):
global score, flying_scores
new_row = [i for i in row if i != 0]
added_score = 0
for i in range(len(new_row) - 1):
if new_row[i] == new_row[i + 1]:
new_row[i] *= 2
added_score += new_row[i]
new_row[i + 1] = 0
new_row = [i for i in new_row if i != 0]
if current_board is board:
score += added_score
if added_score > 0:
# 找到合并位置添加飞入分数特效
for r in range(4):
for c in range(4):
if current_board[r][c] == new_row[0]:
flying_scores.append((added_score, c * 100 + 50, r * 100 + 50, 255))
return new_row + [0] * (4 - len(new_row))
moved = False
if direction == "left":
for i in range(4):
new_row = slide(current_board[i])
if new_row != current_board[i]:
moved = True
current_board[i] = new_row
elif direction == "right":
for i in range(4):
new_row = slide(current_board[i][::-1])[::-1]
if new_row != current_board[i]:
moved = True
current_board[i] = new_row
elif direction == "up":
for i in range(4):
col = slide([current_board[r][i] for r in range(4)])
if col != [current_board[r][i] for r in range(4)]:
moved = True
for r in range(4):
current_board[r][i] = col[r]
elif direction == "down":
for i in range(4):
col = slide([current_board[r][i] for r in range(4)][::-1])[::-1]
if col != [current_board[r][i] for r in range(4)]:
moved = True
for r in range(4):
current_board[r][i] = col[r]
if moved and current_board is board:
add_new_tile()
# 定义按钮矩形
auto_play_button_rect = pygame.Rect(150, 420, 100, 50)
new_game_button_rect = pygame.Rect(150, 480, 100, 50)
# 添加事件处理逻辑
running = True
auto_playing = False
move_delay = 100 # 步骤执行间隔为0.5秒
last_move_time = 0
new_game() # 初始化游戏
while running:
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = event.pos
if auto_play_button_rect.collidepoint(mouse_pos):
auto_playing = not auto_playing
last_move_time = current_time
elif new_game_button_rect.collidepoint(mouse_pos):
new_game()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
move_board("up")
elif event.key == pygame.K_DOWN:
move_board("down")
elif event.key == pygame.K_LEFT:
move_board("left")
elif event.key == pygame.K_RIGHT:
move_board("right")
last_move_time = current_time
if auto_playing and current_time - last_move_time > move_delay:
auto_play()
last_move_time = current_time
draw_board()
pygame.quit()
下面是豆包给的详细注释
import pygame
import random
# 初始化 Pygame 库,为后续使用 Pygame 的各种功能做准备
pygame.init()
# 设置游戏窗口的大小,这里创建了一个宽 400 像素、高 550 像素的窗口
screen = pygame.display.set_mode((400, 550))
# 设置游戏窗口的标题
pygame.display.set_caption("2048 Game with Auto Play")
# 定义游戏中使用的各种颜色,以 RGB 元组的形式表示
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
LIGHT_GRAY = (220, 220, 220)
DARK_GRAY = (180, 180, 180)
# 定义不同数字方块对应的背景颜色
TILE_COLORS = {
0: (205, 193, 180),
2: (238, 228, 218),
4: (237, 224, 200),
8: (242, 177, 121),
16: (245, 149, 99),
32: (246, 124, 95),
64: (246, 94, 59),
128: (237, 207, 114),
256: (237, 204, 97),
512: (237, 200, 80),
1024: (237, 197, 63),
2048: (237, 194, 46)
}
# 定义游戏中使用的字体,None 表示使用系统默认字体,后面的数字是字体大小
font = pygame.font.Font(None, 36)
score_font = pygame.font.Font(None, 48)
flying_score_font = pygame.font.Font(None, 24)
# 初始化游戏板,创建一个 4x4 的二维列表,初始值都为 0
board = [[0] * 4 for _ in range(4)]
# 初始化游戏得分
score = 0
# 用于存储飞入分数特效的列表,每个元素是一个元组 (分数值, x 坐标, y 坐标, 透明度)
flying_scores = []
def add_new_tile():
"""
在游戏板的空白格子中随机添加一个新的数字方块,90% 的概率是 2,10% 的概率是 4
"""
# 找出游戏板中所有值为 0 的空白格子的坐标
empty_tiles = [(r, c) for r in range(4) for c in range(4) if board[r][c] == 0]
if empty_tiles:
# 从空白格子中随机选择一个坐标
r, c = random.choice(empty_tiles)
# 以 90% 的概率添加数字 2,10% 的概率添加数字 4
board[r][c] = 2 if random.random() < 0.9 else 4
def draw_board():
"""
绘制游戏界面,包括游戏板、按钮、分数以及飞入的分数特效
"""
# 用白色填充整个屏幕,清空之前的绘制内容
screen.fill(WHITE)
# 遍历游戏板的每一行
for r in range(4):
# 遍历游戏板的每一列
for c in range(4):
# 获取当前格子的值
value = board[r][c]
# 根据格子的值从颜色字典中获取对应的颜色,如果值不在字典中则使用黑色
color = TILE_COLORS.get(value, (0, 0, 0))
# 在屏幕上绘制当前格子的背景矩形
pygame.draw.rect(screen, color, (c * 100, r * 100, 98, 98))
# 在格子周围绘制深灰色的边框
pygame.draw.rect(screen, DARK_GRAY, (c * 100, r * 100, 98, 98), 2)
# 如果格子的值不为 0,则在格子中心绘制对应的数字
if value:
# 渲染数字文本
text = font.render(str(value), True, BLACK)
# 将文本居中绘制在格子内
screen.blit(text, (c * 100 + 50 - text.get_width() // 2, r * 100 + 50 - text.get_height() // 2))
# 绘制自动播放按钮
# 获取鼠标当前位置
mouse_pos = pygame.mouse.get_pos()
# 定义自动播放按钮的矩形区域
auto_play_button_rect = pygame.Rect(150, 420, 100, 50)
# 检查鼠标是否悬停在自动播放按钮上
if auto_play_button_rect.collidepoint(mouse_pos):
# 鼠标悬停时按钮使用浅灰色
button_color = LIGHT_GRAY
else:
# 鼠标未悬停时按钮使用灰色
button_color = GRAY
# 绘制自动播放按钮的矩形
pygame.draw.rect(screen, button_color, auto_play_button_rect)
# 渲染自动播放按钮的文本
text = font.render("Auto Play", True, BLACK)
# 将文本居中绘制在按钮内
screen.blit(text, (150 + 50 - text.get_width() // 2, 420 + 25 - text.get_height() // 2))
# 绘制新游戏按钮
# 定义新游戏按钮的矩形区域
new_game_button_rect = pygame.Rect(150, 480, 100, 50)
# 检查鼠标是否悬停在新游戏按钮上
if new_game_button_rect.collidepoint(mouse_pos):
# 鼠标悬停时按钮使用浅灰色
button_color = LIGHT_GRAY
else:
# 鼠标未悬停时按钮使用灰色
button_color = GRAY
# 绘制新游戏按钮的矩形
pygame.draw.rect(screen, button_color, new_game_button_rect)
# 渲染新游戏按钮的文本
text = font.render("New Game", True, BLACK)
# 将文本居中绘制在按钮内
screen.blit(text, (150 + 50 - text.get_width() // 2, 480 + 25 - text.get_height() // 2))
# 显示分数
# 渲染当前分数的文本
score_text = score_font.render(f"Score: {score}", True, BLACK)
# 将分数文本绘制在屏幕指定位置
screen.blit(score_text, (10, 480))
# 绘制飞入的分数特效
# 用于存储更新后的飞入分数特效信息
new_flying_scores = []
# 遍历所有的飞入分数特效信息
for flying_score in flying_scores:
# 解包飞入分数特效的信息,包括分数值、位置和透明度
value, x, y, alpha = flying_score
# 每次绘制时降低透明度
alpha -= 5
# 如果透明度大于 0,则继续绘制特效
if alpha > 0:
# 渲染特效文本
text = flying_score_font.render(f"+{value}", True, (255, 0, 0, alpha))
# 设置文本的透明度
text.set_alpha(alpha)
# 将特效文本绘制在屏幕上
screen.blit(text, (x, y))
# 更新特效的位置并添加到新的列表中
new_flying_scores.append((value, x, y - 1, alpha))
# 更新飞入分数特效列表
flying_scores[:] = new_flying_scores
# 更新整个屏幕的显示
pygame.display.flip()
def evaluate_board(current_board):
"""
评估游戏板的得分,考虑合并次数和大数字的集中程度
:param current_board: 当前的游戏板状态,是一个 4x4 的二维列表
:return: 评估后的得分
"""
# 初始化合并次数为 0
merge_count = 0
# 初始化最大数字为 0
max_value = 0
# 遍历游戏板的每一行
for r in range(4):
# 遍历游戏板的每一列
for c in range(4):
# 检查当前格子下方的格子,如果值相同则合并次数加 1
if r < 3 and current_board[r][c] == current_board[r + 1][c]:
merge_count += 1
# 检查当前格子右方的格子,如果值相同则合并次数加 1
if c < 3 and current_board[r][c] == current_board[r][c + 1]:
merge_count += 1
# 更新最大数字
max_value = max(max_value, current_board[r][c])
# 合并次数权重较高,大数字权重次之,计算最终得分
return merge_count * 10 + max_value
def auto_play():
"""
自动游戏模式,尝试找到最优的移动方向并执行移动
"""
# 定义可移动的方向列表
directions = ["up", "down", "left", "right"]
# 初始化最佳得分,设为 -1
best_score = -1
# 初始化最佳移动方向为 None
best_direction = None
# 遍历所有可能的移动方向
for direction in directions:
# 复制当前游戏板,避免修改原游戏板
new_board = [row[:] for row in board]
# 保存当前得分
temp_score = score
# 尝试在复制的游戏板上执行移动操作
move_board(direction, new_board)
# 计算移动后的新得分
new_score = evaluate_board(new_board) + temp_score
# 检查移动是否有效(即新游戏板与原游戏板不同),并且新得分是否大于最佳得分
if new_board != board and new_score > best_score:
# 更新最佳得分
best_score = new_score
# 更新最佳移动方向
best_direction = direction
# 如果找到了最佳移动方向
if best_direction:
# 在原游戏板上执行最佳移动操作
move_board(best_direction)
else:
# 如果所有方向都无法移动,游戏结束
global auto_playing
auto_playing = False
print(f"Game Over! Final Score: {score}")
def move_board(direction, current_board=None):
"""
根据指定方向移动游戏板上的数字方块,并处理合并和得分
:param direction: 移动方向,可选值为 "up", "down", "left", "right"
:param current_board: 可选参数,指定要操作的游戏板,默认为当前游戏板
"""
global score, flying_scores
# 如果未指定游戏板,则使用当前游戏板
if current_board is None:
current_board = board
def slide(row):
"""
辅助函数,用于将一行数字方块进行滑动和合并操作
:param row: 一行数字方块,是一个列表
:return: 滑动和合并后的行
"""
global score, flying_scores
# 移除行中的所有 0,得到一个只包含非零数字的列表
new_row = [i for i in row if i != 0]
# 初始化合并得到的额外得分
added_score = 0
# 遍历非零数字列表,检查相邻数字是否相同,如果相同则合并
for i in range(len(new_row) - 1):
if new_row[i] == new_row[i + 1]:
# 合并相邻相同数字,将前一个数字翻倍
new_row[i] *= 2
# 增加合并得到的额外得分
added_score += new_row[i]
# 将后一个数字置为 0
new_row[i + 1] = 0
# 移除合并后产生的 0
new_row = [i for i in new_row if i != 0]
# 如果操作的是当前游戏板
if current_board is board:
# 更新总得分
score += added_score
if added_score > 0:
# 找到合并位置添加飞入分数特效
for r in range(4):
for c in range(4):
if current_board[r][c] == new_row[0]:
flying_scores.append((added_score, c * 100 + 50, r * 100 + 50, 255))
# 在新行末尾补 0,使其长度为 4
return new_row + [0] * (4 - len(new_row))
# 标记是否有移动发生
moved = False
if direction == "left":
# 向左移动,对每一行进行滑动操作
for i in range(4):
new_row = slide(current_board[i])
if new_row != current_board[i]:
moved = True
current_board[i] = new_row
elif direction == "right":
# 向右移动,先将每一行反转,进行滑动操作后再反转回来
for i in range(4):
new_row = slide(current_board[i][::-1])[::-1]
if new_row != current_board[i]:
moved = True
current_board[i] = new_row
elif direction == "up":
# 向上移动,对每一列进行滑动操作
for i in range(4):
col = slide([current_board[r][i] for r in range(4)])
if col != [current_board[r][i] for r in range(4)]:
moved = True
for r in range(4):
current_board[r][i] = col[r]
elif direction == "down":
# 向下移动,先将每一列反转,进行滑动操作后再反转回来
for i in range(4):
col = slide([current_board[r][i] for r in range(4)][::-1])[::-1]
if col != [current_board[r][i] for r in range(4)]:
moved = True
for r in range(4):
current_board[r][i] = col[r]
# 如果有移动发生,并且操作的是当前游戏板
if moved and current_board is board:
# 在空白格子中添加一个新的数字方块
add_new_tile()
# 定义自动播放按钮的矩形区域
auto_play_button_rect = pygame.Rect(150, 420, 100, 50)
# 定义新游戏按钮的矩形区域
new_game_button_rect = pygame.Rect(150, 480, 100, 50)
# 游戏主循环是否运行的标志
running = True
# 自动播放模式的标志
auto_playing = False
# 自动播放时每次移动的延迟时间(毫秒)
move_delay = 100
# 记录上次移动的时间
last_move_time = 0
def new_game():
"""
开始新游戏,重置游戏板、得分和自动播放标志,并添加两个初始数字方块
"""
global board, score, auto_playing
# 重置游戏板为全 0
board = [[0] * 4 for _ in range(4)]
# 重置得分
score = 0
# 关闭自动播放模式
auto_playing = False
# 添加第一个初始数字方块
add_new_tile()
# 添加第二个初始数字方块
add_new_tile()
# 开始新游戏
new_game()
# 游戏主循环
while running:
# 获取当前时间(毫秒)
current_time = pygame.time.get_ticks()
# 处理所有的 Pygame 事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
# 如果用户点击关闭窗口按钮,退出游戏主循环
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# 如果用户点击鼠标
mouse_pos = event.pos
if auto_play_button_rect.collidepoint(mouse_pos)
mouse_pos = event.pos
if auto_play_button_rect.collidepoint(mouse_pos):
# 若点击自动播放按钮,切换自动播放状态
auto_playing = not auto_playing
# 记录当前时间作为上次移动时间
last_move_time = current_time
elif new_game_button_rect.collidepoint(mouse_pos):
# 若点击新游戏按钮,开始新游戏
new_game()
elif event.type == pygame.KEYDOWN:
# 若用户按下键盘按键
if event.key == pygame.K_UP:
# 按下上方向键,向上移动方块
move_board("up")
elif event.key == pygame.K_DOWN:
# 按下下方向键,向下移动方块
move_board("down")
elif event.key == pygame.K_LEFT:
# 按下左方向键,向左移动方块
move_board("left")
elif event.key == pygame.K_RIGHT:
# 按下右方向键,向右移动方块
move_board("right")
# 记录当前时间作为上次移动时间
last_move_time = current_time
# 若处于自动播放模式,且距离上次移动的时间超过设定的延迟
if auto_playing and current_time - last_move_time > move_delay:
# 执行自动播放逻辑
auto_play()
# 记录当前时间作为上次移动时间
last_move_time = current_time
# 绘制游戏界面
draw_board()
# 退出 Pygame,释放资源
pygame.quit()