python实现《扫雷》小游戏
Posted 牧雨子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python实现《扫雷》小游戏相关的知识,希望对你有一定的参考价值。
童叟无欺啊,主程序绝对不到60行(而且还是算上那种空行的喔)!!!
(文章末尾添加了代码文件下载链接,需要的可自取。)
废话不多说,简简单单的讲一哈过程。
1.画横线,竖线分出格子(拜托,小编。这是个人都知道的好吧,就不能挑重点讲一讲吗!···)
2.创立一个20*20的数组,全部初始化为0,表示一张空白游戏地图。
3.随机在地图内生成40个地雷(代码实现中使用X代替),每生成一个地雷时都要将它周围的方格(不是地雷的方格)的数字加1。
4.建立一个管理未被点开的格子的类,使用列表存储下所有还未被点开的格子的坐标,在鼠标单击一个格子后判断单机位置的合法性,若合法,则将这个鼠标点击的格子的坐标从列表中移除,代表格子被点开过,每次更新屏幕时都是根据这个列表中的元素来画出相对应的格子覆盖物。
5.建立一个管理被点开格子的类,使用一个列表存储下所有被点开过的格子的坐标,根据2中的数组来画出对应的图案(如果对应数字为0,那就什么都不要画,显示出背景色就好)。
6.每当点开一个格子,要是数字为0的话就用宽度搜索,将与他直接相连的数字为0的格子全部展现出来
好了,多余的事情我就不想说了,更详细的解释都是在对应代码处以注解的形式呈现。
代码伺候。
不信你自己数,加上所有的空行,主程序(保命的家伙不能丢!)最后一行也才第59行(这波属实是刀尖上舔血了!!!)
(二次更改的时候才加的5行背景音乐就不算了吧···)
import sys #调用exit()函数来结束游戏
import pygame
from covers import Cover #未点击方块时表面的覆盖物
from map_of_game import Map #游戏方块里面的地图
from settings import setting #游戏参数设置
class Game:
"""管理游戏的主程序"""
def __init__(self):
pygame.init()
pygame.mixer.init()
self.setting = setting()
#创立游戏主界面
self.screen = pygame.display.set_mode(self.setting.screen_size)
self.screen_rect = self.screen.get_rect()
pygame.display.set_caption('扫雷')
self.covers = Cover(self.setting, self.screen) #表面覆盖物
self.maps = Map(self.setting, self.covers, self.screen) # 内部地图
#背景音乐
#pygame.mixer.music.load('data/bgmusic.wav')
#pygame.mixer.music.play()
#pygame.mixer.music.fadeout(3)
def run_game(self):
while True:
#if not pygame.mixer.music.get_busy():
# pygame.mixer.music.play()
self._event_check_() #检测事件
self._update_screen_() #更新屏幕
def _event_check_(self):
for event in pygame.event.get():
if event.type == pygame.QUIT: #结束游戏
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos() #检测到单击鼠标事件,将鼠标的位置传入
self.covers.delete(x, y) #删除对应方块上的覆盖物
self.maps.add(x, y) #将该位置的覆盖物下的游戏地图加入即将要显示的队伍中
def _update_screen_(self):
self.screen.fill(self.setting.background_color) #填充背景颜色
for i in range(25): #绘制方格
pygame.draw.line(self.screen, self.setting.line_color, [0, i * 25], [500, i * 25],
self.setting.line_width) #横线
pygame.draw.line(self.screen, self.setting.line_color, [i * 25, 0], [i * 25, 500],
self.setting.line_width) #竖线
self.maps.show() #将所有被点击过的方格下的数字展现出来
self.covers.show() #将还没有被点击过的数字展现出来
pygame.display.update() #更新屏幕显示,将上面所做的工作展现在游戏界面上
if __name__ == '__main__':
my_game = Game()
my_game.run_game()
class setting:
"""管理游戏中的参数的类"""
def __init__(self):
self.screen_size = (500, 500) # 屏幕大小
self.background_color = (255, 255, 255) # 背景色
self.line_width = 1 # 线条粗细
self.line_color = (120, 120,120) # 线条颜色
self.block_width = 24 #每一个方格的宽度
self.screen_color = (150, 150, 150) # 展示界面的颜色
import pygame
class Cover:
"""管理游戏覆盖物的类"""
def __init__(self, setting, screen): #游戏参数设置和游戏主界面
self.setting = setting
self.screen = screen
self.covers = [] # 存储未被点击过的方块的覆盖物的位置
for i in range(20):
for j in range(20):
self.covers.append([i, j]) #刚开始时整个界面都是被覆盖的
def delete(self, x, y): #传入单机鼠标的位置,判断是否合法,如果是,删除当前方块
x = x // 25
y = y // 25
if [x, y] in self.covers:
self.covers.remove([x, y])
def show(self): #将所有未被点击过的方块展现出来
for cur in self.covers:
pygame.draw.rect(self.screen, self.setting.screen_color, ((cur[0] * 25, cur[1] * 25), (24, 24)))
from random import randint
import pygame
from game_over import Over
from pygame.mixer import *
class Map:
"""管理游戏中出现的雷和数字"""
def __init__(self, setting, covers, screen): #游戏参数,游戏的覆盖物,游戏界面
pygame.mixer.init()
self.setting = setting
self.bg_color = self.setting.background_color
self.screen = screen
self.covers = covers
#踩雷的背景音乐
#self.boot=pygame.mixer.Sound('data/boot.wav')
#self.boot.set_volume(0.2)
#开始时全部初始为0,表示当前方块啥都没有
self.maps = [[0 for _ in range(20)] for _ in range(20)]
#状态转移数组,所有(x+moves[i][0], y+moves[i][1])表示包围坐标为(x, y)方块的另外8块方块,
#用于随机产生地雷后更新它周围方块上的数字
self.moves = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]]
#存储当前需要被显示出来数字方块
self.now_show = []
#点击到地雷后结束游戏
self.over = Over(self.screen)
#随机生成游戏地图
self._born_map()
def _born_map(self):
"""随机生成地图"""
for i in range(40):
x = randint(0, 19)
y = randint(0, 19)
while self.maps[x][y] != 0:
x = randint(0, 19)
y = randint(0, 19)
self.maps[x][y] = 'X'
self._connect_(x, y) #更新地雷周围的数字
def _connect_(self, x, y):
"""更新地雷周围数字的函数"""
for cur in self.moves:
i = x + cur[0]
j = y + cur[1]
if i >= 0 and i < 20 and j >= 0 and j < 20 and self.maps[i][j] != 'X':
self.maps[i][j] += 1
def add(self, x, y): #传入当前鼠标点击的位置,判断合法性,合法则将对应的方块左边传入要展示的列表中
i = x // 25
j = y // 25
if i >= 0 and i < 20 and j >= 0 and j < 20:
self.now_show.append([i, j])
if self.maps[i][j] == 0:
self._add_connect_(i, j)
#如果当前点击到了地雷,将地雷标记为红色方块,并展示出来,暂停游戏3秒,自动退出游戏
if self.maps[i][j] == 'X':
pygame.draw.rect(self.screen, (255, 0, 0), ((i * 25, j * 25), (25, 25)))
#self.boot.play()
self.over.show()
# 宽度搜索将(x,y)周围相连的空白方块也展示出来
def _add_connect_(self, x, y):
que = []
que.append([x, y])
while que:
cur = que[-1]
del (que[-1])
for k in range(8):
i, j = cur[0] + self.moves[k][0], cur[1] + self.moves[k][1]
if i >= 0 and i < 20 and j >= 0 and j < 20 and self.maps[i][j] != 'X' and [i,
j] not in self.now_show:
self.now_show.append([i, j])
self.covers.delete(i * 25, j * 25)
if self.maps[i][j]==0:
que.append([i, j])
def show(self):
"""将生成的地图展现到屏幕上"""
for cur in self.now_show:
i = cur[0]
j = cur[1]
if self.maps[i][j] == 0:
continue
else:
self.msg = str(self.maps[i][j])
self.font = pygame.font.SysFont(None, 45)
self.font_image = self.font.render(self.msg, True, (60, 0, 0), None)
self.image_ract = self.font_image.get_rect()
self.image_ract.x = i * 25
self.image_ract.y = j * 25
self.image_ract.width = 24
self.image_ract.height = 24
self.screen.blit(self.font_image, self.image_ract)
import sys #结束游戏
from time import sleep #暂停游戏
import pygame
class Over:
"""控制游戏结束的类"""
def __init__(self, screen): #游戏主界面
self.is_over = False
self.screen = screen
self.screen_rect = self.screen.get_rect()
self.msg = 'GAME OVER'
#渲染文字'GAME OVER'到游戏主界面上
self.font = pygame.font.SysFont(None, 48)
self.image = self.font.render(self.msg, True, (100, 0, 0), (0, 0, 60))
self.rect = self.image.get_rect()
self.rect.center = self.screen_rect.center #将文字放在界面中心
def show(self): #遇见炸弹,游戏结束,结束前将玩家遇到的炸弹标记未红色方块并在结束前绘制出来
self.screen.blit(self.image, self.rect)
pygame.display.update()
sleep(3)
sys.exit()
都看到这里来了,都不能点个赞嘛!!!(呜呜呜)
代码源文件下载链接:
链接:https://pan.baidu.com/s/1dFjZ2nmtPH7BiYHt3WIpvQ
提取码:1024
下载后直接运行main.py文件即可,其它文件均放在main.py的同级目录下。
1000行Python代码实现俄罗斯方块/扫雷/五子棋/贪吃蛇
Python开发小游戏,它有又双叒叕来了…
一、效果展示
1、俄罗斯方块
这个应该是玩起来最最简单的了…
2、扫雷
运气好,点了四下都没踩雷哈哈…
3、五子棋
我是菜鸡,玩不赢电脑人…
4、贪吃蛇
害,这个是最惊心动魄的,为了我的小心脏,不玩了不玩了…
女朋友:你就是借机在玩游戏,逮到了
啊这…
那我不吹牛逼了,我们来敲代码吧~
二、代码展示
1、俄罗斯方块
方块部分
这部分代码单独保存py文件,这里我命名为 blocks.py
方块形状的设计,一开始我是做成 4 × 4,长宽最长都是4的话旋转的时候就不考虑怎么转了,就是从一个图形替换成另一个。
要实现这个功能,只要固定左上角的坐标就可以了。
import random
from collections import namedtuple
Point = namedtuple('Point', 'X Y')
Shape = namedtuple('Shape', 'X Y Width Height')
Block = namedtuple('Block', 'template start_pos end_pos name next')
# S形方块
S_BLOCK = [Block(['.OO',
'OO.',
'...'], Point(0, 0), Point(2, 1), 'S', 1),
Block(['O..',
'OO.',
'.O.'], Point(0, 0), Point(1, 2), 'S', 0)]
# Z形方块
Z_BLOCK = [Block(['OO.',
'.OO',
'...'], Point(0, 0), Point(2, 1), 'Z', 1),
Block(['.O.',
'OO.',
'O..'], Point(0, 0), Point(1, 2), 'Z', 0)]
# I型方块
I_BLOCK = [Block(['.O..',
'.O..',
'.O..',
'.O..'], Point(1, 0), Point(1, 3), 'I', 1),
Block(['....',
'....',
'OOOO',
'....'], Point(0, 2), Point(3, 2), 'I', 0)]
# O型方块
O_BLOCK = [Block(['OO',
'OO'], Point(0, 0), Point(1, 1), 'O', 0)]
# J型方块
J_BLOCK = [Block(['O..',
'OOO',
'...'], Point(0, 0), Point(2, 1), 'J', 1),
Block(['.OO',
'.O.',
'.O.'], Point(1, 0), Point(2, 2), 'J', 2),
Block(['...',
'OOO',
'..O'], Point(0, 1), Point(2, 2), 'J', 3),
Block(['.O.',
'.O.',
'OO.'], Point(0, 0), Point(1, 2), 'J', 0)]
# L型方块
L_BLOCK = [Block(['..O',
'OOO',
'...'], Point(0, 0), Point(2, 1), 'L', 1),
Block(['.O.',
'.O.',
'.OO'], Point(1, 0), Point(2, 2), 'L', 2),
Block(['...',
'OOO',
'O..'], Point(0, 1), Point(2, 2), 'L', 3),
Block(['OO.',
'.O.',
'.O.'], Point(0, 0), Point(1, 2), 'L', 0)]
# T型方块
T_BLOCK = [Block(['.O.',
'OOO',
'...'], Point(0, 0), Point(2, 1), 'T', 1),
Block(['.O.',
'.OO',
'.O.'], Point(1, 0), Point(2, 2), 'T', 2),
Block(['...',
'OOO',
'.O.'], Point(0, 1), Point(2, 2), 'T', 3),
Block(['.O.',
'OO.',
'.O.'], Point(0, 0), Point(1, 2), 'T', 0)]
BLOCKS = 'O': O_BLOCK,
'I': I_BLOCK,
'Z': Z_BLOCK,
'T': T_BLOCK,
'L': L_BLOCK,
'S': S_BLOCK,
'J': J_BLOCK
def get_block():
block_name = random.choice('OIZTLSJ')
b = BLOCKS[block_name]
idx = random.randint(0, len(b) - 1)
return b[idx]
def get_next_block(block):
b = BLOCKS[block.name]
return b[block.next]
游戏主代码
import sys
import time
import pygame
from pygame.locals import *
import blocks
SIZE = 30 # 每个小方格大小
BLOCK_HEIGHT = 25 # 游戏区高度
BLOCK_WIDTH = 10 # 游戏区宽度
BORDER_WIDTH = 4 # 游戏区边框宽度
BORDER_COLOR = (40, 40, 200) # 游戏区边框颜色
SCREEN_WIDTH = SIZE * (BLOCK_WIDTH + 5) # 游戏屏幕的宽
SCREEN_HEIGHT = SIZE * BLOCK_HEIGHT # 游戏屏幕的高
BG_COLOR = (40, 40, 60) # 背景色
BLOCK_COLOR = (20, 128, 200) #
BLACK = (0, 0, 0)
RED = (200, 30, 30) # GAME OVER 的字体颜色
def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):
imgText = font.render(text, True, fcolor)
screen.blit(imgText, (x, y))
def main():
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('俄罗斯方块')
font1 = pygame.font.SysFont('SimHei', 24) # 黑体24
font2 = pygame.font.Font(None, 72) # GAME OVER 的字体
font_pos_x = BLOCK_WIDTH * SIZE + BORDER_WIDTH + 10 # 右侧信息显示区域字体位置的X坐标
gameover_size = font2.size('GAME OVER')
font1_height = int(font1.size('得分')[1])
cur_block = None # 当前下落方块
next_block = None # 下一个方块
cur_pos_x, cur_pos_y = 0, 0
game_area = None # 整个游戏区域
game_over = True
start = False # 是否开始,当start = True,game_over = True 时,才显示 GAME OVER
score = 0 # 得分
orispeed = 0.5 # 原始速度
speed = orispeed # 当前速度
pause = False # 暂停
last_drop_time = None # 上次下落时间
last_press_time = None # 上次按键时间
def _dock():
nonlocal cur_block, next_block, game_area, cur_pos_x, cur_pos_y, game_over, score, speed
for _i in range(cur_block.start_pos.Y, cur_block.end_pos.Y + 1):
for _j in range(cur_block.start_pos.X, cur_block.end_pos.X + 1):
if cur_block.template[_i][_j] != '.':
game_area[cur_pos_y + _i][cur_pos_x + _j] = '0'
if cur_pos_y + cur_block.start_pos.Y <= 0:
game_over = True
else:
# 计算消除
remove_idxs = []
for _i in range(cur_block.start_pos.Y, cur_block.end_pos.Y + 1):
if all(_x == '0' for _x in game_area[cur_pos_y + _i]):
remove_idxs.append(cur_pos_y + _i)
if remove_idxs:
# 计算得分
remove_count = len(remove_idxs)
if remove_count == 1:
score += 100
elif remove_count == 2:
score += 300
elif remove_count == 3:
score += 700
elif remove_count == 4:
score += 1500
speed = orispeed - 0.03 * (score // 10000)
# 消除
_i = _j = remove_idxs[-1]
while _i >= 0:
while _j in remove_idxs:
_j -= 1
if _j < 0:
game_area[_i] = ['.'] * BLOCK_WIDTH
else:
game_area[_i] = game_area[_j]
_i -= 1
_j -= 1
cur_block = next_block
next_block = blocks.get_block()
cur_pos_x, cur_pos_y = (BLOCK_WIDTH - cur_block.end_pos.X - 1) // 2, -1 - cur_block.end_pos.Y
def _judge(pos_x, pos_y, block):
nonlocal game_area
for _i in range(block.start_pos.Y, block.end_pos.Y + 1):
if pos_y + block.end_pos.Y >= BLOCK_HEIGHT:
return False
for _j in range(block.start_pos.X, block.end_pos.X + 1):
if pos_y + _i >= 0 and block.template[_i][_j] != '.' and game_area[pos_y + _i][pos_x + _j] != '.':
return False
return True
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_RETURN:
if game_over:
start = True
game_over = False
score = 0
last_drop_time = time.time()
last_press_time = time.time()
game_area = [['.'] * BLOCK_WIDTH for _ in range(BLOCK_HEIGHT)]
cur_block = blocks.get_block()
next_block = blocks.get_block()
cur_pos_x, cur_pos_y = (BLOCK_WIDTH - cur_block.end_pos.X - 1) // 2, -1 - cur_block.end_pos.Y
elif event.key == K_SPACE:
if not game_over:
pause = not pause
elif event.key in (K_w, K_UP):
if 0 <= cur_pos_x <= BLOCK_WIDTH - len(cur_block.template[0]):
_next_block = blocks.get_next_block(cur_block)
if _judge(cur_pos_x, cur_pos_y, _next_block):
cur_block = _next_block
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if not game_over and not pause:
if time.time() - last_press_time > 0.1:
last_press_time = time.time()
if cur_pos_x > - cur_block.start_pos.X:
if _judge(cur_pos_x - 1, cur_pos_y, cur_block):
cur_pos_x -= 1
if event.key == pygame.K_RIGHT:
if not game_over and not pause:
if time.time() - last_press_time > 0.1:
last_press_time = time.time()
# 不能移除右边框
if cur_pos_x + cur_block.end_pos.X + 1 < BLOCK_WIDTH:
if _judge(cur_pos_x + 1, cur_pos_y, cur_block):
cur_pos_x += 1
if event.key == pygame.K_DOWN:
if not game_over and not pause:
if time.time() - last_press_time > 0.1:
last_press_time = time.time()
if not _judge(cur_pos_x, cur_pos_y + 1, cur_block):
_dock()
else:
last_drop_time = time.time()
cur_pos_y += 1
_draw_background(screen)
_draw_game_area(screen, game_area)
_draw_gridlines(screen)
_draw_info(screen, font1, font_pos_x, font1_height, score)
# 画显示信息中的下一个方块
_draw_block(screen, next_block, font_pos_x, 30 + (font1_height + 6) * 5, 0, 0)
if not game_over:
cur_drop_time = time.time()
if cur_drop_time - last_drop_time > speed:
if not pause:
if not _judge以上是关于python实现《扫雷》小游戏的主要内容,如果未能解决你的问题,请参考以下文章