目标:
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.下棋并切换玩家直到一方胜利或者退出