python开发的连连看小游戏

说明:

1.在网上找了一个基础版本改进而来,大概增加了200行代码,相对原版要复杂一些;

2.界面采用tkinter开发,比较简单;

3.新增了连接线功能;

4.新增了积分功能;

5.新增了陷入死局时,重新打散功能;

6.新增了关卡功能,目前设置了5关;

7.新增了计算当前地图有多少可连通图标功能。

陷入死局时,效果如下:

打散后,重新排列,然后聚在一起:

 进入下一关界面:

 进入第二关效果如下:

 后面的关卡与上面类似。

源代码只有一个文件,用到的库比较少、比较小,安装起来很容易。代码如下:

from tkinter import BOTH,FIRST,LAST,GROOVE,FLAT
import os, random
import tkinter as tk
import tkinter.messagebox
import numpy as np
from PIL import Image, ImageTk
import time
from playsound import playsoundclass MainWindow():__gameTitle = "连连看游戏"__windowWidth = 600__windowHeigth = 500__gameSize = 10 # 游戏尺寸__iconKind = __gameSize * __gameSize / 4 # 小图片种类数量__iconWidth = 40__iconHeight = 40__map = [] # 游戏地图__delta = 25__isFirst = True__isGameStart = False__formerPoint = NoneEMPTY = -1NONE_LINK = 0STRAIGHT_LINK = 1ONE_CORNER_LINK = 2TWO_CORNER_LINK = 3__images = []#蒙板阴影图像__level = 1#游戏关卡__point_position = {}__score = 0def __init__(self):self.__root = tk.Tk()self.__root.title(self.__gameTitle)self.centerWindow(self.__windowWidth, self.__windowHeigth)self.__root.minsize(460, 460)self.__addComponets()self.new_game()self.__root.mainloop()#菜单栏中添加一个叫“游戏”的菜单项def __addComponets(self):#菜单栏self.menubar = tk.Menu(self.__root, bg="lightgrey", fg="black")#子菜单self.file_menu = tk.Menu(self.menubar, tearoff=0, bg="lightgrey", fg="black")self.file_menu.add_command(label="新游戏", command=self.new_game, accelerator="Ctrl+N")self.menubar.add_cascade(label="游戏", menu=self.file_menu)self.__root.configure(menu=self.menubar)self.__root.bind('<Control-n>',self.new_game)#实现快捷键功能ctrl+N#self.canvas = tk.Canvas(self.__root, bg = '#D3D3D3', width = 450, height = 450,cursor="hand2")self.canvas.grid(row=0,column=0,sticky='N',pady = 5,rowspan=2,padx=(50,0))self.canvas.bind('<Button-1>', self.clickCanvas)#分数面板self.label_score = tkinter.Label(self.__root,width=10,height=2,font=('黑体',13,'bold'),fg="#802A2A",bg="#F5DEB3")self.label_score.grid(row=0,column=1)#显示当前可连接数self.label_linknums = tkinter.Label(self.__root,width=10,height=2,font=('黑体',13,'bold'),fg="#228B22")self.label_linknums.grid(row=1,column=1)'''判断两个接点之间是否能连通'''def __isLink(self,fromPoint,point):return self.isStraightLink(fromPoint,point) or self.isOneCornerLink(fromPoint,point) or self.isTwoCornerLink(fromPoint,point)'''获取提示,逻辑有点复杂,主要解决以下问题:1.获取当前地图上所有的图标;2.然后遍历,计算有多少对可连通的图标;3.如果有三个或者四个相同的图标可以相互连通,要进行判断,以勉出现重复计算。'''def getPromptPoint(self):_link_point = []_icons_arr = self.__map.flatten()#先转化为一维数组,便于操作for i in set(_icons_arr):if i == self.EMPTY: continue #忽略为空的坐标_arr = np.where(_icons_arr==i)[0]#元组的第一个元素,是由i元素的索引组成的数组if _arr.size == 4:#剩下4个相同图标_point1 = Point(int(_arr[0]%10),int(_arr[0]/10))# 计算x,y坐标_point2 = Point(int(_arr[1]%10),int(_arr[1]/10))_point3 = Point(int(_arr[2]%10),int(_arr[2]/10))_point4 = Point(int(_arr[3]%10),int(_arr[3]/10))#4个接点6条线,但最多只能计算2条线,再多就会出现重复计数if self.__isLink(_point1,_point2):_link_point.append((_point1,_point2))if self.__isLink(_point3,_point4):_link_point.append((_point3,_point4))elif self.__isLink(_point1,_point3):_link_point.append((_point1,_point3))if self.__isLink(_point2,_point4):_link_point.append((_point2,_point4))elif self.__isLink(_point1,_point4):_link_point.append((_point1,_point4))if self.__isLink(_point2,_point3):_link_point.append((_point2,_point3))elif self.__isLink(_point2,_point3):_link_point.append((_point2,_point3))elif self.__isLink(_point2,_point4):_link_point.append((_point2,_point4))elif self.__isLink(_point3,_point4):_link_point.append((_point3,_point4))    elif _arr.size == 2:#剩下2个相同图标_point1 = Point(int(_arr[0]%10),int(_arr[0]/10))# 计算x,y坐标_point2 = Point(int(_arr[1]%10),int(_arr[1]/10))if self.__isLink(_point1,_point2):_link_point.append((_point1,_point2))self.label_linknums['text'] = f"{len(_link_point)}连"print("_link_point",_link_point)return _link_pointdef centerWindow(self, width, height):#700 500screenwidth = self.__root.winfo_screenwidth()#窗口距离屏幕左边的宽screenheight = self.__root.winfo_screenheight()#窗口距离屏幕顶部的高size = '%dx%d+%d+%d' % (width, height, (screenwidth - width)/2, (screenheight - height)/2)self.__root.geometry(size)def add_score(self):self.__score += 2 #消除一对图标,分数加2self.set_label_text()def set_label_text(self):self.label_score['text'] = f"{self.__score}分"'''这个方法需要注意的积分问题:1.开始新游戏时,将积分清零;2.开始下一关时,将前一关的积分累积起来。'''def new_game(self, event=None,level=1,score = 0):self.__score = scoreself.set_label_text()self.__level = levelself.extractSmallIconList()self.iniMap()self.drawMap()self.getPromptPoint()self.__isGameStart = Truedef clickCanvas(self, event):if not self.__isGameStart:return# 确认有效点击坐标point = self.getInnerPoint(Point(event.x, event.y))if not point.isUserful() or self.isEmptyInMap(point):return#__isFirst在三种情况下为True,1.游戏开始的时候;2.同一个图标点击了两次;3.成功消除一组图标。if self.__isFirst:self.drawSelectedArea(point)self.__isFirst= Falseself.__formerPoint = pointelse:if self.__formerPoint.isEqual(point):#两次点击的是同一个图标self.__isFirst = Trueself.canvas.delete("rectRedOne")self.canvas.delete('image_mask')else:linkType = self.getLinkType(self.__formerPoint, point)if linkType['type'] != self.NONE_LINK:#画连接线self.draw_link_line(self.__formerPoint,point,linkType)playsound("music2.mp3")time.sleep(.5)# 显示画线的延迟self.ClearLinkedBlocks(self.__formerPoint, point)self.canvas.delete("rectRedOne")self.canvas.delete("linkline")self.canvas.delete('image_mask')#增加分数self.add_score()_link_point = self.getPromptPoint()self.__isFirst = Trueif len(_link_point) == 0:if self.isGameEnd():if tk.messagebox.askokcancel('确认操作', '是否进入下一关?'):self.__level = self.__level+1self.new_game(level = self.__level,score = self.__score)else:self.__isGameStart = Falseelse: #没有可连通图标,且当前地图上还有图标存在,游戏未结束时,打散当前地图tk.messagebox.showinfo("提示", "已没有可连通图标,需重新打散!")self.shuffleMap()self.drawMap()self.getPromptPoint()else:self.__formerPoint = pointself.canvas.delete("rectRedOne")self.drawSelectedArea(point)# 判断游戏是否结束def isGameEnd(self):for y in range(0, self.__gameSize):for x in range(0, self.__gameSize):if self.__map[y][x] != self.EMPTY:return Falsereturn True'''消除图像前,把两个图像之间的连接线画出来。'''def draw_link_line(self,formerPoint,point,linkType):if linkType['type'] == self.STRAIGHT_LINK:p1 = self.getOuterCenterPoint(formerPoint)p2 = self.getOuterCenterPoint(point)#arrow表示线的箭头样式,默认不带箭头,参数值 FIRST表示添加箭头带线段开始位置,LAST表示到末尾占位置,BOTH表示两端均添加self.canvas.create_line((p1.x, p1.y),(p2.x, p2.y),fill = 'red',tags = 'linkline',width=3, arrow=LAST)if linkType['type'] == self.ONE_CORNER_LINK:           corner1 = self.getOuterCenterPoint(linkType["p1"])p1 = self.getOuterFitCenterPoint(formerPoint,corner1)p2 = self.getOuterFitCenterPoint(point,corner1)self.canvas.create_line((p1.x, p1.y),(corner1.x, corner1.y),fill = 'red',tags = 'linkline',width=3)self.canvas.create_line((p2.x, p2.y),(corner1.x, corner1.y),fill = 'red',tags = 'linkline',width=3, arrow=FIRST)elif linkType['type'] == self.TWO_CORNER_LINK:corner1 = self.getOuterCenterPoint(linkType["p1"])corner2 = self.getOuterCenterPoint(linkType["p2"])p1 = self.getOuterFitCenterPoint(formerPoint,corner1)p2 = self.getOuterFitCenterPoint(point,corner2)self.canvas.create_line((p1.x, p1.y),(corner1.x, corner1.y),fill = 'red',tags = 'linkline',width=3)self.canvas.create_line((p2.x, p2.y),(corner2.x, corner2.y),fill = 'red',tags = 'linkline',width=3, arrow=FIRST)self.canvas.create_line((corner1.x, corner1.y),(corner2.x, corner2.y),fill = 'red',tags = 'linkline',width=3)self.canvas.update()'''提取小头像数组'''def extractSmallIconList(self):self.__icons = []imageSouce = Image.open(f"图片/new{self.__level}.png")for index in range(0, int(self.__iconKind)):#0-24region = imageSouce.crop((self.__iconWidth * index, 0, self.__iconWidth * index + self.__iconWidth - 1, self.__iconHeight - 1))self.__icons.append(ImageTk.PhotoImage(region))'''初始化地图 存值为0-24'''def iniMap(self):self.__map = [] # 重置地图_tmpRecords = []# 0-24,一共25个图标,每个图标出现4次,总共出现100次_total = self.__gameSize * self.__gameSize_tmpRecords = np.linspace(0, self.__iconKind, _total, endpoint = False,dtype=int) np.random.shuffle(_tmpRecords)#重新打散洗牌self.__map = _tmpRecords.reshape((10,10))#将一维数组100,转化为二维10*10'''初始化地图 存值为0-24,当前地图中,如果没有可连通路线、进入死局时,将现有的图标进行打散重新洗牌。'''def shuffleMap(self):_icons_arr = self.__map.flatten()#先转化为一维数组,便于操作self.__map = []#重置地图for i in set(_icons_arr):if i == self.EMPTY: continue_tuple = np.where(_icons_arr==i)[0]#元组的第一个元素,是由i元素的索引组成的数组self.__map.extend([i]* _tuple.size)#第i个图标出现的次数np.random.shuffle(self.__map)#洗牌_len = len(self.__map)_total = self.__gameSize * self.__gameSizeself.__map[_len:_total] = [self.EMPTY]*(_total-_len)#剩余元素置为空(-1),填满至整个地图0-99self.__map = np.array(self.__map).reshape((self.__gameSize,self.__gameSize))#转化为二维数组10*10'''根据地图绘制图像1.在使用create_image()函数时,需要先通过Pillow库(或其他图片处理库)读取图片文件并生成图片对象,然后再将这个图片对象传递给create_image()函数,在指定的坐标位置将图片添加到画布上。2.canvas.create_image(x, y, image=图像对象, anchor=定位点),x和y表示图像锚点在画布上的位置,即图像在画布上的左上角坐标。image参数是一个tkinter中的PhotoImage()对象,它可以指定要加载的图像文件。anchor参数是一个字符串,用于指定图像锚点的位置,可以是"nw"(左上角)、"n"(上)、"ne"(右上角)、"w"(左)、"center"(中心)、"e"(右)、"sw"(左下角)或"s"(下)。如果不指定anchor参数,则默认为“center”。'''def drawMap(self):self.canvas.delete("all")#字符串"all""是代表画布上所有项目的特殊标记for y in range(0, self.__gameSize):for x in range(0, self.__gameSize):point = self.getOuterLeftTopPoint(Point(x, y))if self.__map[y][x] != self.EMPTY:#打散重绘时,不再置空的图标self.canvas.create_image((point.x, point.y),image=self.__icons[self.__map[y][x]], anchor='nw', tags = 'im%d%d' % (x, y))'''根据两点不同的位置,获取对应边的中心连接点。例如目标图标在左边,就取靠左的一边的中点。如果连接目标在右边,就取靠右一边的中点坐标。'''def getOuterFitCenterPoint(self, formerPoint, corner):_formerPoint = self.getOuterCenterPoint(formerPoint)if _formerPoint.y>corner.y:fitCenterPoint = Point(self.getX(formerPoint.x) + int(self.__iconWidth / 2),self.getY(formerPoint.y))#顶边elif _formerPoint.y<corner.y:fitCenterPoint = Point(self.getX(formerPoint.x) + int(self.__iconWidth / 2),self.getY(formerPoint.y) + int(self.__iconHeight))#底边elif _formerPoint.y == corner.y:if _formerPoint.x > corner.x:fitCenterPoint = Point(self.getX(formerPoint.x),self.getY(formerPoint.y)+int(self.__iconHeight / 2))#左边elif _formerPoint.x < corner.x:fitCenterPoint = Point(self.getX(formerPoint.x) + int(self.__iconWidth),self.getY(formerPoint.y)+int(self.__iconHeight / 2))#右边return fitCenterPoint'''获取内部坐标对应矩形左上角顶点坐标'''def getOuterLeftTopPoint(self, point):return Point(self.getX(point.x), self.getY(point.y))'''获取内部坐标对应矩形中心坐标'''def getOuterCenterPoint(self, point):return Point(self.getX(point.x) + int(self.__iconWidth / 2), self.getY(point.y) + int(self.__iconHeight / 2))def getX(self, x):# x * 40 + 25return x * self.__iconWidth + self.__deltadef getY(self, y):return y * self.__iconHeight + self.__delta'''获取内部坐标'''def getInnerPoint(self, point):x = -1y = -1for i in range(0, self.__gameSize):x1 = self.getX(i)x2 = self.getX(i + 1)if point.x >= x1 and point.x < x2:x = ifor j in range(0, self.__gameSize):j1 = self.getY(j)j2 = self.getY(j + 1)if point.y >= j1 and point.y < j2:y = jreturn Point(x, y)'''创建一块蒙板,覆盖到选中的图形上'''def create_mask(self,x1, y1, x2, y2, **kwargs):#(0,0,0)代表黑色的RGB,127代表alpha透明度,(x2-x1, y2-y1)指长宽image = Image.new('RGBA', (x2-x1, y2-y1), (0,0,0,127))self.__images = [ImageTk.PhotoImage(image)]#这里一定要用一个实例变量存储,局部变量没有效果,原因不清楚self.canvas.create_image(x1, y1, image=self.__images[0], anchor='nw',tags = "image_mask")'''选择的区域变红,point为内部坐标'''def drawSelectedArea(self, point):pointLT = self.getOuterLeftTopPoint(point)pointRB = self.getOuterLeftTopPoint(Point(point.x + 1, point.y + 1))self.canvas.create_rectangle(pointLT.x, pointLT.y, pointRB.x - 1, pointRB.y - 1, outline = 'red', tags = "rectRedOne", width=2)#蒙板self.create_mask(pointLT.x, pointLT.y, pointRB.x - 1, pointRB.y - 1, fill='skyblue', alpha=.5,width=0)'''消除连通的两个块'''def ClearLinkedBlocks(self, p1, p2):self.__map[p1.y][p1.x] = self.EMPTYself.__map[p2.y][p2.x] = self.EMPTYself.canvas.delete('im%d%d' % (p1.x, p1.y))self.canvas.delete('im%d%d' % (p2.x, p2.y))'''地图上该点是否为空'''def isEmptyInMap(self, point):if self.__map[point.y][point.x] == self.EMPTY:return Trueelse:return False'''获取两个点连通类型'''def getLinkType(self, p1, p2):# 首先判断两个方块中图片是否相同if self.__map[p1.y][p1.x] != self.__map[p2.y][p2.x]:return { 'type': self.NONE_LINK }if self.isStraightLink(p1, p2):return {'type': self.STRAIGHT_LINK}res = self.isOneCornerLink(p1, p2)if res:return {'type': self.ONE_CORNER_LINK,'p1': res}res = self.isTwoCornerLink(p1, p2)if res:return {'type': self.TWO_CORNER_LINK,'p1': res['p1'],'p2': res['p2']}return {'type': self.NONE_LINK}'''直连'''def isStraightLink(self, p1, p2):start = -1end = -1# 水平if p1.y == p2.y:# 大小判断if p2.x < p1.x:start = p2.xend = p1.xelse:start = p1.xend = p2.xfor x in range(start + 1, end):if self.__map[p1.y][x] != self.EMPTY:return Falsereturn Trueelif p1.x == p2.x:if p1.y > p2.y:start = p2.yend = p1.yelse:start = p1.yend = p2.yfor y in range(start + 1, end):if self.__map[y][p1.x] != self.EMPTY:return Falsereturn Truereturn Falsedef isOneCornerLink(self, p1, p2):pointCorner = Point(p1.x, p2.y)if self.isStraightLink(p1, pointCorner) and self.isStraightLink(pointCorner, p2) and self.isEmptyInMap(pointCorner):return pointCornerpointCorner = Point(p2.x, p1.y)if self.isStraightLink(p1, pointCorner) and self.isStraightLink(pointCorner, p2) and self.isEmptyInMap(pointCorner):return pointCornerdef isTwoCornerLink(self, p1, p2):for y in range(-1, self.__gameSize + 1):pointCorner1 = Point(p1.x, y)pointCorner2 = Point(p2.x, y)if y == p1.y or y == p2.y:continueif y == -1 or y == self.__gameSize:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner2, p2):return {'p1': pointCorner1, 'p2': pointCorner2}else:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner1, pointCorner2) and self.isStraightLink(pointCorner2, p2) and self.isEmptyInMap(pointCorner1) and self.isEmptyInMap(pointCorner2):return {'p1': pointCorner1, 'p2': pointCorner2}# 横向判断for x in range(-1, self.__gameSize + 1):pointCorner1 = Point(x, p1.y)pointCorner2 = Point(x, p2.y)if x == p1.x or x == p2.x:continueif x == -1 or x == self.__gameSize:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner2, p2):return {'p1': pointCorner1, 'p2': pointCorner2}else:if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner1, pointCorner2) and self.isStraightLink(pointCorner2, p2) and self.isEmptyInMap(pointCorner1) and self.isEmptyInMap(pointCorner2):return {'p1': pointCorner1, 'p2': pointCorner2}class Point():def __init__(self, x, y):self.x = xself.y = ydef isUserful(self):if self.x >= 0 and self.y >= 0:return Trueelse:return False'''判断两个点是否相同'''def isEqual(self, point):if self.x == point.x and self.y == point.y:return Trueelse:return False'''克隆一份对象'''def clone(self):return Point(self.x, self.y)'''改为另一个对象'''def changeTo(self, point):self.x = point.xself.y = point.y'''显示坐标'''def __repr__(self):return f"x={self.x},y={self.y}"MainWindow()

再提一点,就是游戏地图的制作,原理就是找25张图片,把它们压缩成40*40像素的图标,然后横排合成一张图。在网上找了很久,也没发现靠谱的软件,其实用python程序就能实现这个功能。

代码来源:

使用Python批量拼接图片_python多图合并成一张图_谢欣桁的博客-CSDN博客

稍微改了下:

import os
import math
from PIL import Imagedef merge_images(image_folder, output_file, n, m):# 获取所有图像文件的列表image_files = [os.path.join(image_folder, f) for f in os.listdir(image_folder) if f.endswith('.png')]# 计算每个小图像的大小和大图像的大小image_count = len(image_files)if image_count == 0:print('No image files found in the directory:', image_folder)return# 计算小图像的大小以及大图像的大小img = Image.open(image_files[0])
#     img_size0 = img.size[0]
#     img_size1 = img.size[1]img_size0 = 40img_size1 = 40new_img_size0 = img_size0 * nnew_img_size1 = img_size1 * m# 创建一个新的大图像new_img = Image.new('RGB', (new_img_size0, new_img_size1), 'white')# 将所有小图像粘贴到新图像的正确位置for i, f in enumerate(image_files):row = int(i / n)col = i % nimg = Image.open(f)img = img.resize((img_size0, img_size1))new_img.paste(img, (col * img_size0, row * img_size1))# 保存大图像new_img.save(output_file)# 用法示例
image_folder = 'C:/Users/Administrator/Desktop/图标/卡通' #目录下放25张png图片
output_file = 'C:/Users/Administrator/Desktop/图标/卡通/new5.png'#运行程序合成一张图
n = 25  # 每行显示的图像数
m = 1  # 每列显示的图像数
merge_images(image_folder, output_file, n, m)

2023.6.16日更新:通过chatGPT对部分代码进行了改进,代码精简了一些。

精简版代码如下:

# -*- coding: utf-8 -*-from tkinter import BOTH,FIRST,LAST,GROOVE,FLAT
import os, random
import tkinter as tk
import tkinter.messagebox
import numpy as np
from PIL import Image, ImageTk
import time
from playsound import playsoundclass MainWindow():__gameTitle = "连连看游戏"__windowWidth = 600__windowHeigth = 500__gameSize = 10 # 游戏尺寸__iconKind = __gameSize * __gameSize / 4 # 小图片种类数量__iconWidth = 40__iconHeight = 40__map = [] # 游戏地图__delta = 25__isFirst = True__isGameStart = False__formerPoint = NoneEMPTY = -1NONE_LINK = 0STRAIGHT_LINK = 1ONE_CORNER_LINK = 2TWO_CORNER_LINK = 3__images = []#蒙板阴影图像__level = 1#游戏关卡__point_position = {}__score = 0def __init__(self):self.__root = tk.Tk()self.__root.title(self.__gameTitle)self.centerWindow(self.__windowWidth, self.__windowHeigth)self.__root.minsize(460, 460)self.__addComponets()self.new_game()self.__root.mainloop()#菜单栏中添加一个叫“游戏”的菜单项def __addComponets(self):#菜单栏self.menubar = tk.Menu(self.__root, bg="lightgrey", fg="black")#子菜单self.file_menu = tk.Menu(self.menubar, tearoff=0, bg="lightgrey", fg="black")self.file_menu.add_command(label="新游戏", command=self.new_game, accelerator="Ctrl+N")self.menubar.add_cascade(label="游戏", menu=self.file_menu)self.__root.configure(menu=self.menubar)self.__root.bind('<Control-n>',self.new_game)#实现快捷键功能ctrl+N#self.canvas = tk.Canvas(self.__root, bg = '#D3D3D3', width = 450, height = 450,cursor="hand2")self.canvas.grid(row=0,column=0,sticky='N',pady = 5,rowspan=2,padx=(50,0))self.canvas.bind('<Button-1>', self.clickCanvas)#分数面板self.label_score = tkinter.Label(self.__root,width=10,height=2,font=('黑体',13,'bold'),fg="#802A2A",bg="#F5DEB3")self.label_score.grid(row=0,column=1)#显示当前可连接数self.label_linknums = tkinter.Label(self.__root,width=10,height=2,font=('黑体',13,'bold'),fg="#228B22")self.label_linknums.grid(row=1,column=1)'''判断两个接点之间是否能连通'''def __isLink(self,fromPoint,point):return self.isStraightLink(fromPoint,point) or self.isOneCornerLink(fromPoint,point) or self.isTwoCornerLink(fromPoint,point)'''获取提示,逻辑有点复杂,主要解决以下问题:1.获取当前地图上所有的图标;2.然后遍历,计算有多少对可连通的图标;3.如果有三个或者四个相同的图标可以相互连通,要进行判断,以勉出现重复计算。'''def getPromptPoint(self):_link_point = []_icons_arr = self.__map.flatten()#先转化为一维数组,便于操作for i in set(_icons_arr):if i == self.EMPTY: continue #忽略为空的坐标_arr = np.where(_icons_arr==i)[0]#元组的第一个元素,是由i元素的索引组成的数组if _arr.size == 4:#剩下4个相同图标_point1 = Point(int(_arr[0]%10),int(_arr[0]/10))# 计算x,y坐标_point2 = Point(int(_arr[1]%10),int(_arr[1]/10))_point3 = Point(int(_arr[2]%10),int(_arr[2]/10))_point4 = Point(int(_arr[3]%10),int(_arr[3]/10))#4个接点6条线,但最多只能计算2条线,再多就会出现重复计数if self.__isLink(_point1,_point2):_link_point.append((_point1,_point2))if self.__isLink(_point3,_point4):_link_point.append((_point3,_point4))elif self.__isLink(_point1,_point3):_link_point.append((_point1,_point3))if self.__isLink(_point2,_point4):_link_point.append((_point2,_point4))elif self.__isLink(_point1,_point4):_link_point.append((_point1,_point4))if self.__isLink(_point2,_point3):_link_point.append((_point2,_point3))elif self.__isLink(_point2,_point3):_link_point.append((_point2,_point3))elif self.__isLink(_point2,_point4):_link_point.append((_point2,_point4))elif self.__isLink(_point3,_point4):_link_point.append((_point3,_point4))    elif _arr.size == 2:#剩下2个相同图标_point1 = Point(int(_arr[0]%10),int(_arr[0]/10))# 计算x,y坐标_point2 = Point(int(_arr[1]%10),int(_arr[1]/10))if self.__isLink(_point1,_point2):_link_point.append((_point1,_point2))self.label_linknums['text'] = f"{len(_link_point)}连"print("_link_point",_link_point)return _link_pointdef centerWindow(self, width, height):#700 500screenwidth = self.__root.winfo_screenwidth()#窗口距离屏幕左边的宽screenheight = self.__root.winfo_screenheight()#窗口距离屏幕顶部的高size = '%dx%d+%d+%d' % (width, height, (screenwidth - width)/2, (screenheight - height)/2)self.__root.geometry(size)def add_score(self):self.__score += 2 #消除一对图标,分数加2self.set_label_text()def set_label_text(self):self.label_score['text'] = f"{self.__score}分"'''这个方法需要注意的积分问题:1.开始新游戏时,将积分清零;2.开始下一关时,将前一关的积分累积起来。'''def new_game(self, event=None,level=1,score = 0):self.__score = scoreself.set_label_text()self.__level = levelself.extractSmallIconList()self.iniMap()self.drawMap()self.getPromptPoint()self.__isGameStart = Truedef clickCanvas(self, event):if not self.__isGameStart:return# 确认有效点击坐标point = self.getInnerPoint(Point(event.x, event.y))if not point.isUserful() or self.isEmptyInMap(point):return#__isFirst在三种情况下为True,1.游戏开始的时候;2.同一个图标点击了两次;3.成功消除一组图标。if self.__isFirst:self.drawSelectedArea(point)self.__isFirst= Falseself.__formerPoint = pointelse:if self.__formerPoint.isEqual(point):#两次点击的是同一个图标self.__isFirst = Trueself.canvas.delete("rectRedOne")self.canvas.delete('image_mask')else:linkType = self.getLinkType(self.__formerPoint, point)if linkType['type'] != self.NONE_LINK:#画连接线self.draw_link_line(self.__formerPoint,point,linkType)playsound("music2.mp3")time.sleep(.5)# 显示画线的延迟self.ClearLinkedBlocks(self.__formerPoint, point)self.canvas.delete("rectRedOne")self.canvas.delete("linkline")self.canvas.delete('image_mask')#增加分数self.add_score()_link_point = self.getPromptPoint()self.__isFirst = Trueif len(_link_point) == 0:if self.isGameEnd():if tk.messagebox.askokcancel('确认操作', '是否进入下一关?'):self.__level = self.__level+1self.new_game(level = self.__level,score = self.__score)else:self.__isGameStart = Falseelse: #没有可连通图标,且当前地图上还有图标存在,游戏未结束时,打散当前地图tk.messagebox.showinfo("提示", "已没有可连通图标,需重新打散!")self.shuffleMap()self.drawMap()self.getPromptPoint()else:self.__formerPoint = pointself.canvas.delete("rectRedOne")self.drawSelectedArea(point)# 判断游戏是否结束def isGameEnd(self):return np.all(self.__map == self.EMPTY)'''消除图像前,把两个图像之间的连接线画出来。'''def draw_link_line(self,formerPoint,point,linkType):if linkType['type'] == self.STRAIGHT_LINK:p1 = self.getOuterCenterPoint(formerPoint)p2 = self.getOuterCenterPoint(point)#arrow表示线的箭头样式,默认不带箭头,参数值 FIRST表示添加箭头带线段开始位置,LAST表示到末尾占位置,BOTH表示两端均添加self.canvas.create_line((p1.x, p1.y),(p2.x, p2.y),fill = 'red',tags = 'linkline',width=3, arrow=LAST)if linkType['type'] == self.ONE_CORNER_LINK:           corner1 = self.getOuterCenterPoint(linkType["p1"])p1 = self.getOuterFitCenterPoint(formerPoint,corner1)p2 = self.getOuterFitCenterPoint(point,corner1)self.canvas.create_line((p1.x, p1.y),(corner1.x, corner1.y),fill = 'red',tags = 'linkline',width=3)self.canvas.create_line((p2.x, p2.y),(corner1.x, corner1.y),fill = 'red',tags = 'linkline',width=3, arrow=FIRST)elif linkType['type'] == self.TWO_CORNER_LINK:corner1 = self.getOuterCenterPoint(linkType["p1"])corner2 = self.getOuterCenterPoint(linkType["p2"])p1 = self.getOuterFitCenterPoint(formerPoint,corner1)p2 = self.getOuterFitCenterPoint(point,corner2)self.canvas.create_line((p1.x, p1.y),(corner1.x, corner1.y),fill = 'red',tags = 'linkline',width=3)self.canvas.create_line((p2.x, p2.y),(corner2.x, corner2.y),fill = 'red',tags = 'linkline',width=3, arrow=FIRST)self.canvas.create_line((corner1.x, corner1.y),(corner2.x, corner2.y),fill = 'red',tags = 'linkline',width=3)self.canvas.update()'''提取小头像数组'''def extractSmallIconList(self):self.__icons = []imageSouce = Image.open(f"图片/new{self.__level}.png")for index in range(0, int(self.__iconKind)):#0-24region = imageSouce.crop((self.__iconWidth * index, 0, self.__iconWidth * index + self.__iconWidth - 1, self.__iconHeight - 1))self.__icons.append(ImageTk.PhotoImage(region))'''初始化地图 存值为0-24'''def iniMap(self):self.__map = [] # 重置地图_tmpRecords = []# 0-24,一共25个图标,每个图标出现4次,总共出现100次_total = self.__gameSize * self.__gameSize_tmpRecords = np.linspace(0, self.__iconKind, _total, endpoint = False,dtype=int) #用25个图标构建100个位置np.random.shuffle(_tmpRecords)#重新打散洗牌self.__map = _tmpRecords.reshape((10,10))#将一维数组100,转化为二维10*10'''初始化地图 存值为0-24,当前地图中,如果没有可连通路线、进入死局时,将现有的图标进行打散重新洗牌。''' def shuffleMap(self):_icons_arr = self.__map.flatten()#先转化为一维数组,便于操作self.__map = np.array([i for i in _icons_arr if i != self.EMPTY])np.random.shuffle(self.__map)#洗牌self.__map = np.pad(self.__map, (0, self.__gameSize**2 - len(self.__map)), 'constant', constant_values=self.EMPTY)#补齐剩下地图的值-1self.__map = self.__map.reshape((self.__gameSize, self.__gameSize))'''根据地图绘制图像1.在使用create_image()函数时,需要先通过Pillow库(或其他图片处理库)读取图片文件并生成图片对象,然后再将这个图片对象传递给create_image()函数,在指定的坐标位置将图片添加到画布上。2.canvas.create_image(x, y, image=图像对象, anchor=定位点),x和y表示图像锚点在画布上的位置,即图像在画布上的左上角坐标。image参数是一个tkinter中的PhotoImage()对象,它可以指定要加载的图像文件。anchor参数是一个字符串,用于指定图像锚点的位置,可以是"nw"(左上角)、"n"(上)、"ne"(右上角)、"w"(左)、"center"(中心)、"e"(右)、"sw"(左下角)或"s"(下)。如果不指定anchor参数,则默认为“center”。'''def drawMap(self):self.canvas.delete("all")y, x = np.where(self.__map != self.EMPTY)for i in range(len(x)):point = self.getOuterLeftTopPoint(Point(x[i], y[i]))self.canvas.create_image((point.x, point.y), image=self.__icons[self.__map[y[i]][x[i]]], anchor='nw', tags='im%d%d' % (x[i], y[i]))'''根据两点不同的位置,获取对应边的中心连接点。例如目标图标在左边,就取靠左的一边的中点。如果连接目标在右边,就取靠右一边的中点坐标。'''   def getOuterFitCenterPoint(self,former_point, corner):_former_point = self.getOuterCenterPoint(former_point)mapping = {_former_point.y > corner.y: (self.getX(former_point.x) + int(self.__iconWidth / 2), self.getY(former_point.y)),_former_point.y < corner.y: (self.getX(former_point.x) + int(self.__iconWidth / 2), self.getY(former_point.y) + int(self.__iconHeight)),_former_point.y == corner.y and _former_point.x > corner.x: (self.getX(former_point.x), self.getY(former_point.y) + int(self.__iconHeight / 2)),_former_point.y == corner.y and _former_point.x < corner.x: (self.getX(former_point.x) + int(self.__iconWidth),self.getY(former_point.y) + int(self.__iconHeight / 2))}x, y = mapping[True]return Point(x, y)'''获取内部坐标对应矩形左上角顶点坐标'''def getOuterLeftTopPoint(self, point):return Point(self.getX(point.x), self.getY(point.y))'''获取内部坐标对应矩形中心坐标'''def getOuterCenterPoint(self, point):return Point(self.getX(point.x) + int(self.__iconWidth / 2), self.getY(point.y) + int(self.__iconHeight / 2))def getX(self, x):# x * 40 + 25return x * self.__iconWidth + self.__deltadef getY(self, y):return y * self.__iconHeight + self.__delta'''获取内部坐标,将点击的像素坐标转换为point坐标,即icon位置坐标'''def getInnerPoint(self, point):x = -1y = -1for i in range(0, self.__gameSize):x1 = self.getX(i)x2 = self.getX(i + 1)if point.x >= x1 and point.x < x2:x = ifor j in range(0, self.__gameSize):j1 = self.getY(j)j2 = self.getY(j + 1)if point.y >= j1 and point.y < j2:y = jreturn Point(x, y)'''创建一块蒙板,覆盖到选中的图形上'''def create_mask(self,x1, y1, x2, y2, **kwargs):#(0,0,0)代表黑色的RGB,127代表alpha透明度,(x2-x1, y2-y1)指长宽image = Image.new('RGBA', (x2-x1, y2-y1), (0,0,0,127))self.__images = [ImageTk.PhotoImage(image)]#这里一定要用一个实例变量存储,局部变量没有效果,原因不清楚self.canvas.create_image(x1, y1, image=self.__images[0], anchor='nw',tags = "image_mask")'''选择的区域变红,point为内部坐标'''def drawSelectedArea(self, point):pointLT = self.getOuterLeftTopPoint(point)pointRB = self.getOuterLeftTopPoint(Point(point.x + 1, point.y + 1))self.canvas.create_rectangle(pointLT.x, pointLT.y, pointRB.x - 1, pointRB.y - 1, outline = 'red', tags = "rectRedOne", width=2)#蒙板self.create_mask(pointLT.x, pointLT.y, pointRB.x - 1, pointRB.y - 1, fill='skyblue', alpha=.5,width=0)'''消除连通的两个块'''def ClearLinkedBlocks(self, p1, p2):self.__map[p1.y][p1.x] = self.EMPTYself.__map[p2.y][p2.x] = self.EMPTYself.canvas.delete('im%d%d' % (p1.x, p1.y))self.canvas.delete('im%d%d' % (p2.x, p2.y))'''地图上该点是否为空'''def isEmptyInMap(self, point):return self.__map[point.y][point.x] == self.EMPTY'''获取两个点连通类型'''def getLinkType(self, p1, p2):# 首先判断两个方块中图片是否相同if self.__map[p1.y][p1.x] != self.__map[p2.y][p2.x]:return { 'type': self.NONE_LINK }if self.isStraightLink(p1, p2):return {'type': self.STRAIGHT_LINK}res = self.isOneCornerLink(p1, p2)if res:return {'type': self.ONE_CORNER_LINK,'p1': res}res = self.isTwoCornerLink(p1, p2)if res:return {'type': self.TWO_CORNER_LINK,'p1': res['p1'],'p2': res['p2']}return {'type': self.NONE_LINK}'''直连'''def isStraightLink(self, p1, p2):if p1.y == p2.y:start, end = sorted([p1.x, p2.x])for x in range(start + 1, end):if self.__map[p1.y][x] != self.EMPTY:return Falseelif p1.x == p2.x:start, end = sorted([p1.y, p2.y])for y in range(start + 1, end):if self.__map[y][p1.x] != self.EMPTY:return Falseelse:return Falsereturn Truedef isOneCornerLink(self, p1, p2):pointCorner = Point(p1.x, p2.y)if self.isStraightLink(p1, pointCorner) and self.isStraightLink(pointCorner, p2) and self.isEmptyInMap(pointCorner):return pointCornerpointCorner = Point(p2.x, p1.y)if self.isStraightLink(p1, pointCorner) and self.isStraightLink(pointCorner, p2) and self.isEmptyInMap(pointCorner):return pointCorner'''这可能是此文件最复杂的一个函数,for循环中的第一个数组是纵向比较(x不变),第二个是横向比较(y不变),合在一起可以减少代码量'''def isTwoCornerLink(self, p1, p2):for x1,y1,x2,y2 in [(p1.x, y, p2.x, y) for y in range(-1, self.__gameSize + 1)] + [(x, p1.y, x, p2.y) for x in range(-1, self.__gameSize + 1)]:pointCorner1 = Point(x1, y1) pointCorner2 = Point(x2, y2)if pointCorner1==p1 or pointCorner2==p2:continue #排除p1与p2两个点if y1 == -1 or y1 == self.__gameSize or x2 == -1 or x2 == self.__gameSize: #处理最外四条边上的双拐点if self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner2, p2):return {'p1': pointCorner1, 'p2': pointCorner2}elif self.isStraightLink(p1, pointCorner1) and self.isStraightLink(pointCorner1, pointCorner2) and self.isStraightLink(pointCorner2, p2) and self.isEmptyInMap(pointCorner1) and self.isEmptyInMap(pointCorner2):return {'p1': pointCorner1, 'p2': pointCorner2}class Point():def __init__(self, x, y):self.x = xself.y = ydef isUserful(self):return self.x >= 0 and self.y >= 0'''判断两个点是否相同'''def isEqual(self, point):return self.x == point.x and self.y == point.y'''克隆一份对象'''def clone(self):return Point(self.x, self.y)'''改为另一个对象'''def changeTo(self, point):self.x = point.xself.y = point.y'''显示坐标'''def __repr__(self):return f"x={self.x},y={self.y}"MainWindow()

改进版尽管功能更加强大,但有点复杂,如果初学,可以先看基础版。

增强版代码包(含图片、音效文件):

https://download.csdn.net/download/qiuqiuit/87895753

基础版代码包(包含图片): 

https://download.csdn.net/download/qiuqiuit/87895720

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/26350.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI绘图高级篇 第7篇 MJ以图换图-卡通头像

大家好&#xff0c;我是菜鸟哥 这个是我们MJ系列的第7篇&#xff0c;以前在会员群里发过&#xff0c;就是把头像做成卡通或者3D的效果还是很酷&#xff0c;或者是迪斯尼风格的。其实非常简单&#xff0c;就是用了一个MJ的以图换图的功能&#xff0c;今天给大家详细的说一下。 前…

谷歌大脑与DeepMind合二为一!为对抗OpenAI打造全新大模型

梦晨 发自 凹非寺量子位 | 公众号 QbitAI 谷歌突发大动作&#xff0c;旗下两大顶级AI团队谷歌大脑与DeepMind合二为一&#xff01; 双方人才将并肩作战&#xff0c;以谷歌算力资源为后盾&#xff0c;只为一个目标&#xff1a; 加速AI研究。 △原DeepMind研究VP 而且体量小很多的…

重磅研究!32篇论文硬核盘点2022年度AI热点

新智元报道 编辑&#xff1a;编辑部 【导读】2022超全的AI圈研究合集在这&#xff01;知名博主Louis Bouchard自制视频讲解加短篇分析&#xff0c;对小白也超级友好。 虽然世界仍在复苏&#xff0c;但研究并没有放慢其狂热的步伐&#xff0c;尤其是在人工智能领域。 此外&…

玩元宇宙血亏后 蓝色光标梭哈AI也挺悬

蓝色光标2022年年度报告出炉&#xff0c;巨亏21.75 亿元&#xff0c;其中20.38亿亏损因商誉、无形资产及其他资产减值造成&#xff0c;而在实际亏损业务中&#xff0c;元宇宙占比不小。 蓝色光标在元宇宙领域的布局&#xff0c;主要通过三家子公司实施&#xff0c;分别为蓝色宇…

AGI复仇者联盟!谷歌大脑与DeepMind官宣合体

真急了&#xff01;谷歌大脑与DeepMind合并&#xff0c;合力对抗ChatGPT。 编译 | 吴菲凝 编辑 | 李水青 智东西4月21日报道&#xff0c;当地时间4月20日&#xff0c;谷歌母公司Alphabet首席执行官桑达尔皮查伊&#xff08;Sundar Pichai&#xff09;在官网发文宣布&#xff…

ChatGPT实战100例 - (06) 10倍速可视化组织架构与人员协作流程

文章目录 ChatGPT实战100例 - (06) 10倍速可视化组织架构与人员协作流程一、需求与思路二、 组织架构二、 人员协作四、 总结 ChatGPT实战100例 - (06) 10倍速可视化组织架构与人员协作流程 一、需求与思路 管理研发团队的过程中&#xff0c;组织架构与人员协作流程的可视化是…

微软.NET StockTrader(股票交易示例程序)

Microsoft .NET StockTrader ReadMe 微软 .NET 股票交易示例程序帮助文件&#xff08;中文版&#xff09; http://tb.blog.csdn.net/TrackBack.aspx?PostId1792577 http://msdn2.microsoft.com/zh-cn/netframework/bb499684.aspx .NET StockTrader 示例应用程序 用于说明…

用上最新的 GitHub Copilot Chat 了!

本文首发于我的“职场圈”知识星球&#xff1a; 大家好&#xff01;我是韩老师。 两周前&#xff0c;GitHub Copilot X 横空出世&#xff1a; 重磅&#xff01;GitHub Copilot X 来了&#xff01; 带来了五大功能&#xff1a; GitHub Copilot Chat&#xff08;边写代码边跟 AI …

Copilot Hub 基于私有数据的人格化AI 平台 - 创建自定义ChatGPT知识库AI的简明操作指南...

Copilot Hub 是一个帮助你基于私有数据创建智能知识库 & 人格化 AI 的平台。你可以基于文档、网站、Notion database 或其他数据源在几分钟内创建一个自定义的 ChatGPT。 https://app.copilothub.ai/copilots 需要先登录一下 Copilot Hub 输入邮箱就能登录成功了 我现在使用…

曙光中学2021年高考成绩查询,重温上海40所高中2020年高考成绩(建议收藏)

复旦附中 2020年&#xff0c;复旦大学附属中学共有12位同学被清华大学录取&#xff0c;11位同学被北京大学录取&#xff0c;97位同学被复旦大学录取&#xff0c;44位同学被上海交通大学录取。 上海交大附中 近年&#xff0c;我校大批优秀毕业生被清华、北大、交大、复旦等名校录…

2021上海高考小三门成绩查询,2021上海高考等级考分数怎么划分等级的

上海新高考33模式&#xff0c;与从前相比新增了一个等级考的概念。那么很多考生不知道上海高考等级考分数怎么划分等级的&#xff0c;下面一起带大家来看看。 关于等级考 改革之后的上海高考&#xff0c;统一考试的科目为&#xff1a; 大三门&#xff1a;语文、数学、英语&…

高安二中2021年高考成绩查询,2021年高考来了!高安9287名考生参加

一年一度的高考来了 日前 高品君从市高招办获悉 我市2021年普通高考 共报名考生9287人 比去年增加710人 其中文史类3522人 理工类5004人 三校生类761人 考试将于6月7日至9日举行 全市共设有 高安中学、高安二中 吴有训实验学校、筠阳实验学校4个考点 296个考场 2021年4月单招录…

如何快速、全面、深入地掌握一门编程语言

思考路线 如何快速&#xff1f; 什么样的Demo才能让人觉得你掌握了它&#xff1f; 空 判断&#xff1a;构造一个可以判断所有空的 is_empty 函数 for 循环&#xff1a;i 和 集合迭代两种 时间获取&#xff1a;年/月/日 时分秒 时间戳与时间格式互转 休眠时间函数 字符串处理…

同一台电脑安装多个版本的idea

同一台电脑安装多个版本的idea 需求 ​ 当前&#xff0c;电脑上已经安装了idea2019.3.exe版本&#xff0c;发现此版本太旧了&#xff0c;无法体验新版idea2023.1的很多功能及优化&#xff0c;新出的好多idea插件都不支持idea2019.3版本了&#xff0c;比如&#xff0c;支持Cha…

2023 年 8 大 Web 开发趋势预测

&#xff08;元&#xff09;框架 单页应用 (SPA) 及相关框架&#xff08;例如 React.js、Vue.js、Svelte.js&#xff09;都已经存在了很多年。然而&#xff0c;随着这些解决方案之上的元框架的兴起&#xff0c;可以看到应用从客户端渲染&#xff08;CSR&#xff09;转向服务端…

chatgpt赋能python:Python绘图教程:将画笔移动到绝对位置的方法

Python绘图教程&#xff1a;将画笔移动到绝对位置的方法 Python作为一门高级编程语言&#xff0c;设计初衷是让编程变得简单、易学、易用&#xff0c;且支持多种编程范式&#xff0c;其中产生了让人惊艳的绘图模块——Turtle&#xff08;海龟&#xff09;。 在这篇教程中&…

postgresql 报错 FATAL: no pg_hba.conf entry for host 未配置允许远程连接 解决方法

目录 错误现象 问题原因 解决方案 1、进入到data目录下&#xff0c;找到pg_hba.conf文件 2、修改文件 3、进入到postgres用户下&#xff0c;执行命名 4、连接成功 错误现象 问题原因 这是在远程连接时pg_hba.conf文件没有配置正确。 pg_hba.conf文件在Postgre安装文件目…

探寻生机 | 数说故事助力微播易第七届风向大会,研判新风向,洞察新趋势

“过去一年&#xff0c;有的人用ChatGPT谁出具的北京烤鸭图片最准确搞怪&#xff0c;有的人却已经利用虚拟主播单场带货百万&#xff1b;有的人正在被AIGC淘汰&#xff0c;有的人却通过人机协作实现20秒制作100张创意图&#xff1b;有的百万粉丝接不到广告&#xff0c;有的仅靠…

打磨极致音频体验,声网重磅发布新一代音频技术智能引擎“凤鸣AI引擎”

RTE场景不断丰富&#xff0c;高音质互动需求急需满足&#xff0c;声网凤鸣AI引擎应时而生&#xff1a; 1、一次性解决100种突发性噪声&#xff0c;同时兼顾高保真。 2、利用算法对环境中产生的回声混响进行有效抑制。 3、空间音频通过纯软件算法方案&#xff0c;模拟头部球面区…

ChatGPT最大对手谷歌Bard支持中文了!十级过关,看懂梗图,直接上手免费体验...

编辑&#xff1a;编辑部 【新智元导读】Bard又强了&#xff01;这次不仅支持中文等40种语言&#xff0c;还能上传图片做问答。 前两天&#xff0c;ChatGPT最强竞品Claude升级了二代&#xff0c;谷歌也不甘落后。 今天&#xff0c;最新版本的Bard来了&#xff0c;可以在提示中添…