大多数时间,都在使用 deepin 系统,原来的 WPF 实现似乎越来越遥远了。这几天在家学习了一下 tkinter,顺便予以重写。
内在逻辑是一样的,就不重复,但具体实现层面,如图像不能改变大小等不一而足。由于 AI 出现,再去探讨怎么落子已变得毫无意义,所以只实现了最基本的吃子,打劫,倒扑,悔棋功能。
main.py 代码如下:

import os, sys, enum
import tkinter as tk
from core import Board
CurrDir = os.path.dirname(__file__)
sys.path.append(CurrDir)
class App(tk.Tk):
def __init__(self):
super().__init__()
board = Board(self)
board.pack()
self.geometry('{}x{}+300+50'.format(board.w,board.h))
if __name__ == "__main__":
App().mainloop()
core.py 代码如下:

import os
import tkinter as tk
import tkinter.ttk as ttk
CurrDir = os.path.dirname(__file__)
class Board(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.w = self.h = R.StoneSize * R.CellNumber+10
self.offset = R.StoneSize/2+5
self.canvas = tk.Canvas(self, bg='#ec5',width=self.w,height=self.h)
self.canvas.bind(R.ButtonLeft, self.next_one)
self.canvas.bind(R.ButtonRight, self.back_one)
self.canvas.pack()
self.draw_lines()
self.draw_stars()
self.black_stone = tk.PhotoImage(file=os.path.join(CurrDir,'res/black-{}.png'.format(R.StoneSize)))
self.white_stone = tk.PhotoImage(file=os.path.join(CurrDir,'res/white-{}.png'.format(R.StoneSize)))
self.isBlack = True
self.poses = []
self.stepCount = 0
self.wqcore = WqCore()
self.once = Record() # 打劫
self.deads = [] # 悔棋
def draw_lines(self):
for i in range(R.CellNumber):
self.canvas.create_line(self.offset,i*R.StoneSize + self.offset,self.w-self.offset,i*R.StoneSize+self.offset)
self.canvas.create_line(i*R.StoneSize+self.offset,self.offset,i*R.StoneSize+self.offset,self.h-self.offset)
def draw_stars(self):
stars = [(3,3), (3,9), (3,15),(9,3,),(9,9),(9,15),(15,3),(15,9),(15,15)]
r = 3
for i in range(R.StarNumber):
x,y = self.offset + stars[i][0]*R.StoneSize,self.offset + stars[i][1]*R.StoneSize
self.canvas.create_oval(x-r, y-r, x+r, y+r, fill="black")
def next_one(self, e):
x,y = e.x//R.StoneSize, e.y // R.StoneSize
color = 1 if self.isBlack else -1
self.draw_stone((x,y), color)
self.kill()
def draw_stone(self, pos, color):
if color==0: return
record = self.wqcore.GetRecord(pos)
if record == None or record.color != 0: return
x,y = pos
self.stepCount += 1
self.poses.append(pos)
self.wqcore.Steps.append(Record(row=y,col=x,count=self.stepCount, color=color))
self.wqcore.UpdateRecords()
stone = self.black_stone if color == 1 else self.white_stone
self.canvas.create_image(self.offset + x*R.StoneSize, self.offset + y*R.StoneSize, image=stone)
self.isBlack = not self.isBlack
def kill(self):
deads = self.wqcore.KillOther()
steps = self.wqcore.Steps[:]
if deads != None:
if len(deads) == 1:
col,row = deads[0]
if self.once.count == self.stepCount - 1 and (col,row) in self.wqcore.LinkPoses((self.once.col,self.once.row)):
self.back_one(None)
return
self.once = Record(row, col, self.stepCount,0)
for d in deads:
r = self.wqcore.GetRecord(d)
r = Record(r.row,r.col,r.count, -self.wqcore.Current().color)
self.deads.append(r)
self.delete_stone(d)
for i,s in enumerate(steps):
if (s.col,s.row) == d:
self.wqcore.Steps[i].color = 0
else:
deads = self.wqcore.KillSelf()
if deads != None:
for d in deads:
r = self.wqcore.GetRecord(d)
r = Record(r.row,r.col,r.count,self.wqcore.Current().color)
self.deads.append(r)
self.delete_stone(d)
for i,s in enumerate(steps):
if (s.col,s.row) == d:
self.wqcore.Steps[i].color = 0
self.wqcore.DeadPoses.clear()
self.wqcore.UpdateRecords()
def back_one(self, e):
if len(self.poses) == 0: return
pos = self.poses.pop()
self.delete_stone(pos)
self.show_deads()
p = self.wqcore.Steps.pop()
for i, r in enumerate(self.wqcore.Records[:]):
if (p.col, p.row) == (r.col,r.row):
self.wqcore.Records[i].count = -1
self.wqcore.Records[i].color = 0
self.isBlack = not self.isBlack
self.stepCount -= 1
def get_deads(self):
self.wqcore.GetDeadBlocks(self.deads)
result = [r for r in self.deads if (r.col,r.row) in self.wqcore.DeadPoses]
self.wqcore.DeadPoses.clear()
for r in result[:]:
self.deads.remove(r)
return result
def show_deads(self):
deads = self.get_deads()
if deads == None: return
for d in deads:
for i, s in enumerate(self.wqcore.Steps[:]):
if s.count == d.count:
self.wqcore.Steps[i].color = d.color
stone = self.black_stone if d.color == 1 else self.white_stone
self.canvas.create_image(self.offset + d.col*R.StoneSize, self.offset + d.row*R.StoneSize, image=stone)
self.wqcore.DeadPoses.clear()
self.wqcore.UpdateRecords()
def delete_stone(self, pos):
r = self.wqcore.GetRecord(pos)
if r == None or r.color == 0: return
col,row = pos
x,y = self.offset + col*R.StoneSize, self.offset + row*R.StoneSize
stone = self.canvas.find_closest(x,y)
if stone:
self.canvas.delete(stone)
class WqCore:
def __init__(self):
self.Steps = [] # 棋步
self.Records = [Record(r,c) for r in range(0,19) for c in range(0,19)] # 棋谱
self.DeadPoses = []
def UpdateRecords(self):
records = self.Records[:]
for i,r in enumerate(records):
for s in self.Steps:
if (s.col,s.row) == (r.col,r.row):
self.Records[i] = s
def Current(self):
if len(self.Steps) == 0: return None
else: return self.Steps[-1]
def KillOther(self):
c = self.Current()
if c == None: return None
poses = self.LinkPoses((c.col,c.row))
records = [r for r in self.Records if (r.col,r.row) in poses and r.color != c.color and r.color != 0]
result = []
for temp in records:
self.DeadPoses.clear()
deads = self.GetDeadPoses((temp.col, temp.row))
if deads != None: [result.append(d) for d in deads]
return None if len(result) == 0 else result[:]
def KillSelf(self):
c = self.Current()
if c == None: return None
poses = self.LinkPoses((c.col,c.row))
records = [r for r in self.Records if (r.col,r.row) in poses and r.color == c.color and r.color != 0]
for temp in records:
self.DeadPoses.clear()
deads = self.GetDeadPoses((temp.col, temp.row))
if deads != None: return deads
return None
def GetRecord(self, pos):
for r in self.Records:
if (r.col, r.row) == pos: return r
return None
def GetDeadPoses(self, pos):
record = self.GetRecord(pos)
self.DeadPoses.append(pos)
poses = set(self.LinkPoses(pos)).difference(set(self.DeadPoses))
records = [r for r in self.Records if (r.col,r.row) in poses]
result = []
for t in records:
if t.color == 0:
self.DeadPoses.clear()
return None
elif t.color == record.color:
deads = self.GetDeadPoses((t.col,t.row))
if deads != None: continue
else: return None
return self.DeadPoses
def GetDeadBlocks(self, deads):
self.DeadPoses.clear()
curr = self.Current()
col,row,color = curr.col, curr.row,curr.color
poses = self.LinkPoses((col,row))
for pos in poses:
for d in deads:
if (d.col,d.row) == pos and d.color == -color:
self.GetDeadBlock(d,deads)
def GetDeadBlock(self, record, deads):
col,row,color = record.col,record.row,record.color
self.DeadPoses.append((col,row))
links = set(self.LinkPoses((col,row))).difference(set(self.DeadPoses))
poses = [(r.col,r.row) for r in deads if (r.col,r.row) in links and r.color == color]
for x,y in poses:
r = self.GetRecord((x,y))
r.color = color
self.GetDeadBlock(r, deads)
def IsValid(self, pos):
x,y = pos
if 0 <= x < 19 and 0 <= y < 19: return True
else: return False
def LinkPoses(self, pos):
poses = []
x,y = pos
for i in range(-1,2):
for j in range(-1,2):
if i==j or i==-j: continue
if self.IsValid(pos):
poses.append((x+j,y+i))
poses.append(pos)
return poses
def RoundPoses(self, pos):
poses = []
x,y = pos
for i in range(-1,2):
for j in range(-1,2):
if self.IsValid(pos):
poses.append((x+j,y+i))
return poses
class Record:
def __init__(self, row=-1,col=-1,count=-1,color=0):
self.row = row
self.col = col
self.count = count
self.color = color # black:1,white:-1,empty=0
def __str__(self):
return '(row:{},col:{},count:{},color:{})'.format(self.row,self.col,self.count,self.color)
class R:
# stone and board
StoneSize = 32
CellNumber = 19
StarNumber = 9
# bind event names
ButtonLeft = '<Button-1>'
ButtonRight = '<Button-3>'
if __name__ == "__main__":
c = WqCore()
代码在 x01.lab/py/game/weiqi 下,链接:https://gitee.com/chinax01/x01.lab
来源:https://www.cnblogs.com/china_x01/p/12456382.html
