#--coding:utf8--
import random
import pygame as pg
WIDTH = 400
HEIGHT = 400
NUMGRID = 8
GRIDSIZE = 36
XMARGIN = (WIDTH - GRIDSIZE * NUMGRID) // 2
YMARGIN = (HEIGHT - GRIDSIZE * NUMGRID) // 2
# 加载图片
gem_imgs = [pg.image.load('resources/images/gem{}.png'.format(i)) for i in range(1, 8)]
gem_imgsx = [pg.image.load('resources/images_x/gem{}.png'.format(i)) for i in range(1, 8)]
gem_imgsy = [pg.image.load('resources/images_y/gem{}.png'.format(i)) for i in range(1, 8)]
gem_imgsbomb = [pg.image.load('resources/images_bomb/gem{}.png'.format(i)) for i in range(1, 8)]
class Puzzle(pg.sprite.Sprite):
speed_x = 10
speed_y = 10
def __init__(self,(x,y), img= None, size=(GRIDSIZE, GRIDSIZE)):
self.img =img if img else random.randint(0, len(gem_imgs) - 1)
self.fixed,self.direction = False,"down"
self.pos=[x,y]
self.types = "gem" + str(self.img)
super(Puzzle, self).__init__()
self.image = gem_imgs[self.img]
self.image = pg.transform.smoothscale(self.image, size)
self.rect = self.image.get_rect(x=XMARGIN + x * GRIDSIZE,y=YMARGIN + y * GRIDSIZE - GRIDSIZE) # 向上移动一格
def update(self):
if not self.fixed:
self.move(*self.pos)
# 拼图块移动
def move(self,x,y):
target_x, target_y=[XMARGIN + x * GRIDSIZE, YMARGIN + y * GRIDSIZE]
if self.direction == 'down':
self.rect.top = min(target_y, self.rect.top + self.speed_y)
if target_y == self.rect.top:
self.fixed = True
elif self.direction == 'up':
self.rect.top = max(target_y, self.rect.top - self.speed_y)
if target_y == self.rect.top:
self.fixed = True
elif self.direction == 'left':
self.rect.left = max(target_x, self.rect.left - self.speed_x)
if target_x == self.rect.left:
self.fixed = True
elif self.direction == 'right':
self.rect.left = min(target_x, self.rect.left + self.speed_x)
if target_x == self.rect.left:
self.fixed = True
class Puzzle_x(Puzzle):
def __init__(self, position, img, size=(GRIDSIZE, GRIDSIZE)):
super(Puzzle_x, self).__init__(position, img)
self.image = pg.transform.smoothscale(gem_imgsx[self.img], size)
self.rect.top = self.rect.bottom
class Puzzle_y(Puzzle):
def __init__(self, position, img, size=(GRIDSIZE, GRIDSIZE)):
super(Puzzle_y, self).__init__(position, img)
self.image = pg.transform.smoothscale(gem_imgsy[self.img], size)
self.rect.top = self.rect.bottom
class Puzzle_bomb(Puzzle):
def __init__(self, position, img, size=(GRIDSIZE, GRIDSIZE)):
super(Puzzle_bomb, self).__init__(position, img)
self.image = pg.transform.smoothscale(gem_imgsbomb[self.img], size)
self.rect.top = self.rect.bottom
class Puzzle_bird(Puzzle):
def __init__(self, position, img, size=(GRIDSIZE, GRIDSIZE)):
super(Puzzle_bird, self).__init__(position, img)
self.types = "bird"
self.image=pg.transform.smoothscale(pg.image.load('resources/bird.png'),size)
self.rect.top = self.rect.bottom
特效.py 中建立一个比方格长2格,宽2格的二维列表, 防止 遍历特效区域时报错
并将该二维列表全部置为None ,存入在该区域拼图的类型名称
from tool import *
bird_x = {(-2, 0), (-1, 0), (1, 0), (2, 0)} # x轴五连集合
bird1_x = {(-2, 0), (-1, 0), (-3, 0), (-4, 0)}
bird1_y = {(0, -2), (0, -1), (0, 1), (0, 2)} # y轴五连集合
bird_y = {(0, -2), (0, -1), (0, -3), (0, -4)}
birds = [bird_x,bird1_x, bird_y,bird1_y] # 五连列表
# 四联集合
line_y1 = {(0, -1), (0, 1), (0, 2)}
line_y2 = {(0, -1), (0, 1), (0, -2)}
line_x1 = {(-1, 0), (1, 0), (2, 0)}
line_x2 = {(-1, 0), (1, 0), (-2, 0)}
# 四连列表
lines_x = [line_x1, line_x2]
lines_y = [line_y1, line_y2]
# 左L
bomb1 = {(2, 0), (1, 0), (0, 1), (0, 2)}
bomb2 = {(2, 0), (1, 0), (0, -1), (0, -2)}
bomb3 = {(2, 0), (1, 0), (0, 1), (0, -2)}
bomb4 = {(2, 0), (1, 0), (0, -1), (0, 2)}
# 右L
bomb5 = {(-2, 0), (-1, 0), (0, 1), (0, 2)}
bomb6 = {(-2, 0), (-1, 0), (0, -1), (0, -2)}
bomb7 = {(-2, 0), (-1, 0), (0, 1), (0, -2)}
bomb8 = {(-2, 0), (-1, 0), (0, -1), (0, 2)}
bomb9 = {(-1, 0), (1, 0), (0, 1), (0, 2)} # T
bomb10 = {(-1, 0), (1, 0), (0, -1), (0, -2)} # T
bombs = [bomb1, bomb2, bomb3, bomb4, bomb5, bomb6, bomb7, bomb8, bomb9, bomb10]
# 三联
triple_y1 = {(0, -1), (0, 1)}
triple_y2 = {(0, -1), (0, -2)}
triple_y3 = {(0, 2), (0, 1)}
triple_x1 = {(-1, 0), (1, 0)}
triple_x2 = {(2, 0), (1, 0)}
triple_x3 = {(-1, 0), (-2, 0)}
triples = [triple_x1, triple_x2, triple_x3, triple_y1, triple_y2, triple_y3]
double_for=[(gem_i,gem_j) for gem_i in range(NUMGRID) for gem_j in range(NUMGRID)]
# 初始化置None
def reset():
all_gem.clear()
for gem_i in range(NUMGRID + 2):
all_gem.append([])
for gem_j in range(NUMGRID + 2):
all_gem[gem_i].append(None)
return all_gem
# 将特效集合的区域置None
def assignment(grop, gem_i=0,pos_x=0, pos_y=0):
for g_x, g_y in grop[gem_i]:
all_gem[g_x+pos_x][g_y+pos_y] = None
return len(grop)
def matchgroup(pos_x, pos_y, grp, grp_i=0):
types = all_gem[pos_x][pos_y]
def match(): # 开始匹配
for gem_x, gem_y in grp[grp_i]: # 特效集合
ty = all_gem[gem_x+pos_x][gem_y+pos_y]
# 判断匹配项是否相同并且不为None
if ty != types or ty == types is None:
return True
if grp_i >= len(grp): # 遍历到最后一个特效列表,返回Flase
return False, -1
else:
# 不满足 遍历下一个特效列表
if match():
return matchgroup(pos_x, pos_y, grp, grp_i + 1)
else:
# 满足 直接返回Ture
return True, grp_i
# 是否构成特效,构成那种特效
def isMatch(pos_x, pos_y):
def get_types():
return all_gem[pos_x][pos_y],pos_x,pos_y
"""
:param pos_x: 判断坐标的x轴
:param pos_y: 判断坐标的y轴
:return:
"""
types=get_types()
bird, bird_i = matchgroup(pos_x, pos_y, birds) # 五连
bomb, bomb_i = matchgroup(pos_x, pos_y, bombs) # T形 或L形
line_x, line_i_x = matchgroup(pos_x, pos_y, lines_x) # 四连
line_y, line_i_y = matchgroup(pos_x, pos_y, lines_y) # 四连
triple, triple_i = matchgroup(pos_x, pos_y, triples) # 三连
# 其他特效成立,三联一定成立
# 三连成立 其他特效不一定成立
# 三联不成立,其他特效一定不成立
if bird:
all_gem[pos_x][pos_y] = "bird"
return 'bird', assignment(birds, bird_i,pos_x, pos_y),types
if bomb:
return 'bomb', assignment(bombs, bomb_i,pos_x, pos_y),types
if line_x:
return 'line_x', assignment(lines_x, line_i_x,pos_x, pos_y),types
if line_y:
return 'line_y', assignment(lines_y, line_i_y,pos_x, pos_y),types
if triple:
all_gem[pos_x][pos_y] = None
return 'triple', assignment(triples, triple_i,pos_x, pos_y),types
return None, 0,types
def clear_y(gem_y):
"""
:param gem_y: 将纵坐标为gem_y的列表置None
:return:
"""
for gem_x in range(NUMGRID):
all_gem[gem_x][gem_y] = None
return NUMGRID
def clear_x(gem_x):
"""
:param gem_x: 将横坐标为gem_x的列表置None
:return:
"""
for gem_y in range(NUMGRID):
all_gem[gem_x][gem_y] = None
return NUMGRID
# 爆炸所爆区域
def clear_bomb(gem_x, gem_y):
"""
:param gem_x: 爆炸中心横坐标x点
:param gem_y: 爆炸中心纵坐标y点
:return:
"""
for gem_i in range(NUMGRID):
for gem_j in range(NUMGRID):
if abs(gem_x - gem_i) + abs(gem_y - gem_j) <= 2:
all_gem[gem_x][gem_y] = None
clear.add((gem_i,gem_j))
return len(clear)
# 魔法鸟特效作用坐标的集合
def clear_bird(types):
b = set()
for gem_x in range(NUMGRID):
for gem_y in range(NUMGRID):
if all_gem[gem_x][gem_y] == types:
all_gem[gem_x][gem_y]=None
b.add((gem_x, gem_y))
return b
all_gem = []
reset()
def drop():
for gem_x in range(NUMGRID):
for gem_y in range(NUMGRID):
if not all_gem[gem_x][gem_y]:
return True
def move_gem_xy(gem_x=NUMGRID - 1, gem_y=NUMGRID - 1):
"""
:param gem_x:
:param gem_y:
:return:从右下方开始遍历,从右到左,从下到上
"""
res_match, score,types = isMatch(gem_x, gem_y)
if res_match:
return res_match, score, types
else:
if gem_x >= 0:
return move_gem_xy(gem_x - 1, gem_y)
else:
if gem_y >= 0:
return move_gem_xy(NUMGRID - 1, gem_y - 1)
else:
return res_match, score, types
isMatch:匹配特效区域,如果符合上述特效,引用 assignment 将特效区域集合全部置None
拼图停止下降后,引用 move_gem_xy判断是否能形成新的特效
在消除.py中, 交换的二个拼图都为特效拼图引用 exchange
被波及的特效拼图引用clear_sigle
from 特效 import *
death_gem=pg.sprite.Group()
def get_pos(pos_x,pos_y):
return (XMARGIN + pos_x * GRIDSIZE, YMARGIN + pos_y * GRIDSIZE)
# gem1 特效拼图波及的魔法鸟拼图 或 交换拼图的第一个拼图
# gem2 魔法鸟拼图作用的普通拼图 或 交换拼图的第二个拼图
def exchange(gem1, gem2):
match1,match2 = isMatch(*gem1.pos),isMatch(*gem2.pos)
gem1_x,gem1_y,gem2_x,gem2_y=gem1.pos+gem2.pos # 获取坐标
# 初始化返回值
kind,score,grp,bird=match1[0] or match2[0],-1,[match1,match2],-1
def return_kind():
if kind:
gem1.kill()
gem2.kill()
death_gem.add(gem1,gem2)
return kind abs(assignment([bird])*score),grp
def Jigsaw_puzzle(pos1, pos2): # 四行 或 四纵 的集合
return set([pos1 - 1, pos1, pos1 + 1,pos2 - 1, pos2, pos2 + 1])
# 交换拼图中有一个为魔法鸟 跳出
if isinstance(gem1, Puzzle_bird):
# 魔法鸟作用的特效拼图信息列表,
bird,kind= clear_bird(gem2.types),"bird1"
if isinstance(gem2, Puzzle_bomb):
score+=clear_bomb(gem2_x, gem2_y)
for gem_x, gem_y in bird:
# 添加魔法鸟作用坐标的数据信息 下同
grp.append(['bomb', score,(gem2.types, gem_x, gem_y)])
elif isinstance(gem2, Puzzle_x) or isinstance(gem2, Puzzle_y):
score += NUMGRID
for gem_x, gem_y in bird:
choice = random.choice(["line_x", "line_y"])
grp.append([choice, NUMGRID, (gem2.types, gem_x, gem_y)])
elif isinstance(gem2, Puzzle_bird):
reset() # 重新初始化二维列表
return "clear", int(NUMGRID * NUMGRID * 1.2), grp
elif isinstance(gem2, Puzzle_bird):
# 魔法鸟作用的特效拼图信息列表
bird,kind = clear_bird(gem1.types),"bird2"
if isinstance(gem1, Puzzle_bomb):
score+=clear_bomb(gem1_x, gem1_y)
for gem_x, gem_y in bird:
grp.append(["bomb", score, (gem1.types, gem_x, gem_y)])
elif isinstance(gem1, Puzzle_x) or isinstance(gem1, Puzzle_y):
score += NUMGRID
for gem_x, gem_y in bird:
choice = random.choice(["line_x", "line_y"])
grp.append([choice, NUMGRID, (gem1.types, gem_x, gem_y)])
return ,abs(assignment([bird]) * score), grp
elif isinstance(gem2, Puzzle_y):
kind='y'
if isinstance(gem1, Puzzle_y) or isinstance(gem1, Puzzle_x):
score=clear_x(gem2_x)+clear_y(gem2_y)
elif isinstance(gem1, Puzzle_bomb):
for gem_y in Jigsaw_puzzle(gem1_y, gem2_y):
score +=clear_y(gem_y)
elif isinstance(gem2, Puzzle_x):
kind="x"
if isinstance(gem1, Puzzle_y) or isinstance(gem1, Puzzle_x):
score=clear_x(gem2_x)+clear_y(gem2_y)
elif isinstance(gem1, Puzzle_bomb):
for gem_x in Jigsaw_puzzle(gem1_x, gem2_x):
score +=clear_x(gem_x)
elif isinstance(gem2, Puzzle_bomb):
kind="bomb"
if isinstance(gem1, Puzzle_y):
for gem_y in Jigsaw_puzzle(gem1_y, gem2_y):
score +=clear_y(gem_y)
elif isinstance(gem1, Puzzle_x):
for gem_x in Jigsaw_puzzle(gem1_x, gem2_xy):
score +=clear_x(gem_x)
elif isinstance(gem1, Puzzle_bomb):
score += int(clear_bomb(gem1_x, gem1_y) * 1.2 * clear_bomb(gem2_x, gem2_y))
return return_kind()
def clear_sigle(gem, (gem_x,gem_y)): # 列表 获取的分数
gem.kill()
death_gem.add(gem)
if isinstance(gem, Puzzle_bomb):
return clear_bomb(gem_x, gem_y)
elif isinstance(gem, Puzzle_x):
return clear_x(gem_x)
elif isinstance(gem, Puzzle_y):
return clear_y(gem_y)
elif isinstance(gem, Puzzle_bird):
# 随机获取普通拼图
g = exchange(gem, gem_puzzle())
return g[1]
else: #普通拼图
return 1
tool.py
from puzz import *
pg.init()
pg.display.set_caption("开心消消乐")
screen = pg.display.set_mode((WIDTH, HEIGHT))
font = pg.font.Font(r'C:\Windows\Fonts\simsun.ttc', 25)
FPS = 30
# 三联不生成拼图,
def get_None((x,y)=(-1,-1),img=None):
return None
puzzples={
"line_x":Puzzle_x,"line_y":Puzzle_y,
'bomb':Puzzle_bomb,'bird':Puzzle_bird,
None:Puzzle,"triple":get_None
}
# 获取拼图的相关信息(图像,坐标,到达位置,类型名称 等等)
def gem_puzzle(x=-1, y=-1, types='none', ty=None):
try:
img=int(types[3])
except (ValueError,KeyError,TypeError):
img = None
finally:
return puzzples[ty]((x,y),img)
# 方格
def drawwBlock(rect, color, size):
pg.draw.rect(screen, color, rect, size)
# 信息
def showstep(step, score,gem):
remaining_time_render = font.render('步数' + str(step), True, (85, 65, 0))
score_render = font.render('分数:' + str(score), True, (85, 65, 0))
screen.blit(remaining_time_render, (WIDTH - 190, 15))
screen.blit(score_render, (55, 15))
if gem:
drawwBlock(gem.rect,(255, 0, 0), 2)
# 画出背景
def drawGrids(step, score,gem):
screen.fill((255, 255, 220))
showstep(step, score,gem)
for rect_x in range(NUMGRID):
for rect_y in range(NUMGRID):
rect = pg.Rect((XMARGIN + rect_x * GRIDSIZE, YMARGIN + rect_y * GRIDSIZE, GRIDSIZE, GRIDSIZE))
drawwBlock(rect, color=(255, 165, 0), size=1)
game.py
from 消除 import *
class GAME:
def swapGem(self, gem1, gem2):
gem1_pos,gem2_pos=gem1.pos,gem2.pos
if gem1_pos[0] - gem2_pos[0] == 1:
gem1.direction = 'left'
gem2.direction = 'right'
elif gem1_pos[0] - gem2_pos[0] == -1:
gem2.direction = 'left'
gem1.direction = 'right'
elif gem1_pos[1] - gem2_pos[1] == 1:
gem1.direction = 'up'
gem2.direction = 'down'
elif gem1_pos[1] - gem2_pos[1] == -1:
gem2.direction = 'up'
gem1.direction = 'down'
margin = abs(gem1_pos[0] - gem2_pos[0]) + abs(gem1_pos[1] - gem2_pos[1])
if abs(margin) != 1: # 拼图不相连
return False
gem1.pos,gem2.pos=gem2_pos,gem1_pos
gem1.fixed = gem2.fixed =False
self.all_gem[gem2_pos[0]][gem2_pos[1]] = gem1.types
self.all_gem[gem1_pos[0]][gem1_pos[1]] = gem2.types
return True
# 初始化
def reset(self):
self.step = 20
self.score = 0
self.all_gem = reset()
self.gems_group = pg.sprite.Group()
def effects_puzzle(self, ty, score, (types, pos_x, pos_y)):
# 在该处可形成相应特效拼图
gem = gem_puzzle(pos_x, pos_y, types, ty)
# 可合成特效拼图,更换拼图图像,位置不变
if gem and ty:
self.gems_group.add(gem)
self.all_gem[pos_x][pos_y]=gem.types
return score
# 将列表为空的位置处的拼图消除
def eliminate(self):
score = self.effects_puzzle(*move_gem_xy())
for gem_x in range(NUMGRID):
for gem_y in range(NUMGRID):
gem1= self.get_gem(get_pos(gem_x,gem_y))
gem2= self.get_gem(get_pos(gem_x,gem_y-1))
if not gem1:
self.all_gem[gem_x][gem_y]=None
# 列表的值为None并有拼图就消除
elif not self.all_gem[gem_x][gem_y] and gem1:
clear_sigle(gem1, (gem_x, gem_y)) # 消除特殊拼图
# 列表第一行为空 新增加一个拼图
if not gem_y and not self.all_gem[gem_x][gem_y]:
gems = gem_puzzle(gem_x, gem_y) # 添加Puzzle普通拼图
self.gems_group.add(gems)
self.all_gem[gem_x][gem_y] = gems.types
# 列表的值为None并且上方有拼图
# 将列表上下互换,并且将上方拼图的到达位置下降一格
# 将该位置上方数值赋值给自己,上方数值赋为None
elif not self.all_gem[gem_x][gem_y] and gem2:
self.all_gem[gem_x][gem_y] = self.all_gem[gem_x][gem_y - 1]
self.all_gem[gem_x][gem_y - 1] = None
gem2.pos[1] += 1
gem2.fixed = False
gem2.direction = 'down'
return score
# 检查拼图是否全部下落
def isFull(self):
for gems in self.gems_group:
if not gems.fixed:
return True
# 检查有无拼图块被选中
def get_gem(self, poss):
for gems in self.gems_group:
if gems.rect.collidepoint(poss):
return gems
# 游戏开始
def start(self):
clock = pg.time.Clock()
# gem1 与 gem2 是否相连
individual_moving = False
# gem_group 中的拼图是否全部下落
overall_moving = False
gem1=gem2 = None
while True:
overall_moving = not self.isFull()
for event in pg.event.get():
pos = pg.mouse.get_pos()
if event.type == pg.QUIT:
sys.exit()
elif event.type == pg.MOUSEBUTTONDOWN and overall_moving and not drop():
if not gem1:
gem1 = self.get_gem(pos)
else:
gem2 = self.get_gem(pos)
if gem2:
if self.swapGem(gem1, gem2):
individual_moving = True #交换拼图相连
else:
gem1 = gem2
if individual_moving and overall_moving:
cl,score,grp = exchange(gem1, gem2)
if not cl:
self.swapGem(gem1, gem2)
else:
for ma in grp:
self.score += self.effects_puzzle(*ma)
self.score += score
self.step -= 1
if cl == 'clear':
# 列表初始化后,精灵组全部清空,以防出错
self.gems_group.empty()
individual_moving = False
gem1 =gem2= None
elif overall_moving:
self.score += self.eliminate()
self.gems_group.update()
drawGrids(self.step, self.score)
self.gems_group.draw(screen)
if self.step <= 0 and not self.isFull():
return self.score
pg.display.flip()
clock.tick(FPS)
主函数
g = GAME()
g.reset()
score = g.start()