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()
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()
3.下载