x01.weiqi: 跨平台 python 实现

て烟熏妆下的殇ゞ 提交于 2020-03-10 17:28:39

大多数时间,都在使用 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()
main.py

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() 
core.py

代码在 x01.lab/py/game/weiqi 下,链接:https://gitee.com/chinax01/x01.lab

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!