今年过年真是前所未有的有时间,哪都去不了,于是难得得有时间想学习一下,好多年没编程,没写博客,因为总是编嵌入式的c程序,对界面开发基本没搞过,过去只会用Borland C++,还是6.0,真是太过时了。好在还学过python,听说用python开发界面也很方便了,于是想学习一下,选了一个小游戏“数回”(Slither Link)来作为目标。本以为不会太麻烦,没想到一个不到千行的程序,让我踩了好多的坑,花了足一个星期的时间。可能也是自己年纪大了,脱离编程太久了。感慨之余,把踩的坑记一下,以资来人。
首先介绍一下数回这个游戏,规则很简单,一个N*M的格子,其中给定一些数字,数字表明这个格子周围有几条边有连续通过,游戏要求画出一条 唯一的,封闭的 曲线,并且满足所有的数字条件,OK了。
这个游戏很有意思,有的很难,解题时要注意有些定式和技巧,特别是注意唯一性这个特定,很多时候可以帮助解题。
好了游戏不多说了,大家可以去找来玩玩,手机上有很多。
我开始也是玩手机上的数回游戏,然后很多关太难了玩不过去,所以,我其实是想编一个自动求解数回的程序。然而开工之后却发现,我先得有一个展现游戏的界面才行啊!编界面偏偏是我的弱项,于是想放弃,可是想了几天,还是去学学吧,于是开始了这个程序的开发。这一开始,就是不停的踩坑,一个不到千行的程序,竟花了一个星期的时间才初具规模。
这第一个坑,说来搞笑,我听说Tkinter比较简单,于是在办公室一台比较旧的电脑上试了下,安装的是老版的python2.7,试了几个基本的控件和画界面方法,觉得还不错,就决定用tk,然后就放假了,回家后在家里装了个anaconda,使用import Tkinter,竟然失败,仔细对照了在办公室的程序确认大小写没错,于是以为没有装这个模块,于是又去找tkinter的安装,结果用pip找不到,只能放弃了,差点就不想编了,后来看到有个wxPython,于是又去学wxPython,可是安装又是一番折腾,用pip在线装下载总是报错,后来只好去找安装包下载好了,本地安装总算装好了。这是第一个坑,还未开始就踩坑。可是今天我在看python安装目录时竟然发现了有个tkinter的目录,于是试了下全小写的 import tkinter,竟然成功了,真是%&¥……#……#……¥……%%&……*……
不管怎么说,总算开始了,又是一路踩坑,实在太多了,挑几个主要的说一下。
第一个,是wxPython的BoxSizer, 不知怎么回事,在程序开始时里面的控件不能够自动排号,必须要拖动一下,改变一下尺寸才行,于是我不得不在初始化时显式的指定好各控件的位置。
第二个,是用鼠标拖动改变界面大小时,竟然不触发EVT_PAINT事件,后来在EVT_SIZE事件中加了resize函数,好了一点,但还是经常不进OnPaint函数,后来不得不强制加 Refresh()函数。更神奇的是,即使进了OnPaint函数,执行完了绘制工作后,绘制的结果仍然经常会不显示,让我一直以为是我画图操作哪里有问题。后来逼得没办法,在画图完后把图存到一个bmp里,打开bmp看,发现图是正确的。后来在OnPaint里面最后加了对Refresh()调用才好。这真是超出我的认知了!而且在OnPaint里加Refresh调用还会触发一次OnPain,这样就无尽循环了,为此只能又加了个需要刷新的标志,在真正需要刷新时置位,进Onpaint时判断一下,置位了才画,绘图完成后清除,这样才好。
第三个是对python本身理解的问题。为了简化编程,把界面做一个py文件,把地图操作做一个py文件,把地图配置存成一个py文件里面的一个列表,在地图操作py文件里用exec file的方式加载地图配置py文件,然后读取里面的地图配置列表。比如在地图配置文件里面定义地图列表
mapList=[.........]
在地图操作文件里,加载地图配置文件,读取里面的列表,
execfile( "地图配置.py")
for map in mapList: ............
单独调试地图操作文件时,一切正常,可是运行时地图操作文件是在主文件里被import的,这时再执行就显示mapList找不到了,后来猜到是变量局部空间的问题,查了半天才查到解决办法,需要locals()函数先获取局部变量字典,再exec file,再从局部变量字典里通过变量名得到变量。
其它的就不多说了,大大小小的坑踩多了,还是直接上代码吧。
主文件 SlitherLink.py
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 31 18:19:25 2020
Slitherlink
MainFrame
use two panel,one place menus ,one place feild.
@author: pc
"""
version="0.1"
import wx
import copy
#-------------------------------------------------------------
from ini import *
import GameMap as gmap
gMapList=[]
gCurrentMapIdx=-1
gCurrentMap=None
gReDraw=False
gMiniNumRect=40
gCurrentNumRect=40 #current number Square side length
gCurrentRectPos=[0,0] #current map left top point
gCurrentMinSize=minisize
# menu panel============================================================
class MPanel(wx.Panel):
def __init__(self,parent,*args,**kwargs):
super(MPanel,self).__init__(parent,*args,**kwargs)
self.parent=parent
self.SetBackgroundColour(wx.WHITE)
#create a combo box
box=wx.BoxSizer(wx.HORIZONTAL)
wid,high=self.GetSize()
self.cbox=wx.ComboBox(self,pos=(wid-200,2))
box.AddStretchSpacer(10)
box.Add(self.cbox,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL,10)
box.AddSpacer(100)
self.SetSizer(box)
self.Bind(wx.EVT_COMBOBOX, self.OnCombox,self.cbox)
#load game map list to combo box
global gMapList
self.LoadMap(gMapList,mapfilepath,savefilepath)
#load game map list ------------------------------------
def LoadMap(self,maplist,filename,savefile=None):
gmap.loadLeves(maplist,filename,savefile)
mapcnt=len(maplist)
#add to combo box
self.cbox.Clear()
for lv in maplist:
texts="%d×%d "%(lv.colum,lv.row) + lv.title
self.cbox.Append(texts)
def OnCombox(self,event):
global gCurrentMapIdx, gCurrentMap, gMapList,gReDraw
idx= self.cbox.GetSelection()
#print("cbox select",idx)
if idx!=gCurrentMapIdx:
#change map and redraw
gCurrentMapIdx=idx
gCurrentMap=gMapList[idx]
gReDraw=True
self.parent.Refresh()
# game field panel ======================================================
class FPanel(wx.Panel):
def __init__(self,parent,*args,**kwargs):
super(FPanel,self).__init__(parent,*args,**kwargs)
self.parent=parent
self.bgColour=wx.Colour(232,232,232)
self.SetBackgroundColour(self.bgColour)
self.Bind(wx.EVT_PAINT,self.OnPaint)
#set OnMove and click action
self.MouseItem=[-1,-1,0] #the mouse position map to squares [row, colum, right|down]
#self.Bind(wx.EVT_MOTION,self.OnMove)
self.Bind(wx.EVT_MOUSE_EVENTS,self.OnMouse,self)
self.MouseDownItem=[None,None] #the items under the mouse when mouse button is down [left , right]
def OnMove(self,event):
global gCurrentMapIdx, gCurrentMap, gMapList,gReDraw, gCurrentNumRect,gCurrentRectPos
x,y=event.GetPosition()
left,top=gCurrentRectPos
if gCurrentMap:
row=gCurrentMap.row
colum=gCurrentMap.colum
if x<=left or x>=left+gCurrentNumRect*colum+int(gCurrentNumRect/6) or y<=top or y>=top+gCurrentNumRect*row+int(gCurrentNumRect/6):
self.MouseItem=[-1,-1,0]
else:
squCol=int((x-left)/gCurrentNumRect)
squRow=int((y-top)/gCurrentNumRect)
sx=x-left-squCol*gCurrentNumRect
sy=y-top-squRow*gCurrentNumRect
#diviion a square to 8 part
sx= int(sx*8/gCurrentNumRect)
sy=int(sy*8/gCurrentNumRect)
if sx<2 and 05 and 05 and 0=0 and colum>=0 and line>0:
con=gCurrentMap.connected[row][colum]
if line==1: #right direction
if con&0x10 ==0: #unlock ,inverse the connect
gCurrentMap.setConnect(row,colum,gCurrentMap.Up,gCurrentMap.INVERSE,gCurrentMap.UNCHANGE)
gReDraw=True
#print(gCurrentMap.connected)
self.Refresh()
#print(self.toDrawAction)
elif line==2: #down
if con&0x1000==0: #unlock
gCurrentMap.setConnect(row,colum,gCurrentMap.Left,gCurrentMap.INVERSE,gCurrentMap.UNCHANGE)
gReDraw=True
self.Refresh()
self.MouseDownItem[0]=None
if event.RightUp():
if self.MouseDownItem[1] and self.MouseDownItem[1]==self.MouseItem:
row,colum,line=self.MouseItem
if row>=0 and colum>=0 and line>0:
con=gCurrentMap.connected[row][colum]
if line==1: #right direction
gCurrentMap.setConnect(row,colum,gCurrentMap.Up,gCurrentMap.UNCHANGE,gCurrentMap.INVERSE)
gReDraw=True
self.Refresh()
elif line==2: #down
gCurrentMap.setConnect(row,colum,gCurrentMap.Left,gCurrentMap.UNCHANGE,gCurrentMap.INVERSE)
gReDraw=True
self.Refresh()
self.MouseDownItem[1]=None
if gCurrentMap:
if gCurrentMap.succ and not gCurrentMap.finish:
gCurrentMap.finish=True
#print('succeessful finish')
if gCurrentMap.answer==None:
gCurrentMap.answer=copy.deepcopy(gCurrentMap.connected)
for r in range(gCurrentMap.row+1):
for c in range(gCurrentMap.colum+1):
gCurrentMap.answer[r][c]&=0x101
wx.MessageBox("Congratulations!","Finish",wx.OK)
else:
gCurrentMap.finish=gCurrentMap.succ
#print(self.MouseDownItem)
# draw feild
def OnPaint(self,event):
rect=self.GetClientRect()
global gCurrentMap, gReDraw,gCurrentNumRect,gCurrentRectPos
if gCurrentMap==None:
return
colum =gCurrentMap.colum
row = gCurrentMap.row
#dc=wx.PaintDC(self)
'''bmp=wx.Bitmap(width=rect.width,height=rect.height)
dc=wx.MemoryDC()
dc.SelectObject(bmp)'''
dc=wx.BufferedPaintDC(self)
gbBrush=wx.Brush(self.bgColour)
gbBrush.SetStyle(wx.BRUSHSTYLE_SOLID)
dc.SetBrush( gbBrush)
#print ("fpanel on paint",gReDraw)
if gReDraw: #need redraw whole map
#calculate size , minist number Square side length is 40,can zoom up by the rate of client size
#if client size less than the need of minist square ,resize the frame
x=int((rect.width-20)/colum /4) #must times of 4
y=int((rect.height-200)/row /4)
m=min(x,y)
m<<=2
if m>120:
m=120
if m=0:
dc.DrawText(repr(number), left+y*m+m/4+psize, top+x*m +int(m/6)-psize )
# draw locked null connect ,use a red cross
redPen=wx.Pen(wx.Colour(255,32,32),int(psize/2),style=wx.PENSTYLE_SOLID) #use light pen to draw not locked line
dc.SetPen(redPen)
for x in range(row+1):
for y in range(colum+1):
tmp=con[x][y]
if tmp&0xff==0x10:
dc.DrawLine(left+y*m + int(m*3/8)+psize/2, top+x*m - int(m/8) , left+y*m + int(m*5/8)+psize/2, top+x*m + int(m/8) )
dc.DrawLine(left+y*m + int(m*3/8)+psize/2, top+x*m + int(m/8) , left+y*m + int(m*5/8)+psize/2, top+x*m - int(m/8) )
if tmp&0xff00==0x1000:
dc.DrawLine(left+y*m - int(m/8), top+x*m + int(m*3/8)+psize/2 , left+y*m + int(m/8), top+x*m + int(m*5/8)+psize/2 )
dc.DrawLine(left+y*m - int(m/8), top+x*m + int(m*5/8)+psize/2 , left+y*m + int(m/8), top+x*m + int(m*3/8)+psize/2 )
if gCurrentMap and gCurrentMap.succ and gCurrentMap.finish:
dc.SetTextForeground(wx.RED)
dc.DrawText("Finished !", int (rect.width/2)-int(1.5*m), 20)
#end of draw map
gReDraw = False
#self.Refresh()
'''dc2=wx.PaintDC(self)
print("blit",left,top)
dc2.Blit(0,0,rect.width,rect.height,dc,0,0)
dc.SelectObject(wx.NullBitmap)'''
#MainFrame ==============================================================
class MFrame(wx.Frame):
def __init__(self,parent,*args,**kwargs):
super(MFrame,self).__init__(parent,*args,**kwargs)
# add a menu bar
self.menu=wx.MenuBar()
self.menu_play=wx.Menu()
self.id_restart=101
self.menu_play.Append(self.id_restart,"Restart")
self.id_seeAnswer=102
self.menu_play.Append(self.id_seeAnswer,"see answer")
self.menu.Append(self.menu_play,"&Play")
self.SetMenuBar(self.menu)
self.Bind(wx.EVT_MENU,self.OnMenu)
# create menu panel
self.mPanel=MPanel(self,size=(initsize[0],toolbarHeight))
#create field panel
self.fPanel= FPanel(self,style=wx.BORDER_DOUBLE,pos=(0,toolbarHeight),size=(initsize[0],initsize[1]-toolbarHeight) )
# add resize event
self.Bind(wx.EVT_SIZE,self.OnResize)
self.Bind(wx.EVT_CLOSE,self.OnClose)
#------------------------------------------
def OnResize(self,event):
global gReDraw,gCurrentMinSize
wid,high=self.GetSize()
#print("f size",wid,high)
#resize the panels,but not small then minsize
if wid0:
gmap.saveLeves(gMapList,savefilepath)
event.Skip()
#Main app=================================================================
class MyApp(wx.App):
def OnInit(self):
self.frame= MFrame(None,title="Slither Link v%s"%version,size=initsize)
self.SetTopWindow(self.frame)
self.frame.Show()
return True
#======================================================================
if __name__=="__main__":
app=MyApp(False)
app.MainLoop()
地图管理文件 GameMap.py
# -*- coding: utf-8 -*-
"""
Created on Sat Feb 1 16:55:53 2020
Slitherlink game map class
@author: pc
"""
import os
import copy
import hashlib
class GameMap():
def __init__(self,numMap=None,title="",difficulty=0,answer=None,connected=None):
self.numMap=None #num define
self.FitMap=None
self.mapHash=''
self.succ=False
self.finish=False
self.title=title
self.difficulty=difficulty
self.answer=answer
self.seeAnswer=False
self.connected=None
self.row=0
self.colum=0
#define the direction code and connect action code,used in setConnect function
self.Up=0
self.Left=1
self.Down=2
self.Right=3
self.CONNECT=1
self.DISCONNECT=0
self.INVERSE=-1
self.UNCHANGE=-2
if numMap:
self.setMap(numMap,connected)
#--------------------------------------------------------------------------
def setMap(self, numMap,connected=None):
if numMap:
row=len(numMap)
#check map ,must be a 2D matrix
col=len(numMap[0])
for r in numMap:
if col!=len(r):
return False
else:
for i in r:
if i not in (-1,0,1,2,3):
return False
self.row=row
self.colum=col
self.numMap=numMap
self.clear()
self.calcuHash()
if connected:
succ=True
#check connected map
#connect map is a connected relation of the points
#as a m*n num map ,it will be a (m+1)*(n+1) points matrix
#from left-top to right-down , each point can have tow connections that to right and down
#each line use one byte , lower byte is right direction,upper byte is down direction,
#0 mean not connected ,1 means connected,0x10 means must not be connected ,0x11 means must be connected
conFlag= (0,1,0x10,0x11)
if len(connected)!=self.row+1:
succ=False
else:
for r in connected:
if not succ or len(r)!=self.colum+1:
succ=False
break
else:
for i in r:
if i&0xff not in conFlag or i>>8 not in conFlag:
succ=False
break
if succ :
self.connected=connected
self.checkFit()
else:
self.numMap=None
self.answer=None
self.connected=None
self.row=0
self.colum=0
return True
#-----------------------------------------------------------------
def createFitMap(self):
self.FitMap=[]
for i in range(self.row):
a=[0]*(self.colum)
self.FitMap.append(a)
for r in range(self.row+1):
for c in range(self.colum+1):
state=self.connected[r][c]
if state&1 !=0:
if r0:
self.FitMap[r-1][c]+=1
if state&0x100 !=0:
if c < self.colum:
self.FitMap[r][c]+=1
if c>0:
self.FitMap[r][c-1]+=1
def checkFit(self):
self.createFitMap()
#print(self.FitMap)
succ=True
for r in range(self.row):
if not succ:
break
for c in range(self.colum):
if self.numMap[r][c]>=0:
if self.numMap[r][c]!=self.FitMap[r][c]:
succ=False
break
#print ("1",succ)
contmp=copy.deepcopy(self.connected)
trace=[]
if succ:
#print(contmp)
for r in range(self.row+1):
for c in range(self.colum+1):
if contmp[r][c]!=0:
break
if contmp[r][c]!=0:
break
#print("2",succ,r,c)
if contmp[r][c]&1 ==0:
succ=False
else:
startpoint=(r,c)
trace.append(startpoint)
direction=self.Right
contmp[r][c]&=0xff00
c+=1
while (r,c)!=startpoint:
#print(100,succ,r,c,direction,contmp[r][c])
if (r,c) in trace:
succ=False
break
trace.append((r,c))
tmp=contmp[r][c]
if direction==self.Right:
if tmp&1 !=0: #go right
contmp[r][c]&=0xff00
direction=self.Right
c+=1
continue
elif tmp&0x100 !=0: #go down
contmp[r][c]&=0xff
direction=self.Down
r+=1
continue
elif r>0 and contmp[r-1][c]&0x100 !=0: #go up
r-=1
contmp[r][c]&=0xff
direction=self.Up
continue
else:
succ=False
break
elif direction==self.Down:
#print(10,r,c ,contmp[r][c])
if tmp&1 !=0: #go right
#print(11,contmp[r][c])
contmp[r][c]&=0xff00
direction=self.Right
c+=1
continue
elif tmp&0x100 !=0: #go down
#print(12,contmp[r][c])
contmp[r][c]&=0xff
direction=self.Down
r+=1
continue
elif c>0 and contmp[r][c-1] &1 !=0: #go left
#print(13,contmp[r][c-1])
c-=1
contmp[r][c]&=0xff00
direction=self.Left
continue
else:
succ=False
break
elif direction==self.Left:
if tmp&0x100 !=0: #go down
contmp[r][c]&=0xff
direction=self.Down
r+=1
continue
elif c>0 and contmp[r][c-1] &1 !=0: #go left
c-=1
contmp[r][c]&=0xff00
direction=self.Left
continue
elif r>0 and contmp[r-1][c] &0x100 !=0: # go up
r-=1
contmp[r][c]&=0xff
direction=self.Up
continue
else:
succ=False
break
elif direction==self.Up:
if tmp&1 !=0: #go right
contmp[r][c]&=0xff00
direction=self.Right
c+=1
continue
elif c>0 and contmp[r][c-1] &1 !=0: #go left
c-=1
contmp[r][c]&=0xff00
direction=self.Left
continue
elif r>0 and contmp[r-1][c] &0x100 !=0: # go up
r-=1
contmp[r][c]&=0xff
direction=self.Up
continue
else:
succ=False
break
#print("3",succ)
if succ:
if (r,c)!=startpoint:
succ=False
else:
for r in range(self.row+1):
if not succ:
break
for c in range(self.colum+1):
if contmp[r][c]!=0:
succ=False
break
#print("4",trace,succ)
self.succ=succ
#print ("final",succ)
return succ
#---------------------------------------------------------------
def setConnect(self,row,colum, direction, state , lock):
# set the connected state of lines , row and colum defind the number ,drection in self.Up,Left,Down,Right
#state in CONNECT DISCONNECT or INVERSE or UNCHAGE
#lock in TRUE , FALSE or INVERSE or UNCHANGE
#adjust direction to up right and right
if not self.connected:
return False
if direction==self.Down:
row+=1
direction=self.Up
elif direction==self.Right:
colum+=1
direction=self.Left
if row>self.row or colum>self.colum:
return False
elif direction not in (0,1):
return False
elif state not in (-2,-1,0,1):
return False
elif lock not in (-2,-1,0,1):
return False
#if change the connect , close answer show
self.seeAnswer=False
curstate=self.connected[row][colum]
tmp=curstate
if direction==self.Left:
tmp>>=8
else:
tmp&=0xff
if lock!=self.UNCHANGE:
if lock==self.INVERSE:
tmp^=0x10
else:
tmp=(tmp&0xf) |(lock<<4)
if state !=self.UNCHANGE:
if state==self.INVERSE:
tmp^=1
else:
tmp=(tmp&0xf0)|state
if direction==self.Left:
self.connected[row][colum]= (curstate&0xff ) | tmp<<8
else:
self.connected[row][colum]= (curstate&0xff00 ) | tmp
self.checkFit()
return True
#---------------------------------------------------------------------
def clear(self):
self.connected=[]
for i in range(self.row+1):
a=[0]*(self.colum+1)
self.connected.append(a)
self.FitMap=[]
for i in range(self.row):
a=[0]*(self.colum)
self.FitMap.append(a)
self.succ=False
self.finish=False
def calcuHash(self):
s=""
for r in self.numMap:
for num in r:
s=s+repr(num)
#print(s)
m=hashlib.md5(s.encode('utf-8'))
self.mapHash=m.hexdigest()
#print (s,"\nhash=",self.mapHash)
def loadLeves(tarList, defFile,savefile=None):
if os.path.isfile(defFile):
with open(defFile,'r') as f:
# this code is very important,if not this code ,when call exec in a function,
# it will not see the variables in current environment
loc=locals()
exec(f.read())
Levels=loc["Levels"]
#or you can use a global dic as a parameters when call exec like this
'''a=1
loc={'a':a}
glb={}
exec(f.read(),glb,loc)
Levels=loc["Levels"]'''
try:
for lv in Levels:
if "field" in lv:
nummap=lv["field"]
if "title" in lv:
title=lv["title"]
else:
title=""
if "difficulty" in lv:
diff=lv["difficulty"]
else:
diff=0
if "answer" in lv:
answer=lv["answer"]
else:
answer=None
if "connected" in lv:
con = lv["connected"]
else:
con=None
gmap=GameMap(nummap,title,diff,answer,con)
if gmap.numMap:
tarList.append(gmap)
except:
print("load map error")
if savefile and os.path.isfile(savefile):
with open(savefile,'r') as sf:
loc=locals()
exec(sf.read())
saves=loc["saves"]
try:
for sv in saves:
for gmap in tarList:
if gmap.mapHash==sv["hash"]:
if "connected" in sv:
gmap.connected=sv["connected"]
gmap.checkFit()
gmap.finish=gmap.succ
if "answer" in sv:
gmap.answer = sv["answer"]
break
except:
print("load save error")
def saveLeves(tarList, savefile):
if len(tarList)>0:
f=open(savefile,'w')
f.write("saves=[ \n")
for gmap in tarList:
f.write( "{'hash':'%s' ,\n"% gmap.mapHash)
if gmap.title:
f.write("'title': '")
f.write(gmap.title)
f.write(" ' ,\n")
f.write("'connected' :")
f.write( repr(gmap.connected))
if gmap.answer:
f.write(",\n'answer':")
f.write(repr(gmap.answer))
f.write("\n},\n")
f.write("]\n")
f.close()
if __name__=="__main__":
lvs=[]
loadLeves(lvs,".\levels.py")
初始化配置文件 ini.py
initsize=(800,1000)
minisize=(600,800)
toolbarHeight=30
mapfilepath=".\levels.py"
savefilepath=".\saves.py"
地图文件 levels.py,里面有两个测试用的地图配置
# -*- coding: utf-8 -*-
n=-1
Levels=[
{"size":[7,10],
"title":"test 1",
"difficulty":1,
"field":[[2,n,1,1,0,1,n],
[n,n,1,n,n,n,n],
[2,n,n,1,n,0,1],
[n,n,3,n,n,2,n],
[n,n,2,n,n,n,n],
[n,n,n,n,3,n,n],
[n,0,n,n,0,n,n],
[3,2,n,3,n,n,3],
[n,n,n,n,0,n,n],
[n,3,2,2,2,n,3]]
},
{"size":[3,3],
"title":"test 2",
"difficulty":1,
"field":[[3,n,3],
[3,n,3],
[3,n,3]]
}
]
最后是在线存盘文件saves.py,可以在线生成
saves=[
{'hash':'e0cd7477fa558ad58b45633bdf29ba20' ,
'title': 'test 1 ' ,
'connected' :[[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]],
'answer':[[257, 1, 256, 0, 0, 0, 0, 0], [256, 257, 0, 257, 256, 257, 1, 256], [256, 256, 0, 256, 1, 0, 0, 256], [256, 256, 257, 0, 257, 256, 0, 256], [256, 256, 1, 1, 0, 1, 256, 256], [256, 1, 1, 256, 257, 256, 256, 256], [256, 0, 0, 1, 0, 1, 0, 256], [1, 256, 257, 1, 256, 0, 257, 0], [257, 0, 256, 257, 0, 0, 1, 256], [1, 256, 256, 256, 0, 257, 256, 256], [0, 1, 0, 1, 1, 0, 1, 0]]
},
{'hash':'c5dd983b8c34f667a89585c914998a0c' ,
'title': 'test 2 ' ,
'connected' :[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
'answer':[[257, 1, 1, 256], [1, 256, 257, 0], [257, 0, 1, 256], [1, 1, 1, 0]]
},
]
最后还有一个坑,我开始用python自带的hash函数算地图hash,结果发现竟然每次运行算出来的都不一样,这真是再次刷新我的认知了,只好换了md5函数。哎,路漫漫其修远兮,深坑更浅坑……
最后的最后,说下这个程序的功能,左键点击可以连线和取消连线,右键是锁定和取消锁定,锁定是对可以确定有连接或不能连接的地方加以标记,锁定后左键点击就不能改变了。就像这个样子。(锁定的线条粗一些,锁定不连接的地方有红叉)
成功连接完成后有完成提示,中途正常关闭会保持当前盘面,再次打开后可以继续接着玩。如果成功完成还可以自动作为答案保存,有答案的可以看答案。地图文件里只有两个地图,可以自己加,格式很简单。
好了,接下来该回归编这个程序的最初目的了,就是自动求解数回的算法,等完成了再写一个博客吧。