x01.chess: 国际象棋

忽略王车易位,国际象棋实现起来还是比较简单的,按照行棋路线不断进行即可,遇到边界或棋子则停,兵,王,马只走一步,兵有方向,如此而已。

1.效果图 

                                   

2.代码

import sys, copy, os, configparser
import tkinter as tk 
from tkinter import messagebox

import utils 
from game.chess.core import Board, R 

sys.path.append(utils.R.CurrentDir)

class MainWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("x01.chess")
        size = (R.CellNumber+0.5)*R.CellSize
        utils.R.win_center(self, w=size, h=size)

        self.background = 'lightgrey'
        self.foreground = 'black'
        self.menu = tk.Menu(self, bg=self.background, fg=self.foreground)
        utils.R.generate_menus(self, ['file', 'help'])
        self.configure(menu=self.menu)

        self.board = Board(self)
        self.board.pack(pady=R.CellSize/4)

    def file_start_game(self):
        self.board.pack_forget()
        self.board = Board(self)
        self.board.pack(pady=R.CellSize/4)

    def help_about(self):
        messagebox.showinfo('x01.chess', '国际象棋程序,版权属于x01(黄雄)所有。')

if __name__ == "__main__":
    w = MainWindow()
    w.mainloop()
main.py
import tkinter as tk 
import utils
import os
from itertools import count 
from tkinter import messagebox

class R:
    ImgDir = os.path.join(utils.R.CurrentDir, r'game\chess\res')
    CellNumber = 8
    CellSize = 64
    Color1 = '#e6a803'
    Color2 = '#8b8350'
    Highlight = '#2ef70d'

    OrthogalPoses = ((-1, 0), (0, 1), (1, 0), (0, -1))
    DiagonalPoses = ((-1, -1), (-1, 1), (1, -1), (1, 1))
    KnightPoses = ((-2, -1), (-2, 1), (-1, -2), (-1, 2),
                    (1, -2), (1, 2), (2, -1), (2, 1))

    Directions = {
        'p': ((1,0), (2,0), (1,1), (1,-1)),
        'n': KnightPoses,
        'b': DiagonalPoses,
        'r': OrthogalPoses,
        'q': DiagonalPoses + OrthogalPoses,
        'k': DiagonalPoses + OrthogalPoses,

        'P': ((-1,0), (-2,0), (-1,1), (-1,-1)),
        'N': KnightPoses,
        'B': DiagonalPoses,
        'R': OrthogalPoses,
        'Q': DiagonalPoses + OrthogalPoses,
        'K': DiagonalPoses + OrthogalPoses
    }

class Board(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master 
        self.cell_size = R.CellSize 
        self.offset = R.CellSize // 2
        self.cell_number = R.CellNumber
        self.color1 = R.Color1 
        self.color2 = R.Color2 
        self.highlight = R.Highlight

        self.canvas = tk.Canvas(self, bg="#999" , width=R.CellNumber*R.CellSize, height=R.CellNumber*R.CellSize)
        self.canvas.pack()

        self.piece_images = {
            'r': tk.PhotoImage(file=os.path.join(R.ImgDir, 'rook_white.png')),
            'n': tk.PhotoImage(file=os.path.join(R.ImgDir, 'knight_white.png')),
            'b': tk.PhotoImage(file=os.path.join(R.ImgDir, 'bishop_white.png')),
            'q': tk.PhotoImage(file=os.path.join(R.ImgDir, 'queen_white.png')),
            'k': tk.PhotoImage(file=os.path.join(R.ImgDir, 'king_white.png')),
            'p': tk.PhotoImage(file=os.path.join(R.ImgDir, 'pawn_white.png')),

            'R': tk.PhotoImage(file=os.path.join(R.ImgDir, 'rook_black.png')),
            'N': tk.PhotoImage(file=os.path.join(R.ImgDir, 'knight_black.png')),
            'B': tk.PhotoImage(file=os.path.join(R.ImgDir, 'bishop_black.png')),
            'Q': tk.PhotoImage(file=os.path.join(R.ImgDir, 'queen_black.png')),
            'K': tk.PhotoImage(file=os.path.join(R.ImgDir, 'king_black.png')),
            'P': tk.PhotoImage(file=os.path.join(R.ImgDir, 'pawn_black.png')),
        }
        self.pieces = []
        self.init_pieces()

        self.current_move = (None, None)
        self.chess_core = ChessCore(self.pieces)
        self.is_black = True 

        self.draw_cells()
        self.draw_pieces()
        self.bind_events()

    def init_pieces(self):
        names = 'rnbqkbnrppppppppPPPPPPPPRNBQKBNR'
        for row in range(8):
            for col in range(8):
                if row == 0 or row==1:
                    self.pieces.append(Piece(row,col,names[row*8+col]))
                elif row == 6 or row==7:
                    self.pieces.append(Piece(row,col,names[(row-4)*8+col]))
                else:
                    self.pieces.append(Piece(row,col,'.'))

    def draw_pieces(self):
        self.canvas.delete('pieces')
        for p in self.pieces:
            if p.name == '.': continue
            self.canvas.create_image(p.col*R.CellSize+self.offset, 
                p.row*R.CellSize+self.offset, image=self.piece_images[p.name], tags=('pieces'), anchor='c')

    def bind_events(self):
        self.canvas.bind('<Button-1>', self.click)

    def click(self, e=None):
        dead = self.check_dead()
        if dead[0]: 
            messagebox.showinfo('x01.chess', 'black king is dead!')
            return
        if dead[1]:
            messagebox.showinfo('x01.chess', 'white king is dead!')
            return

        self.canvas.delete("highlights")
        r,c = e.y // self.cell_size,  e.x // self.cell_size
        piece = self.chess_core.get_piece((r,c))
        moves = self.chess_core.get_moves(self.is_black)
        highlight_moves = [p[1] for p in moves if piece == p[0]]
        for h in highlight_moves:
            self.draw_highlight(h)
            self.draw_pieces()

        if self.current_move[0]  and self.current_move[1] is None:
            self.current_move = (self.current_move[0], piece)
            if self.current_move[0] and self.current_move[1]:
                moves = self.chess_core.get_moves(self.is_black)
                can_moves = [p[1] for p in moves if self.current_move[0] == p[0]]
                if self.current_move[1] in can_moves:
                    if self.move():
                        self.current_move = (None, None)
                        self.is_black = not self.is_black
                        self.p2q()
                        self.draw_pieces()
        self.current_move = (piece, None)

    def check_dead(self):
        blackdead, whitedead = True, True
        for p in self.pieces:
            if 'k' == p.name: whitedead = False
            if 'K' == p.name: blackdead = False
        return (blackdead, whitedead)

    def p2q(self):
        pieces = self.pieces[:]
        for i, p in enumerate(pieces):
            if p.name == 'p' and p.row == 7:
                self.pieces[i].name = 'q'
            elif p.name == 'P' and p.row == 0:
                self.pieces[i].name = 'Q'

    def move(self):
        org, dest = self.current_move
        org_index = self.get_index(org)
        dest_index = self.get_index(dest)
        if org_index is None or dest_index is None: 
            return  False
        self.pieces[dest_index].name = org.name 
        self.pieces[org_index].name = '.'
        self.draw_pieces()
        return True 

    def get_index(self, piece):
        for i, p in enumerate(self.pieces):
            if p == piece: return i 
        return None 
        
    def draw_cells(self):
        color = self.color2 
        for row in range(self.cell_number):
            color = self.alternate_color(color)
            for col in range(self.cell_number):
                x1,y1 = self.get_xy(row,col) 
                x2,y2 = x1+self.cell_size, y1+self.cell_size
                self.canvas.create_rectangle(x1,y1,x2,y2,fill=color)
                color = self.alternate_color(color)

    def get_xy(self, row, col):
        x = self.cell_size * col 
        y = self.cell_size * row 
        return (x,y)

    def alternate_color(self, color):
        if color == self.color1:
            return self.color2 
        return self.color1 

    def test(self):
        moves = self.chess_core.get_moves(isblack=False)
        for org,dest in moves:
            print(org, dest)

    def draw_highlight(self, piece):
        # self.canvas.delete('highlights')
        x1,y1 = self.get_xy(piece.row, piece.col)
        x2,y2 = x1+self.cell_size, y1+self.cell_size
        self.canvas.create_rectangle(x1,y1,x2,y2,fill=R.Highlight, tags='highlights')

class Piece:
    def __init__(self, row, col, name):
        self.row = row 
        self.col = col 
        self.name = name

    def __str__(self):
        return '(row={},col={},name={})'.format(self.row, self.col, self.name)

    def __eq__(self, other):
        return self.row == other.row and self.col == other.col

class ChessCore:
    def __init__(self, pieces):
        self.pieces = pieces[:]
        self.rotates = []

    def get_piece(self, pos):
        for p in self.pieces:
            if pos == (p.row, p.col):
                return p
        return None 
    
    def get_moves(self, isblack=True):
        # 忽略王车易位
        for p in self.pieces:
            if isblack:
                if p.name.islower() or p.name == '.': continue
                for d in R.Directions[p.name]:
                    for i in range(1,8):
                        pos = (p.row + d[0]*i, p.col + d[1]*i)
                        dest = self.get_piece(pos)
                        if not self.valid_pos(pos) or dest.name.isupper():  break
                        if p.name == 'P' and d in ((-1,0), (-2,0)) and dest.name != '.': break
                        if p.name == 'P' and d == (-2,0) and (p.row != 6 or self.get_piece((p.row-1,p.col)).name != '.'): break
                        if p.name == 'P' and d in ((-1,1), (-1,-1)) and dest.name == '.': break 
                        yield (p, dest)
                        if p.name in 'PNK' or dest.name.islower(): break 
            else:
                if p.name.isupper() or p.name == '.': continue
                for d in R.Directions[p.name]:
                    for i in range(1,9): 
                        pos = (p.row + d[0]*i, p.col + d[1]*i)
                        dest = self.get_piece(pos)
                        if not self.valid_pos(pos) or dest.name.islower():  break
                        if p.name == 'p' and d in ((1,0), (2,0)) and dest.name != '.': break
                        if p.name == 'p' and d == (2,0) and (p.row != 1 or self.get_piece((p.row+1,p.col)).name != '.'): break
                        if p.name == 'p' and d in ((1,1), (1,-1)) and dest.name == '.': break 
                        yield (p, dest)
                        if p.name in 'pnk' or dest.name.islower(): break 


    def valid_pos(self, pos):
        if 0 <= pos[0] <= 7 and 0 <= pos[1] <= 7: return True
        return False 


if __name__ == "__main__":
    b = Board()
    b.test()
core.py

3.下载

x01.lab/py/game/chess

posted on 2020-05-20 15:45  x01  阅读(465)  评论(0编辑  收藏  举报

导航