文章目录python九宫格拼图游戏一、分割载入图片二、打乱顺序,生成空白图三、添加游戏循环四、添加按钮控制五、细化和润色
python九宫格拼图游戏
最终效果展示(为了方便用四宫格展示):
一、分割载入图片首先我们要先载入一张图片,并把图片分割开来。
python的PIL是一个图像处理库,使用
import PILPic_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_listw = 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) # 行列布局效果:
二、打乱顺序,生成空白图创建用一个新的列表[0, 1, 2, 3, …],打乱这个列表并按照打乱后的列表展示被分割的图片;
生成一张纯色图并添加进tkImage_list中;
重写展示image_list的方法,通过index_list展示image_list中的图片:
def cut_image(Pic): # 分割图片并存进tkImage_list中global tkImage_listglobal index_listfor 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 = 0for 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效果:
三、添加游戏循环为了游戏的进行需要为游戏添加一个循环,打乱index_list并刷新win,测试是否能实现预期的效果:
def game_loop():win.update()show_image_list()"""测试代码:发现可以根据index_list快速地更新图片,可以通过修改index_list实现窗口图片的更新"""global index_listrandom.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_indexglobal index_listx = index_indexif 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 -= 1elif 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 += 1elif 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 -= depthelif 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 += depthelse: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 Falsereturn Truedef game_loop():win.update()show_image_list()if not check_list():win.after(FPS, game_loop)到这里就已经大致完成了,我发现这个游戏居然还挺难的额…
效果:
五、细化和润色添加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_listreverseCount = 0random.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()但是这个好像真的只对九宫图有效,我换成四宫图就无解了。
效果: