欢迎关注 『Python黑科技』 系列,持续更新中
欢迎关注 『Python黑科技』 系列,持续更新中
实现效果
实现思路
链接: https://pan.baidu.com/s/1Q6peGnZe0k0zBalAGaJ8wg?pwd=pdux
提取码: pdux
搞一堆图片bar_00-----bar_09.png
如果有条件找到gif就更好了
imgs = [PhotoImage(file='images\\bar_0' + str(i) + '.png') for i in range(0, 10)] # 所有图标图案
经典连连看,随机生成图片元素,相邻图片元素的标记相同,可以消除。
实现代码
# @Time : 2022/2/9 20:22
# @Author : 南黎
# @FileName: 连连看.py
from tkinter import *
from tkinter.messagebox import *
from threading import Timer
import time
import random
class Point:
# 点类
def __init__(self, x, y):
self.x = x
self.y = y
# --------------------------------------
'''
判断选中的两个方块是否可以消除
'''
def IsLink(p1, p2):
if lineCheck(p1, p2):
return True
if OneCornerLink(p1, p2): # 一个转弯(折点)的联通方式
return True
if TwoCornerLink(p1, p2): # 两个转弯(折点)的联通方式
return True
return False
# ---------------------------
def IsSame(p1, p2):
if map[p1.x][p1.y] == map[p2.x][p2.y]:
print("clicked at IsSame")
return True
return False
def callback(event): # 鼠标左键事件代码
global Select_first, p1, p2
global firstSelectRectId, SecondSelectRectId
# print ("clicked at", event.x, event.y,turn)
x = (event.x) // 40 # 换算棋盘坐标
y = (event.y) // 40
print("clicked at", x, y)
if map[x][y] == " ":
showinfo(title="提示", message="此处无方块")
else:
if Select_first == False:
p1 = Point(x, y)
# 画选定(x1,y1)处的框线
firstSelectRectId = cv.create_rectangle(x * 40, y * 40, x * 40 + 40, y * 40 + 40, width=2, outline="blue")
Select_first = True
else:
p2 = Point(x, y)
# 判断第二次点击的方块是否已被第一次点击选取,如果是则返回。
if (p1.x == p2.x) and (p1.y == p2.y):
return
# 画选定(x2,y2)处的框线
print('第二次点击的方块', x, y)
# SecondSelectRectId=cv.create_rectangle(100,20,x*40+40,y*40+40,width=2,outline="yellow")
SecondSelectRectId = cv.create_rectangle(x * 40, y * 40, x * 40 + 40, y * 40 + 40, width=2,
outline="yellow")
print('第二次点击的方块', SecondSelectRectId)
cv.pack()
# 判断是否连通
if IsSame(p1, p2) and IsLink(p1, p2):
print('连通', x, y)
Select_first = False
# 画选中方块之间连接线
drawLinkLine(p1, p2)
# clearTwoBlock()
# time.sleep(0.6)
# clearFlag=True
t = Timer(timer_interval, delayrun) # 定时函数
t.start()
else: # 重新选定第一个方块
# 清除第一个选定框线
cv.delete(firstSelectRectId)
cv.delete(SecondSelectRectId)
# print('清除第一个选定框线')
# firstSelectRectId=SecondSelectRectId
# p1=Point(x,y) #设置重新选定第一个方块的坐标
Select_first = False
timer_interval = 0.3 # 0.3秒
# --------------------------------------
def delayrun():
clearTwoBlock() # 清除连线及方块
def clearTwoBlock(): # 清除连线及方块
# 延时0.1秒
# time.sleep(0.1)
# 清除第一个选定框线
cv.delete(firstSelectRectId)
# 清除第2个选定框线
cv.delete(SecondSelectRectId)
# 清空记录方块的值
map[p1.x][p1.y] = " "
cv.delete(image_map[p1.x][p1.y])
map[p2.x][p2.y] = " "
cv.delete(image_map[p2.x][p2.y])
Select_first = False
undrawConnectLine() # 清除选中方块之间连接线
def drawQiPan(): # 画棋盘
for i in range(0, 15):
cv.create_line(20, 20 + 40 * i, 580, 20 + 40 * i, width=2)
for i in range(0, 15):
cv.create_line(20 + 40 * i, 20, 20 + 40 * i, 580, width=2)
cv.pack()
def print_map(): # 输出map地图
global image_map
for x in range(0, Width): # 0--14
for y in range(0, Height): # 0--14
if (map[x][y] != ' '):
img1 = imgs[int(map[x][y])]
id = cv.create_image((x * 40 + 20, y * 40 + 20), image=img1)
image_map[x][y] = id
cv.pack()
for y in range(0, Height): # 0--14
for x in range(0, Width): # 0--14
print(map[x][y], end=' ')
print(",", y)
'''
* 同行同列情况消除方法 原理:如果两个相同的被消除元素之间的 空格数
spaceCount等于他们的(行/列差-1)则 两者可以联通消除
* x代表列,y代表行
* param p1 第一个保存上次选中点坐标的点对象
* param p2 第二个保存上次选中点坐标的点对象
'''
# 直接连通
def lineCheck(p1, p2):
absDistance = 0
spaceCount = 0
if (p1.x == p2.x or p1.y == p2.y): # 同行同列的情况吗?
print("同行同列的情况------")
# 同列的情况
if (p1.x == p2.x and p1.y != p2.y):
print("同列的情况")
# 绝对距离(中间隔着的空格数)
absDistance = abs(p1.y - p2.y) - 1
# 正负值
if p1.y - p2.y > 0:
zf = -1
else:
zf = 1
for i in range(1, absDistance + 1):
if (map[p1.x][p1.y + i * zf] == " "):
# 空格数加1
spaceCount += 1
else:
break; # 遇到阻碍就不用再探测了
# 同行的情况
elif (p1.y == p2.y and p1.x != p2.x):
print(" 同行的情况")
absDistance = abs(p1.x - p2.x) - 1
# 正负值
if p1.x - p2.x > 0:
zf = -1
else:
zf = 1
for i in range(1, absDistance + 1):
if (map[p1.x + i * zf][p1.y] == " "):
# 空格数加1
spaceCount += 1
else:
break; # 遇到阻碍就不用再探测了
if (spaceCount == absDistance):
# 可联通
print(absDistance, spaceCount)
print("行/列可直接联通")
return True
else:
print("行/列不能消除!")
return False
else:
# 不是同行同列的情况所以直接返回false
return False;
# --------------------------------------
# 第二种,直角连通
'''
直角连接,即X,Y坐标都不同的,可以用这个方法尝试连接
param first:选中的第一个点
param second:选中的第二个点
'''
def OneCornerLink(p1, p2):
# 第一个直角检查点,如果这里为空则赋予相同值供检查
checkP = Point(p1.x, p2.y)
# 第二个直角检查点,如果这里为空则赋予相同值供检查
checkP2 = Point(p2.x, p1.y);
# 第一个直角点检测
if (map[checkP.x][checkP.y] == " "):
if (lineCheck(p1, checkP) and lineCheck(checkP, p2)):
linePointStack.append(checkP)
print("直角消除ok", checkP.x, checkP.y)
return True
# 第二个直角点检测
if (map[checkP2.x][checkP2.y] == " "):
if (lineCheck(p1, checkP2) and lineCheck(checkP2, p2)):
linePointStack.append(checkP2)
print("直角消除ok", checkP2.x, checkP2.y)
return True
print("不能直角消除")
return False;
# -----------------------------------------
'''
#第三种,双直角连通
双直角联通判定可分两步走:
1. 在p1点周围4个方向寻找空格checkP
2. 调用OneCornerLink(checkP, p2)
3. 即遍历 p1 4 个方向的空格,使之成为 checkP,然后调用 OneCornerLink(checkP,
p2)判定是否为真,如果为真则可以双直角连同,否则当所有的空格都遍历完而没有找
到一个checkP使OneCornerLink(checkP, p2)为真,则两点不能连同
具体代码:
双直角连接方法
@param p1 第一个点
@param p2 第二个点
'''
def TwoCornerLink(p1, p2):
checkP = Point(p1.x, p1.y)
# 四向探测开始
for i in range(0, 4):
checkP.x = p1.x
checkP.y = p1.y
# 向下
if (i == 3):
checkP.y += 1
while ((checkP.y < Height) and map[checkP.x][checkP.y] == " "):
linePointStack.append(checkP)
if (OneCornerLink(checkP, p2)):
print("下探测OK")
return True
else:
linePointStack.pop()
checkP.y += 1
print("ssss", checkP.y, Height - 1)
# 补充两个折点都在游戏区域底侧外部
if checkP.y == Height: # 出了底部,则仅需判断p2能否也达到底部边界
z = Point(p2.x, Height - 1) # 底部边界点
if lineCheck(z, p2): # 两个折点在区域外部的底侧
linePointStack.append(Point(p1.x, Height))
linePointStack.append(Point(p2.x, Height))
print("下探测到游戏区域外部OK")
return True
# 向右
elif (i == 2):
checkP.x += 1
while ((checkP.x < Width) and map[checkP.x][checkP.y] == " "):
linePointStack.append(checkP)
if (OneCornerLink(checkP, p2)):
print("右探测OK")
return True
else:
linePointStack.pop()
checkP.x += 1
# 补充两个折点都在游戏区域右侧外部
if checkP.x == Width: # 出了右侧,则仅需判断p2能否也达到右部边界
z = Point(Width - 1, p2.y) # 右部边界点
if lineCheck(z, p2): # 两个折点在区域外部的底侧
linePointStack.append(Point(Width, p1.y))
linePointStack.append(Point(Width, p2.y))
print("右探测到游戏区域外部OK")
return True
# 向左
elif (i == 1):
checkP.x -= 1
while ((checkP.x >= 0) and map[checkP.x][checkP.y] == " "):
linePointStack.append(checkP)
if (OneCornerLink(checkP, p2)):
print("左探测OK")
return True
else:
linePointStack.pop()
checkP.x -= 1
# 向上
elif (i == 0):
checkP.y -= 1
while ((checkP.y >= 0) and map[checkP.x][checkP.y] == " "):
linePointStack.append(checkP)
if (OneCornerLink(checkP, p2)):
print("上探测OK")
return True
else:
linePointStack.pop()
checkP.y -= 1
# 四个方向都寻完都没找到适合的checkP点
print("两直角连接没找到适合的checkP点")
return False;
# ---------------------------
# 画连接线
def drawLinkLine(p1, p2):
if (len(linePointStack) == 0):
Line_id.append(drawLine(p1, p2))
else:
print(linePointStack, len(linePointStack))
if (len(linePointStack) == 1):
z = linePointStack.pop()
print("一折连通点z", z.x, z.y)
Line_id.append(drawLine(p1, z))
Line_id.append(drawLine(p2, z))
if (len(linePointStack) == 2):
z1 = linePointStack.pop()
print("2折连通点z1", z1.x, z1.y)
Line_id.append(drawLine(p2, z1))
z2 = linePointStack.pop()
print("2折连通点z2", z2.x, z2.y)
Line_id.append(drawLine(z1, z2))
Line_id.append(drawLine(p1, z2))
# 删除连接线
def undrawConnectLine():
while len(Line_id) > 0:
idpop = Line_id.pop()
cv.delete(idpop)
def drawLine(p1, p2):
print("drawLine p1,p2", p1.x, p1.y, p2.x, p2.y)
# cv.create_line( 40+20, 40+20,200,200,width=5,fill='red')
id = cv.create_line(p1.x * 40 + 20, p1.y * 40 + 20, p2.x * 40 + 20, p2.y * 40 + 20, width=5, fill='red')
# cv.pack()
return id
# --------------------------------------
def create_map(): # 产生map地图
global map
# 生成随机地图
# 将所有匹配成对的动物物种放进一个临时的地图中
tmpMap = []
m = (Width) * (Height) // 10
print('m=', m)
for x in range(0, m):
for i in range(0, 10): # 每种方块有10个
tmpMap.append(x)
random.shuffle(tmpMap)
for x in range(0, Width): # 0--14
for y in range(0, Height): # 0--14
map[x][y] = tmpMap[x * Height + y]
# --------------------------------------
def find2Block(event): # 自动查找
global firstSelectRectId, SecondSelectRectId
m_nRoW = Height
m_nCol = Width
bFound = False;
# 第一个方块从地图的0位置开始
for i in range(0, m_nRoW * m_nCol):
# 找到则跳出循环
if (bFound):
break
# 算出对应的虚拟行列位置
x1 = i % m_nCol
y1 = i // m_nCol
p1 = Point(x1, y1)
# 无图案的方块跳过
if (map[x1][y1] == ' '):
continue
# 第二个方块从前一个方块的后面开始
for j in range(i + 1, m_nRoW * m_nCol):
# 算出对应的虚拟行列位置
x2 = j % m_nCol
y2 = j // m_nCol
p2 = Point(x2, y2)
# 第二个方块不为空 且与第一个方块的动物相同
if (map[x2][y2] != ' ' and IsSame(p1, p2)):
# 判断是否可以连通
if (IsLink(p1, p2)):
bFound = True
break
# 找到后自动消除
if (bFound): # p1(x1,y1)与p2(x2,y2)连通
print('找到后', p1.x, p1.y, p2.x, p2.y)
# 画选定(x1,y1)处的框线
firstSelectRectId = cv.create_rectangle(x1 * 40, y1 * 40, x1 * 40 + 40, y1 * 40 + 40, width=2, outline="red")
# 画选定(x2,y2)处的框线
secondSelectRectId = cv.create_rectangle(x2 * 40, y2 * 40, x2 * 40 + 40, y2 * 40 + 40, width=2, outline="red")
# t=Timer(timer_interval,delayrun)#定时函数
# t.start()
return bFound
# 游戏主逻辑
root = Tk()
root.title("Python连连看 ")
imgs = [PhotoImage(file='images\\bar_0' + str(i) + '.png') for i in range(0, 10)] # 所有图标图案
Select_first = False # 是否已经选中第一块
firstSelectRectId = -1 # 被选中第一块地图对象
SecondSelectRectId = -1 # 被选中第二块地图对象
clearFlag = False
linePointStack = []
Line_id = []
Height = 10
Width = 10
map = [[" " for y in range(Height)] for x in range(Width)]
image_map = [[" " for y in range(Height)] for x in range(Width)]
cv = Canvas(root, bg='green', width=440, height=440)
# drawQiPan( )
cv.bind("<Button-1>", callback) # 鼠标左键事件
cv.bind("<Button-3>", find2Block) # 鼠标右键事件
cv.pack()
create_map() # 产生map地图
print_map() # 打印map地图
root.mainloop()
总结
大家喜欢的话,给个👍,点个关注!给大家分享更多有趣好玩的Python黑科技!
欢迎关注 『Python黑科技』 系列,持续更新中
欢迎关注 『Python黑科技』 系列,持续更新中
【Python黑科技】tkinter库实战7个小项目合集(保姆级图文+实现代码)
【Python黑科技】tkinter库实战制作一个计算器(保姆级图文+实现代码)
【Python黑科技】tkinter库实战制作一个记事本(保姆级图文+实现代码)
【Python黑科技】tkinter库实战用户的注册和登录(保姆级图文+实现代码)
【Python黑科技】tkinter库实战“2048”小游戏(保姆级图文+实现代码)
【Python黑科技】tkinter库实战“俄罗斯方块”小游戏(保姆级图文+实现代码)
【Python黑科技】tkinter库实战“贪吃蛇”小游戏(保姆级图文+实现代码)
【Python黑科技】tkinter库实战“连连看”小游戏(保姆级图文+实现代码)
【Python安装第三方库一行命令永久提高速度】
【使用PyInstaller打包exe】
【免登陆爬虫一键下载知乎文章图片(保姆级图文+实现代码)】
【孤独的程序员和AI机器人朋友聊天解闷(免费接口+保姆级图文+实现代码注释)】
【几行代码绘制gif动图(保姆级图文+实现代码)】
【几行代码实现网课定时循环截屏,保存重要知识点(保姆级图文+实现代码)】
【常用的user_agent 浏览器头爬虫模拟用户(保姆级图文+实现代码)】
【更多内容敬请期待】