python九宫格拼图游戏

文章目录

  • python九宫格拼图游戏
    • 一、分割载入图片
    • 二、打乱顺序,生成空白图
    • 三、添加游戏循环
    • 四、添加按钮控制
    • 五、细化和润色

python九宫格拼图游戏

完整代码已上传至github

最终效果展示(为了方便用四宫格展示):
python九宫格拼图游戏_第1张图片

一、分割载入图片

首先我们要先载入一张图片,并把图片分割开来。

python的PIL是一个图像处理库,使用

import PIL

Pic_path = "img5.jpg"  # 图片路径
im = Image.open(Pic_path)  # 载入图片

载入图片后就是分割图片,不过windows操作系统下会使用系统自带的图片处理器打开,我需要把分割后的图片展示在一个新的tkinter窗体上:

这里我们把载入的im转化为tk窗口能够接收的格式,PIL库中提供了一种PhotoImage的格式。但是PhotoImage显示时,当把该变量放在自定义函数里时,不能正常显示,移出函数又可正常显示,所以想到可能是变量不是全局性的缘故,改为全局变量后果然可正常显示:

win = tk.Tk()
Pic_path = "img5.jpg"
im = Image.open(Pic_path)  # 读取图片
tkImage_list = []  # 定义为全局变量,以便photoimage能够正常显示

depth = 3  # 分割的层数
width, height = im.size  # 获取图片的大小


def cut_image(Pic):  # 分割图片
    global tkImage_list
    w = int(width/depth)
    h = int(height/depth)
    for i in range(depth):
        for j in range(depth):
            box = (w*j, h*i, w*(j+1), h*(i+1))  # 需要切割的矩形区域
            image = Pic.crop(box)
            tkImage_list.append(ImageTk.PhotoImage(image))
            label = tk.Label(win, image=tkImage_list[i*depth + j])
            label.grid(row=i, column=j)  # 行列布局

python tkinter库的三种布局方式

效果:
python九宫格拼图游戏_第2张图片

二、打乱顺序,生成空白图

创建用一个新的列表[0, 1, 2, 3, …],打乱这个列表并按照打乱后的列表展示被分割的图片;

生成一张纯色图并添加进tkImage_list中;

重写展示image_list的方法,通过index_list展示image_list中的图片:

def cut_image(Pic):  # 分割图片并存进tkImage_list中
    global tkImage_list
    global index_list
    for i in range(depth):
        for j in range(depth):
            box = (w*j, h*i, w*(j+1), h*(i+1))  # 需要切割的矩形区域
            image = Pic.crop(box)
            tkImage_list.append(ImageTk.PhotoImage(image))
            index_list.append(i*depth+j)
    random.shuffle(index_list)  # 打乱下标的顺序
    print(index_list)


def form_white_box():  # 生成一个白色的区域并添加至tkImage_list中
    global tkImage_list
    (x, y) = int(depth / 2), int(depth / 2)  # 坐标
    list_index = x * depth + y  # 列表下标
    white_box = Image.new('RGB', (w, h), box_color)  # 生成一张颜色为box_color的图片
    tk_white_box = ImageTk.PhotoImage(white_box)
    tkImage_list.pop(list_index)
    tkImage_list.insert(list_index, tk_white_box)  # 通过索引删除和添加元素


def show_image_list():  # 通过index_list展示image_list中的图片
    i = 0
    for index in index_list:
        label = tk.Label(win, image=tkImage_list[index])
        label.grid(row=int(i/depth), column=int(i % depth))  # 第i张图片的位置
        i += 1

效果:

python九宫格拼图游戏_第3张图片

三、添加游戏循环

为了游戏的进行需要为游戏添加一个循环,打乱index_list并刷新win,测试是否能实现预期的效果:

def game_loop():
    win.update()
    show_image_list()

    """测试代码:发现可以根据index_list快速地更新图片,可以通过修改index_list实现窗口图片的更新"""
    global index_list
    random.shuffle(index_list)

    win.after(FPS, game_loop)


def main():
    cut_image(im)
    form_white_box()

    game_loop()

    win.mainloop()


if __name__ == '__main__':
    main()

效果:

四、添加按钮控制

为空白图添加按钮响应,按下箭头 上下左右 键或 wsad 能够做出相应的反应:

def exchange_img(event):
    """
    响应键盘事件
    交换白色图片与相邻图片的位置
    :return:
    """
    global index_index
    global index_list
    x = index_index
    if event.keysym == "Left" or event.keysym == "a":
        # 需要实现index_list的交换
        if x % depth != 0:  # 不是在最左边
            index_list[x - 1], index_list[x] = index_list[x], index_list[x - 1]
            index_index -= 1
    elif event.keysym == "Right" or event.keysym == "d":
        if (x+1) % depth != 0:  # 不是在最右边
            index_list[x + 1], index_list[x] = index_list[x], index_list[x + 1]
            index_index += 1
    elif event.keysym == "Up" or event.keysym == "w":
        if x >= depth:  # 不是在最上面
            index_list[x - depth], index_list[x] = index_list[x], index_list[x - depth]
            index_index -= depth
    elif event.keysym == "Down" or event.keysym == "s":
        if x + depth < depth*depth:  # 不是在最下面
            index_list[x + depth], index_list[x] = index_list[x], index_list[x + depth]
            index_index += depth
    else:
        return

窗体绑定键盘按钮:

    win.focus_set()
    win.bind("", exchange_img)
    win.bind("", exchange_img)
    win.bind("", exchange_img)
    win.bind("", exchange_img)
    win.bind("", exchange_img)
    win.bind("", exchange_img)
    win.bind("", exchange_img)
    win.bind("", exchange_img)

添加游戏结束的条件:

def check_list():
    for i in range(depth * depth):
        if index_list[i] != i:
            return False
    return True


def game_loop():
    win.update()
    show_image_list()

    if not check_list():
        win.after(FPS, game_loop)

到这里就已经大致完成了,我发现这个游戏居然还挺难的额…
效果:
python九宫格拼图游戏_第4张图片

五、细化和润色

添加game_over()方法,并在check_list()成功时调用,使得游戏结束时的表现更直观:

def game_over():
    """
    游戏结束时在窗口添加“you win”文字
    将被替代的图片还原
    :return:
    """
    label = tk.Label(win, text="YOU WIN!!!")
    label.grid(row=depth, column=1)
    tkImage_list.pop(depth * depth - 1)
    tkImage_list.append(replaced_img)
    show_image_list()
    win.update()
    return

我发现用random自带的shuffle函数打乱列表会出现无解的情况。网上找到这种说法:

九宫格拼图的可解性:
原数组和随机出来的数组的逆序数的奇偶性一致,那么就是可解的。如果原数组是[0,1,2,3,4,5,6,7,8], 算出来的 逆序数 为 0, 那么只要保证随机出来的数组的 逆序数 为偶数 ,那么拼图就是可解的。

于是我修改了打乱列表的方法:

def shuffle_list():
    """
    打乱index_list并使逆序数一定为偶数
    :return:
    """
    global index_list
    reverseCount = 0
    random.shuffle(index_list)
    for i in range(len(index_list)):
        for j in range(i):
            if index_list[i] < index_list[j]:
                reverseCount += 1  # 计算逆序数
    if reverseCount % 2 != 0:
        shuffle_list()

但是这个好像真的只对九宫图有效,我换成四宫图就无解了。
效果:
python九宫格拼图游戏_第5张图片
实在是太好(nan)玩了,我撒花❀~

你可能感兴趣的:(py_games,游戏,python)