用到了pygame,主要是这个方便演示:
框架用了pygame+thinker,感觉上是不相容的,因为用了pygame,底层它实现,你怎么可能再用thinker呢,它也有自己的一套,结果国外高手把这两个整合在一起了,有好的方面,一个表示图形切换简单,一个有按钮。
八皇后问题用了全排列:8的8次方中排列,那么状态0->8^8个,然后取(8^n)的余数再整除(8^(n-1)),就是,每一排的对应位置,然后调用局面检查函数,这里函数不返回true,false,而是返回出错行,因为这一行出错了,以后的行进行的牌列,检查,全是无用功,直接状态+8^(n-1),就是他这一行的下一个状态对应数值。实际这个逻辑和所有快速搜索是一模一样的。就好比用了8个for循环,哪一个出错,直接next哪一个
由于需要画面演示,八皇后求解要做成类,每一次不是返回全部,而是当前的局面,取一个局面函数queen_next_rows(),这样返回一个局面的数据,每次pygame,loop的时候,可以画面显示管理把结果交给CMG.printAll(res)
画面演示中,需要打印两个棋盘(当然这个背景实际不复杂,复杂的可能画1000多条线),这里用了下面语句,就是可以把画面上花了许多力气作出的图,保存起来,下一次直接打印保存的rect(到底是不是rect其实不明白,只知道这样才行),网上找了半天,没找到,最后好不容易试出来的:
select_rect = CMG.screen.subsurface(0,0,800,570)
CMG.screen_rect = select_rect.copy()
import pygame import sys import os import random import time from pygame.locals import * import tkinter as tk from tkinter import * import tkinter.messagebox # 要使用messagebox先要导入模块 class Eight_queen: queens = 0 sts = 0 pows = None def __init__(self,queens): Eight_queen.queens = queens Eight_queen.pows = [pow(queens,queens-i) for i in range(0,queens+1)] Eight_queen.sts = 0 @staticmethod def check(rows): for row in range(1,len(rows)): for i in range(row): if abs(rows[i] - rows[row]) in (0, row - i): return row return 0 @staticmethod def queen_next_rows(): if Eight_queen.sts >= Eight_queen.pows[0]: return None n = Eight_queen.queens rows = [0]*n for x in range(0,n): rows[x]=(Eight_queen.sts%Eight_queen.pows[x])//Eight_queen.pows[x+1] err_row = Eight_queen.check(rows) if err_row == 0: Eight_queen.sts += 1 return [0,rows] else: Eight_queen.sts += Eight_queen.pows[err_row+1] return [1,rows] class CMG: #画面显示管理 screen = None map = None gameAnswer = None WHITE = (255, 255, 255) GREEN = (0, 255, 0) RED = (255, 0, 0) YELLOW = (255, 255, 0) blocksize = 0 step = 0 last_answer = None backgr = None screen_rect = None def __init__(self,screen): #if CMG.screen == None: #print("#############OS.PATH=%s"% os.path.dirname(os.path.abspath(__file__))) CMG.screen = screen CMG.blocksize = 350//Eight_queen.queens CMG.screen_rect = None #如果存在对象成员 self.init_game_info() def init_game_info(self): pass def printAll(self,res): #if CMG.step > len(CMG.gameAnswer): # return rtn = res[0] rows = res[1] if rtn == 0: CMG.last_answer = rows[:] CMG.screen.fill((0, 0, 0)) queens = Eight_queen.queens if CMG.screen_rect == None: for x in range(0,queens+1): end_pos = [(x*CMG.blocksize,0),(x*CMG.blocksize,queens*CMG.blocksize)] pygame.draw.lines(CMG.screen, CMG.GREEN,0 , end_pos, 2) end_pos = [(x*CMG.blocksize+400,0),(x*CMG.blocksize+400,queens*CMG.blocksize)] pygame.draw.lines(CMG.screen, CMG.GREEN,0 , end_pos, 2) for y in range(0,queens+1): end_pos = [(0,y*CMG.blocksize),(queens*CMG.blocksize,y*CMG.blocksize)] pygame.draw.lines(CMG.screen, CMG.GREEN,0 , end_pos, 2) end_pos = [(400,y*CMG.blocksize),(400+queens*CMG.blocksize,y*CMG.blocksize)] pygame.draw.lines(CMG.screen, CMG.GREEN,0 , end_pos, 2) select_rect = CMG.screen.subsurface(0,0,800,570) CMG.screen_rect = select_rect.copy() else: self.screen.blit(CMG.screen_rect,(0,0)) for r in range(0,len(rows)): y = r*CMG.blocksize + 3 x = rows[r] *CMG.blocksize + 3 pygame.draw.rect(CMG.screen, CMG.WHITE, ((x,y), (CMG.blocksize-6, CMG.blocksize-6)), 0) if CMG.last_answer != None: rows = CMG.last_answer for r in range(0,len(rows)): y = r*CMG.blocksize + 3 x = rows[r] *CMG.blocksize + 403 pygame.draw.rect(CMG.screen, CMG.YELLOW, ((x,y), (CMG.blocksize-6, CMG.blocksize-6)), 0) #CMG.step += 1 def moveAll(self): pass #------------------------------------------------ #tkinter,pygame混合区 START #------------------------------------------------ root = tk.Tk() root.resizable(0,0) embed = tk.Frame(root, width = 800, height = 570) #creates embed frame for pygame window embed.grid(columnspan = (800), rowspan = 730) # Adds grid embed.pack(side = TOP) #packs window to the left buttonwin = tk.Frame(root, width = 800, height = 150) buttonwin.pack(side = BOTTOM) os.environ['SDL_WINDOWID'] = str(embed.winfo_id()) os.environ['SDL_VIDEODRIVER'] = 'windib' screen = pygame.display.set_mode((800,570)) #pygame.init() pygame.display.init() pygame.mixer.init() #------------------------------------------------ #tkinter,pygame混合区 END #------------------------------------------------ #参数,因为函数内要使用之外的变量,需要globe,因此全部打包 class PARAM: STATUS = 0 TICK_NORMAL = 5 TICK_STEP = 30 #按钮动作区 ===================================== def get_input_data(inputcell,min,max): if inputcell.get().isdigit(): num = int(inputcell.get()) if min<=num and num<=max: return num return -1 def exit_game(): global param param.STATUS = 100 def stop_game(): global param param.STATUS = 10 def sel_game(): global param qs = get_input_data(inputqs,4,9) speed = get_input_data(inputspd,11,99) if qs == -1 or speed == -1: return else: eq = Eight_queen(qs) param.cmg = CMG(screen) PARAM.TICK_STEP = speed param.STATUS = 0 #控件定义区 ===================================== button_stop_b = Button(buttonwin,text = '本次演示结束', width=11, command=stop_game) button_stop_b.place(x=700,y=30) btnwin_qs_l = tk.Label(buttonwin, text='皇后数(4-9):') btnwin_qs_l.place(x=530,y=70) inputqs = StringVar() btnwin_qs_e = tk.Entry(buttonwin, show=None,width=2,textvariable = inputqs) btnwin_qs_e.place(x=590,y=70) btnwin_spd_l = tk.Label(buttonwin, text='演示速度(11-99):') btnwin_spd_l.place(x=610,y=70) inputspd = StringVar() btnwin_spd_e = tk.Entry(buttonwin, show=None,width=3,textvariable = inputspd) btnwin_spd_e.place(x=710,y=70) button_sel_b = Button(buttonwin,text = '执行', width=7, command=sel_game) button_sel_b.place(x=740,y=65) button_exit_b = Button(buttonwin,text = '退出画面', width=7, command=exit_game) button_exit_b.place(x=740,y=100) #状态,参数,循环中使用,比如STATUS=0:初次进入,1:...100:退出 param = PARAM() eq = Eight_queen(6) param.cmg = CMG(screen) #------------------------------------------------ #主函数,使用pygame框架,无限LOOP对各种事件然后相应处理 #------------------------------------------------ def main(): global param #pygame.mixer.music.play(-1) clock = pygame.time.Clock() while True: #这段event代码是必须的,哪怕在这个程序中不需要,不执行的话整个框架转不动 for event in pygame.event.get(): if event.type == QUIT: sys.exit() #画面按钮按下后,修改param.STATUS,实际动作这里实现 if param.STATUS == 100: #退出按钮 if tk.messagebox.askokcancel('提示', '要退出画面吗'): break param.STATUS = 0 elif param.STATUS == 10: pass elif param.STATUS == 0: res = Eight_queen.queen_next_rows() if res != None: param.cmg.printAll(res) else: STATUS = 10 #显示游戏画面 pygame.display.flip() #设置帧率:长期画面不操作,设置成最闲 if param.STATUS == 0: clock.tick(param.TICK_STEP) else: clock.tick(param.TICK_NORMAL) root.update() main()