当前位置:   article > 正文

【Python黑科技】tkinter库实战“连连看”小游戏(保姆级图文+实现代码)_kivy连连看

kivy连连看


欢迎关注 『Python黑科技』 系列,持续更新中
欢迎关注 『Python黑科技』 系列,持续更新中

实现效果

在这里插入图片描述


实现思路

链接: https://pan.baidu.com/s/1Q6peGnZe0k0zBalAGaJ8wg?pwd=pdux 
提取码: pdux
  • 1
  • 2

搞一堆图片bar_00-----bar_09.png
如果有条件找到gif就更好了

imgs = [PhotoImage(file='images\\bar_0' + str(i) + '.png') for i in range(0, 10)]  # 所有图标图案
  • 1

经典连连看,随机生成图片元素,相邻图片元素的标记相同,可以消除。


实现代码

在这里插入图片描述
在这里插入图片描述

# @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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431

总结

大家喜欢的话,给个

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/854307
推荐阅读
相关标签