目标:

1.对战功能的中国象棋游戏2.UDP局域网对战功能3.有聊天框便于交流4.有若干按钮,悔棋或退出游戏等。

2.游戏逻辑分析:

棋盘是个9*10(90个交叉点)的布局,红黑子各为16个。

9*10的二维列表表示棋盘,有棋子的交点对应的元素为棋子图像,没有棋子的交点所对应的元素设置为-1,用此二维列表表示当前棋盘的棋局。

 

将或帅只能在各自的九宫格内垂直或水平移动。

士只能在九宫格对角线位置移动。

象只能在自己一侧移动,可以每次沿着对角线移动两点,但在移动过程中不能穿越障碍(象腿)

马可以随便移动,每次沿着水平或垂直方向移动一点,然后再沿对角线移动一次,但在移动过程中不能穿越障碍(马腿)

车可以任何位置水平或垂直移动到无阻碍的点

炮可以任何位置水平或垂直移动,但吃子时必须跳过一个棋子来吃掉。

兵每次只能移动一点,过河后,便增加了向左右移动的能力,兵卒不允许向后移动。

3.胜负逻辑

自己的帅被吃掉、发出认输请求、超出时间算失败。

 

 

首先代码实现移动棋子功能试验:

from tkinter import *


def callback(): #棋子移动函数测试
    cv.move(rt1, 150, 150)  # 图形对象、x偏移量、y坐标偏移量


root = Tk()
root.title('移动帅棋子')
cv = Canvas(root, bg='white', width=260, height=220)

img1 = PhotoImage(file='红帅.png')
cv.create_rectangle(40, 40, 190, 190, outline='red', fill='green')  # 左上坐标40,40,右下190,190,后色边缘,绿色北京
rt1 = cv.create_image((40, 40), image=img1)  # 40,40坐标插入图片
cv.pack()  # 显示画布对象
button1 = Button(root, text='移动棋子', command=callback, fg='red')  # 按钮设置
button1.pack()
root.mainloop()

再测试棋子被吃掉的方法:

def callback():  # 棋子移动函数测试
    cv.move(rt1, 150, 150)  # 图形对象、x偏移量、y坐标偏移量


def callback2():  # 测试删除棋子图片
    cv.delete(rt1)


root = Tk()
root.title('移动帅棋子')
cv = Canvas(root, bg='white', width=260, height=220)

img1 = PhotoImage(file='红帅.png')
cv.create_rectangle(40, 40, 190, 190, outline='red', fill='green')  # 左上坐标40,40,右下190,190,后色边缘,绿色北京
rt1 = cv.create_image((40, 40), image=img1)  # 40,40坐标插入图片
cv.pack()  # 显示画布对象
button1 = Button(root, text='移动棋子', command=callback, fg='red')  # 按钮设置
button1.pack()
button2 = Button(root, text='删除棋子', command=callback2, fg='red')  # 按钮设置
button2.pack()
root.mainloop()

 

显示棋盘图像:

from tkinter import *

root = Tk()
root.title('棋盘')
img1 = PhotoImage(file='棋盘.png')
cv = Canvas(root, bg='white', width=720, height=800)  # 720*800的画布
p1 = cv.create_image((0, 0), image=img1)  # 从左上角0,0对齐
cv.coords(p1, (360, 400))  # cv的中心点坐标
cv.pack()
root.mainloop()

 

画出棋子:

from tkinter import *
from tkinter.messagebox import *

root = Tk()
root.title('中国象棋')
img1 = PhotoImage(file='棋盘.png')
cv = Canvas(root, bg='white', width=720, height=800)  # 720*800的画布
p1 = cv.create_image((0, 0), image=img1)  # 从左上角0,0对齐
cv.coords(p1, (360, 400))  # cv的中心点坐标

chessname = ['黑车', '黑马', '黑象', '黑仕', '黑将', '黑仕', '黑象', '黑马', '黑车', '黑卒', '黑炮', '红车', '红马', '红相', '红仕', '红帅', '红仕', '红相',
             '红马', '红车', '红兵', '红炮']
imgs = [PhotoImage(file=chessname[i] + '.png') for i in range(0, 22)]
chessmap = [[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] for y in range(10)]  # 棋盘初始符号
dict_ChessName = {}
LocalPlayer = '红'  # 记录自己是红方还是黑方
first = True  # 区分第一次还是第二次选中棋子 IsMyTurn=True
rect1 = 0
rect2 = 0
firstChessid = 0

for i in range(0, 9):
    img = imgs[i]
    id = cv.create_image((60 + 76 * i, 54), image=img)  # 76*76格子的棋盘,id独有
    chessmap[i][0] = id
    dict_ChessName[id] = chessname[i]
for i in range(0, 5):
    img = imgs[9]  # 卒子图像
    id = cv.create_image((60 + 76 * 2 * i, 54 + 3 * 76), image=img)
    chessmap[i * 2][3] = id
    dict_ChessName[id] = '黑卒'
img = imgs[10]  # 黑炮
id = cv.create_image((60 + 76 * 7, 54 + 2 * 76), image=img)
chessmap[7][2] = id
dict_ChessName[id] = '黑炮'
id = cv.create_image((60 + 76 * 1, 54 + 2 * 76), image=img)
chessmap[1][2] = id
dict_ChessName[id] = '黑炮'

for i in range(0, 9):
    img = imgs[i + 11]
    id = cv.create_image((60 + 76 * i, 54 + 9 * 76), image=img)  # 76*76格子的棋盘,id独有
    chessmap[i][9] = id
    dict_ChessName[id] = chessname[i + 11]
for i in range(0, 5):
    img = imgs[20]  # 红兵图像
    id = cv.create_image((60 + 76 * 2 * i, 54 + 6 * 76), image=img)
    chessmap[i * 2][6] = id
    dict_ChessName[id] = '红兵'
img = imgs[21]  # 红炮
id = cv.create_image((60 + 76 * 7, 54 + 7 * 76), image=img)
chessmap[7][7] = id
dict_ChessName[id] = '红炮'
id = cv.create_image((60 + 76 * 1, 54 + 7 * 76), image=img)
chessmap[1][7] = id
dict_ChessName[id] = '红炮'

cv.pack()
root.mainloop()

 

实现代码:

# -*- coding: utf-8 -*-
# @FileName: qizi.py
# @Software: PyCharm
from tkinter import *
from tkinter.messagebox import *


# def callback(event):  # 走棋
#     global LocalPlayer
#     global chessmap
#     global rect1, rect2
#     global firstChessid, secondChessid
#     global x1, x2, y1, y2
#     global first
#     x = (event.x - 14) // 76
#     y = (event.y - 14) // 76
#     print('点击处:', x, y, '玩家', LocalPlayer)
#     if first:
#         x1 = x
#         y1 = y
#         firstChessid = chessmap[x1][y1]
#         if not (chessmap[x1][y1] == -1):  # 位置不为空
#             player = dict_ChessName[firstChessid][0]  # 获取棋子颜色
#             if player != LocalPlayer:
#                 print('单击对方棋子了!')
#                 return
#             print('第一次单击', firstChessid)
#             first = False
#             rect1 = cv.create_rectangle(60 + 77 * x - 40, 54 + y * 76 - 38, 60 + 76 * x + 80 - 40,
#                                         54 + y * 76 + 80 - 38, outline='red')
#     else:
#         x2 = x
#         y2 = y
#         secondChessid = chessmap[x2][y2]
#         if not (chessmap[x2][y2] == -1):  # 位置不为空
#             player = dict_ChessName[firstChessid][0]  # 获取棋子颜色
#             if player == LocalPlayer:
#                 firstChessid = chessmap[x2][y2]
#                 print('第二次单击', firstChessid)
#                 cv.delete(rect1)
#                 x1 = x
#                 y1 = y
#                 rect1 = cv.create_rectangle(60 + 76 * x - 40, 54 + y * 76 - 38, 60 + 76 * x + 80 - 40,
#                                             54 + y * 76 + 80 - 38, outline='red')
#                 print('第二次单击', firstChessid)
#                 return
#             else:
#                 rect2 = cv.create_rectangle(60 + 76 * x - 40, 54 + y * 76 - 38, 60 + 76 * x + 80 - 40,
#                                             54 + y * 76 + 80 - 38, outline='yellow')
#                 print('kkk', firstChessid)
#                 if chessmap[x2][y2] == ' ' or chessmap[x2][y2] == -1:
#                     print('目标位置没有棋子,移动棋子', firstChessid, x2, y2, x1, y1)
#                     if IsAblePut(firstChessid, x2, y2, x1, y1):
#                         print('可以移动棋子', x1, y1)
#                         cv.move(firstChessid, 76 * (x2 - x1), 76 * (y2 - y1))
#                         chessmap[x1][y1] = -1
#                         chessmap[x2][y2] = firstChessid
#                         cv.delete(rect1)
#                         cv.delete(rect2)
#                     else:
#                         print('错误走棋,不符合走棋规则')
#                         showinfo(title='提示', message='不符合走棋规则')
#                         return
#                 else:
#                     if not (chessmap[x2][y2] == -1) and IsAblePut(firstChessid, x2, y2, x1, y1):
#                         first = True
#                         print('可以吃子', x1, y1)
#                         cv.move(firstChessid, 76 * (x2 - x1), 76 * (y2 - y1))
#                         chessmap[x1][y1] = -1
#                         chessmap[x2][y2] = firstChessid
#                         cv.delete(secondChessid)
#                         cv.delete(rect1)
#                         cv.delete(rect2)
#                         if dict_ChessName[secondChessid][1] == '将':
#                             showinfo(title='提示', message='红方你赢了')
#                             return
#                         if dict_ChessName[secondChessid][1] == '帅':
#                             showinfo(title='提示', message='黑方你赢了')
#                             return
#                         SetMyTrun(False)
#                     else:
#                         print('不能吃子')
#                         label1['text'] = '不能吃子'
#                         cv.delete(rect2)
def callback(event):  # 走棋picBoard_MouseClick
    global LocalPlayer
    global chessmap
    global rect1, rect2  # 选中框图像id
    global firstChessid, secondChessid
    global x1, x2, y1, y2
    global first
    print("clicked at", event.x, event.y, LocalPlayer)
    x = (event.x - 14) // 76  # 换算棋盘坐标
    y = (event.y - 14) // 76
    print("clicked at", x, y, LocalPlayer)

    # if (IsMyTurn == False):
    #   return;

    if (first):  # 第1次单击棋子
        x1 = x;
        y1 = y;
        firstChessid = chessmap[x1][y1]
        if not (chessmap[x1][y1] == -1):
            player = dict_ChessName[firstChessid][0]
            if (player != LocalPlayer):
                print("单击成对方棋子了!");
                return
            print("第1次单击", firstChessid)
            first = False;
            rect1 = cv.create_rectangle(60 + 76 * x - 40, 54 + y * 76 - 38, 60 + 76 * x + 80 - 40,
                                        54 + y * 76 + 80 - 38, outline="red")  # 画选中标记框

    else:  # 第2次单击
        x2 = x;
        y2 = y;
        secondChessid = chessmap[x2][y2]
        # 目标处如果是自己的棋子,则换上次选择的棋子
        if not (chessmap[x2][y2] == -1):
            player = dict_ChessName[secondChessid][0]
            if (player == LocalPlayer):  # 如果是自己的棋子,则换上次选择的棋子
                firstChessid = chessmap[x2][y2]
                print("第2次单击", firstChessid)
                cv.delete(rect1);  # 取消上次选择的棋子标记框
                x1 = x;
                y1 = y;
                # 设置选择的棋子颜色
                rect1 = cv.create_rectangle(60 + 76 * x - 40, 54 + y * 76 - 38, 60 + 76 * x + 80 - 40,
                                            54 + y * 76 + 80 - 38, outline="red")  # 画选中标记框
                print("第2次单击", firstChessid)
                return;
            else:  # 在落子目标处画框
                rect2 = cv.create_rectangle(60 + 76 * x - 40, 54 + y * 76 - 38, 60 + 76 * x + 80 - 40,
                                            54 + y * 76 + 80 - 38, outline="yellow")  # 目标处画框;

        # 目标处没棋子,移动棋子
        print("kkkkk", firstChessid)
        if (chessmap[x2][y2] == " " or chessmap[x2][y2] == -1):  # 目标处没棋子,移动棋子
            print("目标处没棋子,移动棋子", firstChessid, x2, y2, x1, y1)
            if (IsAblePut(firstChessid, x2, y2, x1, y1)):  # 判断是否可以走棋
                print("can移动棋子", x1, y1)
                cv.move(firstChessid, 76 * (x2 - x1), 76 * (y2 - y1));
                # ******************************************
                # 在map取掉原CurSelect棋子
                chessmap[x1][y1] = -1;
                chessmap[x2][y2] = firstChessid
                cv.delete(rect1);
                cv.delete(rect2);

                # send
                # send("move" + "|" + idx.ToString() + "|" + (10 - x2).ToString() + "|"
                #    + Convert.ToString(11 - y2) + "|" + StepList[StepList.Count - 1]);
                # CurSelect = 0;
                first = True;
                SetMyTurn(False);  # 该对方了
                # toolStripStatusLabel1.Text = "";
            else:
                # 错误走棋
                print("不符合走棋规则");
                showinfo(title="提示", message="不符合走棋规则")
            return;
        else:
            # 目标处有棋子,可以吃子
            if (not (chessmap[x2][y2] == -1) and IsAblePut(firstChessid, x2, y2, x1, y1)):  # 可以吃子
                first = True;
                print("can吃子", x1, y1)
                cv.move(firstChessid, 76 * (x2 - x1), 76 * (y2 - y1));
                # ******************************************
                # 在map取掉原CurSelect棋子
                chessmap[x1][y1] = -1;
                chessmap[x2][y2] = firstChessid
                cv.delete(secondChessid);
                cv.delete(rect1);
                cv.delete(rect2);

                if (dict_ChessName[secondChessid][1] == "将"):  # "将"
                    showinfo(title="提示", message="红方你赢了")
                    return;
                if (dict_ChessName[secondChessid][1] == "帅"):  # "帅"
                    showinfo(title="提示", message="黑方你赢了")
                    return;
                # send

                SetMyTurn(False);  # 该对方了
                # toolStripStatusLabel1.Text = "";
            else:  # 不能吃子
                print("不能吃子");


def SetMyTurn(flag):
    global LocalPlayer
    IsMyTurn = flag
    if LocalPlayer == '红':
        LocalPlayer = '黑'
        label1['text'] = '轮到黑方走'
    else:
        LocalPlayer = '红'
        label1['text'] = '轮到红方走'


def IsAblePut(id, x, y, oldx, oldy):  # 判断能走走棋,返回逻辑值
    """oldx,oldy为原坐标,x,y为移动后新坐标,"""
    qi_name = dict_ChessName[id][1]  # 字符串的第二个字符,得到棋子类型,如“将”
    if qi_name == '将' or qi_name == '帅':  # 帅棋走法约束
        if (x - oldx) * (y - oldy) != 0:  # 斜着走
            return False
        if abs(x - oldx) > 1 or abs(y - oldy) > 1:  # 走多个格子
            return False
        if x < 3 or x > 5 or (3 <= y <= 6):  # 超出九宫格
            return False
        return True
    if qi_name == '仕' or qi_name == '士':
        if (x - oldx) * (y - oldy) == 0:  # 直着走
            return False
        if abs(x - oldx) > 1 or abs(y - oldy) > 1:  # 走多个格子
            return False
        if x < 3 or x > 5 or (3 <= y <= 6):  # 超出九宫格
            return False
    if qi_name == '象' or qi_name == '相':
        if (x - oldx) * (y - oldy) == 0:  # 直着走
            return False
        if abs(x - oldx) != 2 or abs(y - oldy) != 2:
            return False
        if y < 5 and qi_name == '相':
            return False
        if y >= 5 and qi_name == '象':
            return False
        i, j = 0, 0
        if x - oldx == 2:
            i = x - 1
        if x - oldx == -2:
            i = x + 1
        if y - oldy == 2:
            j = y - 1
        if y - oldy == -2:
            j = y + 1
        if chessmap[i][j] != -1:
            return False
        return True
    if qi_name == '马':
        if abs(x - oldx) * abs(y - oldy) == '马':
            return False
        if x - oldx == 2:
            if chessmap[x - 1][oldy] != -1:  # 蹩马腿
                return False
        if x - oldx == -2:
            if chessmap[x + 1][oldy] != -1:  # 蹩马腿
                return False
        if y - oldy == 2:
            if chessmap[oldx][y - 1] != -1:  # 蹩马腿
                return False
        if y - oldy == -2:
            if chessmap[oldx][y + 1] != -1:  # 蹩马腿
                return False
        return True
    if qi_name == '车':
        if (x - oldx) * (y - oldy) != 0:
            return False
        if x != oldx:

            if oldx > x:
                t = x
                x = oldx
                oldx = t
            for i in range(oldx, x + 1):
                if i != x and i != oldx:
                    if chessmap[i][y] != -1:
                        return False
        if y != oldy:

            if oldy > y:
                t = y
                y = oldy
                oldy = t
            for i in range(oldy, y + 1):
                if i != y and i != oldy:
                    if chessmap[x][i] != -1:
                        return False
        return True
    if qi_name == '炮':
        swapflagx = False
        swapflagy = False
        if (x - oldx) * (y - oldy) != 0:
            return False
        c = 0
        if x != oldx:
            if oldx > x:
                t = x
                x = oldx
                oldx = t
                swapflagx = True
            for i in range(oldx, x + 1):
                if i != x and i != oldx:
                    if chessmap[i][y] != -1:
                        c += 1
        if y != oldy:
            if oldy > y:
                t = y
                y = oldy
                oldy = t
                swapflagy = True
            for i in range(oldy, y + 1):
                if i != y and i != oldy:
                    if chessmap[x][i] != -1:
                        c += 1
        if c > 1:
            return False
        if c == 0:
            if swapflagx:
                t = x
                x = oldx
                oldx = t
            if swapflagy:
                t = y
                y = oldy
                oldy = t
            if chessmap[x][y] != -1:
                return False
        if c == 1:
            if swapflagx:
                t = x
                x = oldx
                oldx = t
            if swapflagy:
                t = y
                y = oldy
                oldy = t
            if chessmap[x][y] == -1:
                return False
        return True
    if qi_name == '卒' or qi_name == '兵':
        if (x - oldx) * (y - oldy) != 0:  # 斜着走
            return False
        if abs(x - oldx) > 1 or abs(y - oldy) > 1:  # 走多个格子
            return False
        if y >= 5 and x - oldx != 0 and qi_name == '兵':
            return False
        if y < 5 and x - oldx != 0 and qi_name == '卒':
            return False
        if y - oldy > 0 and qi_name == '兵':
            return False
        if y - oldy < 0 and qi_name == '卒':
            return False
        return True
    return True


root = Tk()
root.title('中国象棋')
img1 = PhotoImage(file='棋盘.png')
cv = Canvas(root, bg='white', width=720, height=800)  # 720*800的画布
p1 = cv.create_image((0, 0), image=img1)  # 从左上角0,0对齐
cv.coords(p1, (360, 400))  # cv的中心点坐标

chessname = ['黑车', '黑马', '黑象', '黑仕', '黑将', '黑仕', '黑象', '黑马', '黑车', '黑卒', '黑炮', '红车', '红马', '红相', '红仕', '红帅', '红仕', '红相',
             '红马', '红车', '红兵', '红炮']
imgs = [PhotoImage(file=chessname[i] + '.png') for i in range(0, 22)]
chessmap = [[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1] for y in range(10)]  # 棋盘初始符号
dict_ChessName = {}
LocalPlayer = '红'  # 记录自己是红方还是黑方
first = True  # 区分第一次还是第二次选中棋子 IsMyTurn=True

rect1 = 0
rect2 = 0  # 棋子被选中时的框线
firstChessid = 0
secondChessid = 0

for i in range(0, 9):
    img = imgs[i]
    id = cv.create_image((60 + 76 * i, 54), image=img)  # 76*76格子的棋盘,id独有
    chessmap[i][0] = id
    dict_ChessName[id] = chessname[i]
for i in range(0, 5):
    img = imgs[9]  # 卒子图像
    id = cv.create_image((60 + 76 * 2 * i, 54 + 3 * 76), image=img)
    chessmap[i * 2][3] = id
    dict_ChessName[id] = '黑卒'
img = imgs[10]  # 黑炮
id = cv.create_image((60 + 76 * 7, 54 + 2 * 76), image=img)
chessmap[7][2] = id
dict_ChessName[id] = '黑炮'
id = cv.create_image((60 + 76 * 1, 54 + 2 * 76), image=img)
chessmap[1][2] = id
dict_ChessName[id] = '黑炮'

for i in range(0, 9):
    img = imgs[i + 11]
    id = cv.create_image((60 + 76 * i, 54 + 9 * 76), image=img)  # 76*76格子的棋盘,id独有
    chessmap[i][9] = id
    dict_ChessName[id] = chessname[i + 11]
for i in range(0, 5):
    img = imgs[20]  # 红兵图像
    id = cv.create_image((60 + 76 * 2 * i, 54 + 6 * 76), image=img)
    chessmap[i * 2][6] = id
    dict_ChessName[id] = '红兵'
img = imgs[21]  # 红炮
id = cv.create_image((60 + 76 * 7, 54 + 7 * 76), image=img)
chessmap[7][7] = id
dict_ChessName[id] = '红炮'
id = cv.create_image((60 + 76 * 1, 54 + 7 * 76), image=img)
chessmap[1][7] = id
dict_ChessName[id] = '红炮'

# print(dict_ChessName)
cv.bind('<Button-1>', callback)
label1 = Label(root, fg='red', bg='white', text='红方先走')  # 显示先手标签
label1['text'] = '红方先走1'
label1.pack()
cv.pack()
root.mainloop()

 

给象棋程序网络对战功能。

1.发送消息功能UDP

2.接收并根据消息内容画出棋盘情况

3.下棋并切换玩家直到一方胜利或者退出