<think>题目要求是“开心消消乐”,这是一个消除类游戏。我们需要实现一个基本的消除逻辑,通常包括:
1. 一个网格,里面有不同的颜色块(用字符或数字表示)
2. 当三个或三个以上相同的块在水平或垂直方向相连,则消除这些块
3. 消除后,上方的块下落填充空缺
4. 重复消除直到没有可消除的块
我们将设计一个简单的消除游戏逻辑,使用一个二维数组来表示游戏网格。
我们假设网格大小为8x8,用不同的字母代表不同颜色(例如:'R','G','B','Y'等)。
步骤:
1. 初始化一个8x8的网格,随机填充几种颜色。
2. 实现一个函数来检查网格中是否有可消除的连续块(水平或垂直方向连续三个或以上相同)。
3. 实现消除函数:将连续三个或以上的相同块标记为空(用空格或None表示),然后让上方的块下落填充。
4. 填充后,顶部新出现的空位用随机颜色填充。
5. 重复步骤2-4直到没有可消除的块。
注意:消除可能同时发生在水平和垂直方向,并且一次消除后可能会引起新的连续。
我们将逐步实现:
第一步:初始化网格
第二步:打印网格(方便观察)
第三步:查找连续块
第四步:消除连续块并下落
第五步:填充新块
第六步:循环直到没有连续块
注意:我们可能需要多次扫描,因为一次消除后,下落和填充可能形成新的连续。
我们用一个函数`find_matches(grid)`来查找所有连续的块(标记为需要消除的位置)。
消除后,我们进行下落操作,然后填充顶部空缺。
由于可能存在多个连续块,我们使用一个辅助网格来标记哪些位置需要消除。
具体实现如下:
</think>
# 开心消消乐游戏实现
下面是一个基于Python和Pygame的开心消消乐游戏实现,包含核心游戏逻辑和可视化界面。
```python
import pygame
import sys
import random
import time
# 初始化pygame
pygame.init()
# 游戏常量
GRID_SIZE = 8
CELL_SIZE = 70
MARGIN = 5
SCREEN_WIDTH = GRID_SIZE * (CELL_SIZE + MARGIN) + MARGIN + 200
SCREEN_HEIGHT = GRID_SIZE * (CELL_SIZE + MARGIN) + MARGIN
FPS = 60
# 颜色定义
BACKGROUND = (230, 230, 250)
GRID_COLOR = (200, 200, 220)
HIGHLIGHT = (255, 255, 0, 150)
TEXT_COLOR = (70, 70, 70)
# 糖果颜色 (R, G, B)
CANDY_COLORS = [
(255, 89, 94), # 红色
(255, 202, 58), # 黄色
(138, 201, 38), # 绿色
(25, 130, 196), # 蓝色
(106, 76, 147), # 紫色
(255, 157, 129) # 橙色
]
class Candy:
def __init__(self, row, col, color_idx):
self.row = row
self.col = col
self.color_idx = color_idx
self.color = CANDY_COLORS[color_idx]
self.selected = False
self.x = col * (CELL_SIZE + MARGIN) + MARGIN
self.y = row * (CELL_SIZE + MARGIN) + MARGIN
def draw(self, screen):
# 绘制糖果
rect = pygame.Rect(self.x, self.y, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(screen, self.color, rect, border_radius=15)
pygame.draw.rect(screen, (255, 255, 255), rect, 2, border_radius=15)
# 绘制糖果内部的装饰
inner_rect = pygame.Rect(self.x + 10, self.y + 10, CELL_SIZE - 20, CELL_SIZE - 20)
pygame.draw.rect(screen, (255, 255, 255, 100), inner_rect, border_radius=10)
# 如果被选中,绘制高亮边框
if self.selected:
pygame.draw.rect(screen, HIGHLIGHT, rect, 4, border_radius=15)
class Game:
def __init__(self):
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("开心消消乐")
self.clock = pygame.time.Clock()
self.font = pygame.font.SysFont(None, 36)
self.small_font = pygame.font.SysFont(None, 28)
self.reset_game()
def reset_game(self):
self.grid = []
self.selected = None
self.score = 0
self.move_count = 30
self.game_over = False
self.message = ""
# 初始化网格
for row in range(GRID_SIZE):
self.grid.append([])
for col in range(GRID_SIZE):
color_idx = random.randint(0, len(CANDY_COLORS) - 1)
self.grid[row].append(Candy(row, col, color_idx))
# 确保初始网格没有可消除的组合
while self.find_matches():
self.remove_matches()
self.fill_empty_spaces()
def find_matches(self):
matches = []
# 检查水平匹配
for row in range(GRID_SIZE):
for col in range(GRID_SIZE - 2):
if (self.grid[row][col].color_idx == self.grid[row][col + 1].color_idx ==
self.grid[row][col + 2].color_idx):
# 找到匹配的起点和终点
match_length = 3
while col + match_length < GRID_SIZE and self.grid[row][col].color_idx == self.grid[row][col + match_length].color_idx:
match_length += 1
# 添加匹配的所有糖果
for i in range(match_length):
if (row, col + i) not in matches:
matches.append((row, col + i))
# 检查垂直匹配
for col in range(GRID_SIZE):
for row in range(GRID_SIZE - 2):
if (self.grid[row][col].color_idx == self.grid[row + 1][col].color_idx ==
self.grid[row + 2][col].color_idx):
# 找到匹配的起点和终点
match_length = 3
while row + match_length < GRID_SIZE and self.grid[row][col].color_idx == self.grid[row + match_length][col].color_idx:
match_length += 1
# 添加匹配的所有糖果
for i in range(match_length):
if (row + i, col) not in matches:
matches.append((row + i, col))
return matches
def remove_matches(self):
matches = self.find_matches()
if not matches:
return 0
# 计算得分 (基础分 + 连击奖励)
match_count = len(matches)
self.score += match_count * 10 + (match_count - 3) * 5
# 移除匹配的糖果
for row, col in matches:
self.grid[row][col] = None
return match_count
def fill_empty_spaces(self):
# 下落糖果填充空缺
for col in range(GRID_SIZE):
# 从底部向上移动糖果
empty_row = GRID_SIZE - 1
for row in range(GRID_SIZE - 1, -1, -1):
if self.grid[row][col] is not None:
if empty_row != row:
self.grid[empty_row][col] = self.grid[row][col]
self.grid[empty_row][col].row = empty_row
self.grid[empty_row][col].y = empty_row * (CELL_SIZE + MARGIN) + MARGIN
self.grid[row][col] = None
empty_row -= 1
# 在顶部生成新糖果
for row in range(empty_row, -1, -1):
color_idx = random.randint(0, len(CANDY_COLORS) - 1)
self.grid[row][col] = Candy(row, col, color_idx)
self.grid[row][col].y = -CELL_SIZE # 从屏幕上方开始下落
def swap_candies(self, row1, col1, row2, col2):
# 交换两个糖果的位置
self.grid[row1][col1], self.grid[row2][col2] = self.grid[row2][col2], self.grid[row1][col1]
# 更新糖果的行列信息
self.grid[row1][col1].row, self.grid[row1][col1].col = row1, col1
self.grid[row2][col2].row, self.grid[row2][col2].col = row2, col2
# 更新糖果的坐标
self.grid[row1][col1].x = col1 * (CELL_SIZE + MARGIN) + MARGIN
self.grid[row1][col1].y = row1 * (CELL_SIZE + MARGIN) + MARGIN
self.grid[row2][col2].x = col2 * (CELL_SIZE + MARGIN) + MARGIN
self.grid[row2][col2].y = row2 * (CELL_SIZE + MARGIN) + MARGIN
self.move_count -= 1
def is_adjacent(self, row1, col1, row2, col2):
# 检查两个糖果是否相邻
return (abs(row1 - row2) == 1 and col1 == col2) or (abs(col1 - col2) == 1 and row1 == row2)
def handle_click(self, pos):
if self.game_over:
self.reset_game()
return
# 计算点击的网格位置
col = pos[0] // (CELL_SIZE + MARGIN)
row = pos[1] // (CELL_SIZE + MARGIN)
if 0 <= row < GRID_SIZE and 0 <= col < GRID_SIZE:
if self.selected is None:
# 第一次选择糖果
self.selected = (row, col)
self.grid[row][col].selected = True
else:
# 第二次选择糖果
prev_row, prev_col = self.selected
self.grid[prev_row][prev_col].selected = False
if self.is_adjacent(prev_row, prev_col, row, col):
# 交换糖果
self.swap_candies(prev_row, prev_col, row, col)
# 检查是否有匹配
if not self.find_matches():
# 如果没有匹配,交换回来
self.swap_candies(prev_row, prev_col, row, col)
else:
# 有匹配,进行消除和填充
self.process_matches()
self.selected = None
def process_matches(self):
# 处理所有连续匹配
while self.find_matches():
self.remove_matches()
self.draw()
pygame.display.flip()
time.sleep(0.3) # 暂停一下让玩家看到消除效果
self.fill_empty_spaces()
self.draw()
pygame.display.flip()
time.sleep(0.3) # 暂停一下让玩家看到下落效果
# 检查游戏是否结束
if self.move_count <= 0:
self.game_over = True
self.message = "游戏结束! 最终得分: " + str(self.score)
def draw(self):
# 绘制背景
self.screen.fill(BACKGROUND)
# 绘制游戏网格背景
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
rect = pygame.Rect(col * (CELL_SIZE + MARGIN) + MARGIN,
row * (CELL_SIZE + MARGIN) + MARGIN,
CELL_SIZE, CELL_SIZE)
pygame.draw.rect(self.screen, GRID_COLOR, rect, border_radius=10)
# 绘制糖果
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
if self.grid[row][col]:
self.grid[row][col].draw(self.screen)
# 绘制信息面板
panel_rect = pygame.Rect(GRID_SIZE * (CELL_SIZE + MARGIN) + 20, 20, 160, SCREEN_HEIGHT - 40)
pygame.draw.rect(self.screen, (240, 240, 250), panel_rect, border_radius=15)
pygame.draw.rect(self.screen, (180, 180, 200), panel_rect, 3, border_radius=15)
# 绘制标题
title = self.font.render("开心消消乐", True, (106, 76, 147))
self.screen.blit(title, (panel_rect.x + 20, panel_rect.y + 20))
# 绘制分数
score_text = self.small_font.render(f"分数: {self.score}", True, TEXT_COLOR)
self.screen.blit(score_text, (panel_rect.x + 20, panel_rect.y + 70))
# 绘制剩余步数
moves_text = self.small_font.render(f"剩余步数: {self.move_count}", True, TEXT_COLOR)
self.screen.blit(moves_text, (panel_rect.x + 20, panel_rect.y + 110))
# 绘制操作说明
pygame.draw.line(self.screen, (200, 200, 220), (panel_rect.x + 10, panel_rect.y + 160),
(panel_rect.x + panel_rect.width - 10, panel_rect.y + 160), 2)
instructions = [
"游戏说明:",
"- 点击选择糖果",
"- 再点击相邻糖果交换",
"- 匹配3个或更多相同糖果",
"- 每步匹配得分",
"- 30步后游戏结束"
]
for i, text in enumerate(instructions):
inst_text = self.small_font.render(text, True, TEXT_COLOR)
self.screen.blit(inst_text, (panel_rect.x + 10, panel_rect.y + 180 + i * 30))
# 如果游戏结束,显示消息
if self.game_over:
game_over_rect = pygame.Rect(SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2 - 60, 300, 120)
pygame.draw.rect(self.screen, (255, 255, 255), game_over_rect, border_radius=20)
pygame.draw.rect(self.screen, (255, 89, 94), game_over_rect, 3, border_radius=20)
game_over_text = self.font.render("游戏结束!", True, (255, 89, 94))
self.screen.blit(game_over_text, (SCREEN_WIDTH // 2 - game_over_text.get_width() // 2,
SCREEN_HEIGHT // 2 - 40))
score_text = self.small_font.render(f"最终得分: {self.score}", True, TEXT_COLOR)
self.screen.blit(score_text, (SCREEN_WIDTH // 2 - score_text.get_width() // 2,
SCREEN_HEIGHT // 2))
restart_text = self.small_font.render("点击任意位置重新开始", True, (25, 130, 196))
self.screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2,
SCREEN_HEIGHT // 2 + 40))
def run(self):
# 游戏主循环
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
self.handle_click(event.pos)
self.draw()
pygame.display.flip()
self.clock.tick(FPS)
# 启动游戏
if __name__ == "__main__":
game = Game()
game.run()
```
## 代码解释
### 游戏核心机制
1. **网格系统**:使用8x8的网格表示游戏区域,每个单元格包含一个糖果
2. **糖果类型**:6种不同颜色的糖果,使用明亮颜色增强视觉效果
3. **匹配规则**:水平或垂直方向连续3个或更多相同糖果即可消除
4. **游戏流程**:
- 玩家选择相邻的两个糖果进行交换
- 如果交换后形成匹配,则消除糖果并计分
- 上方的糖果下落填补空缺,顶部生成新糖果
- 重复匹配消除直到没有更多匹配
- 玩家有30步机会,步数用尽游戏结束
### 关键函数说明
1. `find_matches()`:扫描整个网格,找出所有可消除的糖果组合
2. `remove_matches()`:移除匹配的糖果并计算得分
3. `fill_empty_spaces()`:让糖果下落填补空缺,并在顶部生成新糖果
4. `swap_candies()`:交换两个相邻糖果的位置
5. `process_matches()`:处理连续消除和糖果下落动画
### 游戏特点
- 可视化界面:使用Pygame绘制彩色糖果和游戏界面
- 动画效果:糖果消除和下落有短暂的延迟,增强游戏体验
- 计分系统:基础分+连击奖励分
- 游戏状态显示:实时显示分数和剩余步数
- 操作引导:右侧面板显示游戏说明
### 运行说明
1. 需要安装Python和Pygame库:`pip install pygame`
2. 运行代码即可开始游戏
3. 点击选择糖果,再点击相邻糖果交换
4. 匹配3个或更多相同糖果获得分数
5. 30步后游戏结束,点击屏幕重新开始
这个实现包含了开心消消乐的核心玩法,并提供了直观的视觉效果和流畅的游戏体验。