Compare commits

..

27 Commits

Author SHA1 Message Date
tzzzzzzzx 57ff012187 final final
2 years ago
tzzzzzzzx e6128c7b00 final_commit
2 years ago
tzzzzzzzx dc786654e4 basic function & foul judge completed
2 years ago
tzzzzzzzx 413be11470 Merge branch 'qq_branch'
2 years ago
tzzzzzzzx 8deec0d443 first total function
2 years ago
tzzzzzzzx 636deddbf1 presaved before 1001
2 years ago
DoubleQ 1441a00475 比较完整
3 years ago
tzzzzzzzx 677e3e0716 fixed countdown and current_player a bit...
3 years ago
DoubleQ 12d0a71380 UI加入了部分按钮
3 years ago
tzzzzzzzx 5118bd9cbe fixed some bugs... on network functions
3 years ago
tzzzzzzzx cb713a61df Merge branch 'qq_branch' of https://bdgit.educoder.net/mvms9yluk/SafariChess into qq_branch
3 years ago
tzzzzzzzx e5419fe56d ar
3 years ago
DoubleQ b97c28f04b
3 years ago
DoubleQ 17234132a0
3 years ago
tzzzzzzzx 15b3394ae5 aaa0919
3 years ago
DoubleQ f3f2f60df3 9/15TZY
3 years ago
DoubleQ 1a4f07f70c 9.14下午进度
3 years ago
tzzzzzzzx 33c0989010 what?
3 years ago
tzzzzzzzx 0e05e4f0c5 Merge branch 'qq_branch'
3 years ago
tzzzzzzzx 5f14dfdd61 Merge branch 'master' of https://bdgit.educoder.net/mvms9yluk/SafariChess
3 years ago
tzzzzzzzx f46b14e867 add gitignore.
3 years ago
DoubleQ 655ef43aa5 网络版
3 years ago
DoubleQ 4c4e4afc3b 9.6下午5678节课
3 years ago
mvms9yluk 60c1a640d4 Merge pull request 'V2.0开始啦' (#2) from qq_branch into master
3 years ago
DoubleQ c6980f6ef7 增加了V2.0的需求
3 years ago
DoubleQ e66d379260 Merge branch 'master' of https://bdgit.educoder.net/mvms9yluk/SafariChess into qq_branch
3 years ago
DoubleQ f42c9b1621 加入了软件需求
3 years ago

BIN
.gitignore vendored

Binary file not shown.

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/SafariChess.iml" filepath="$PROJECT_DIR$/.idea/SafariChess.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -12,6 +12,24 @@
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "run single server",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/network/server.py",
"cwd": "${workspaceFolder}/network/",
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "current file",
"type": "python",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

@ -68,6 +68,10 @@ ex.大致图像
(a方在左 默认为红色b方在右 默认为蓝色)
#### 二、后端逻辑
行棋规则实现
@ -76,3 +80,56 @@ ex.大致图像
双方轮流移动:判断能否移动,移动结果显示(字幕提示)
#### 三、待完善的软件需求
子菜单:选择单机还是联网
左右留白用于各自功能实现:如计时,
顶部留白用于主控功能:认输,回主界面,音乐暂停等
### 网络版开发v2.0
#### 一、增加需求
> 基于游戏服务器,加入联网通信功能
>
> 主要工作:
>
> 1.实现socket通信
>
> 2.JSON数据解析以及封装——协议见“游戏服务器及通信协议.pdf”
>
> 3.多线程编程——建议把通信模块实现为一个单独的新线程,主程序和通信模块之间需要交互,超时判断(来自服务器,接受并处理)
>
> 4.游戏逻辑调整
一些约定:
1.红0蓝1 通信协议中的side=0红棋先手
2.棋子约定 chessman 0到6
3.服务器配置文件config.txt可改
客户端:
+ 申请加入对战 -> 先后手规则
+ 过程中可以叫停游戏或者主动退出游戏(弃权)
#### 二、开发流程
UI设计
-----增加相关按钮,增加提示,增加每一步的提示
(二)功能实现
-----与服务器数据交换

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

@ -0,0 +1,256 @@
'''
Notice that we are using 2 unique moving functions for:
fox_moves and eagle_moves
varies on the two genres
'''
import random
from math import inf
import collections
piece_score = {"7": 180, "1": 200, "6": 150, "5": 100, "4": 60, "3": 50, "2": 81}
mouse_score = [[13, 13, 13, 13, 12, 11, 10, 9, 8],
[25, 20, 15, 18, 16, 13, 10, 9, 8],
[50, 25, 20, 16, 15, 13, 10, 9, 8],
[1919810, 50, 20, 11, 9, 9, 9, 9, 0],
[50, 25, 20, 16, 14, 12, 8, 8, 8],
[25, 20, 15, 17, 15, 12, 8, 8, 8],
[11, 11, 10, 8, 8, 8, 8, 8, 8]]
# eagle_score = [[11, 12, 14, 13, 12, 11, 10, 8, 8],
# [15, 15, 14, 14, 0, 0, 0, 8, 8],
# [50, 20, 20, 0, 0, 0, 0, 8, 8],
# [100000, 50, 20, 13, 12, 11, 10, 8, 0],
# [50, 20, 20, 14, 0, 0, 0, 8, 8],
# [15, 12, 15, 14, 0, 0, 0, 8, 8],
# [11, 12, 14, 13, 12, 11, 10, 8, 8]]
eagle_score = [[14, 13, 10, 12, 14, 13, 10, 8, 7],
[15, 15, 14, 0, 0, 0, 9, 8, 6],
[50, 20, 20, 0, 0, 0, 7, 5, 4],
[1919810, 50, 20, 14, 13, 11, 10, 8, 0],
[50, 20, 20, 0, 0, 0, 7, 5, 4],
[15, 12, 15, 0, 0, 0, 9, 8, 6],
[14, 12, 10, 13, 14, 13, 10, 8, 7]]
fox_score = [[11, 12, 14, 13, 12, 11, 10, 8, 8],
[15, 15, 14, 0, 0, 0, 10, 14, 11],
[50, 20, 20, 0, 0, 0, 10, 8, 10],
[1919810, 50, 20, 13, 12, 11, 11, 10, 0],
[50, 20, 20, 0, 0, 0, 10, 8, 11],
[15, 12, 15, 0, 0, 0, 10, 14, 11],
[11, 12, 14, 13, 12, 11, 10, 8, 8]]
# wolf_score = [[11, 12, 14, 13, 12, 11, 10, 8, 8],
# [15, 15, 14, 0, 0, 0, 10, 8, 8],
# [50, 20, 20, 0, 0, 0, 10, 9, 10],
# [1919810, 50, 20, 13, 12, 11, 11, 10, 0],
# [50, 20, 20, 0, 0, 0, 10, 9, 10],
# [15, 12, 15, 0, 0, 0, 10, 8, 8],
# [11, 12, 14, 13, 12, 11, 10, 8, 8]]
wolf_score = [[11, 12, 14, 13, 12, 11, 10, 10, 8],
[15, 15, 14, 0, 0, 0, 10, 14, 10],
[50, 20, 20, 0, 0, 0, 10, 12, 10],
[1919810, 50, 20, 12, 11, 10, 11, 12, 0],
[50, 20, 20, 0, 0, 0, 10, 13, 10],
[15, 12, 15, 0, 0, 0, 13, 14, 8],
[11, 12, 14, 13, 12, 10, 11, 12, 8]]
leopard_score = [[11, 12, 14, 13, 12, 11, 10, 8, 8],
[15, 15, 14, 0, 0, 0, 10, 8, 8],
[50, 20, 20, 0, 0, 0, 10, 9, 10],
[1919810, 50, 20, 13, 12, 11, 11, 10, 0],
[50, 20, 20, 0, 0, 0, 10, 9, 10],
[15, 12, 15, 0, 0, 0, 10, 8, 8],
[11, 12, 14, 13, 12, 11, 10, 8, 8]]
lion_score = [[20, 20, 18, 15, 12, 11, 14, 12, 5],
[40, 25, 30, 0, 0, 0, 16, 12, 12],
[50, 40, 30, 0, 0, 0, 16, 12, 12],
[1919810, 50, 20, 15, 15, 15, 9, 12, 0],
[50, 40, 30, 0, 0, 0, 16, 12, 12],
[40, 25, 30, 0, 0, 0, 16, 12, 12],
[20, 20, 18, 15, 12, 11, 14, 12, 5]]
elephant_score = [[20, 20, 18, 15, 12, 11, 14, 12, 5],
[40, 25, 30, 0, 0, 0, 16, 12, 12],
[50, 40, 30, 0, 0, 0, 16, 12, 12],
[1919810, 50, 20, 15, 15, 15, 9, 12, 0],
[50, 40, 30, 0, 0, 0, 16, 12, 12],
[40, 25, 30, 0, 0, 0, 16, 12, 12],
[20, 20, 18, 15, 12, 11, 14, 12, 5]]
piece_position_scores = {"r1": mouse_score,
"b1": [line[::-1] for line in mouse_score[::-1]],
"r2": eagle_score,
"b2": [line[::-1] for line in eagle_score[::-1]],
"r3": fox_score,
"b3": [line[::-1] for line in fox_score[::-1]],
"r4": wolf_score,
"b4": [line[::-1] for line in wolf_score[::-1]],
"r5": leopard_score,
"b5": [line[::-1] for line in leopard_score[::-1]],
"r6": lion_score,
"b6": [line[::-1] for line in lion_score[::-1]],
"r7": elephant_score,
"b7": [line[::-1] for line in elephant_score[::-1]],
}
DEN_CONQUESTED = 10000
DRAW = 0
global DEPTH # =4
def findRandomMove(valid_moves):
return valid_moves[random.randint(0, len(valid_moves) - 1)]
# is greedy_move function used here?
def find_GreadyMove(game_state, valid_moves):
turnMultiplier = 1 if game_state.red_to_move else -1
maxScore = -DEN_CONQUESTED
bestMove = None
for playerMove in valid_moves:
game_state.makeMove(playerMove)
score = turnMultiplier * scoreMaterial(game_state)
if score > maxScore:
maxScore = score
bestMove = playerMove
game_state.undoMove()
return bestMove
def scoreMaterial(game_state): # get the score
# input: current game_state
score = 0
penalty_for_rep = 0
for row in range(len(game_state.board)):
for col in range(len(game_state.board[row])): # 遍历整个棋盘
piece = game_state.board[row][col]
if piece != "00":
piece_position_score = 0
piece_position_score = piece_position_scores[piece][row][col] # 获得当前位置的得分
if piece_position_scores[piece][row][col] in last_moves: # 重复判罚
penalty_for_rep += 70
if piece[0] == 'r':
score += piece_position_score + piece_score[piece[1]] - penalty_for_rep
elif piece[0] == 'b':
score -= piece_position_score + piece_score[piece[1]] - penalty_for_rep # 注意:这个默认没有考虑”淘汰“的可能性?
#待检查:
return score
def findMove_NegaMaxAlphaBeta(game_state, valid_moves, depth, DEPTH, alpha, beta, turn_multiplier):
# 对于各个参数的理解:
# game_state当前状态
# valid_moves可行的行动列表
# depth当前深度
# DEPTH限制深度
# alphaalpha限制值
# betabeta限制值
# turn_multiplierNegaMax特性
global next_move
if depth == 0:
return turn_multiplier * scoreMaterial(game_state)
max_score = -inf
for move in valid_moves:
game_state.makeMove(move)
next_moves = game_state.getAllMoves()
score = -findMove_NegaMaxAlphaBeta(game_state, next_moves, depth - 1, DEPTH, -beta, -alpha, -turn_multiplier)
if score > max_score: # > or >= ??
max_score = score
if depth == DEPTH:
next_move = move
game_state.undoMove()
if max_score > alpha:
alpha = max_score
if alpha >= beta:
break
return max_score
def findMove_MiniMaxAlphaBeta(game_state, valid_moves, depth, alpha, beta, turn_multiplier):
global next_move
def max_value(game_state, next_moves, alpha, beta, depth):
if depth == 0:
return turn_multiplier * scoreMaterial(game_state)
v = -inf
for move in valid_moves:
next_moves = game_state.getAllMoves()
v = max(v, min_value(game_state, next_moves, alpha, beta, depth - 1))
game_state.undoMove()
if v >= beta:
return v
alpha = max(alpha, v)
return v
def min_value(game_state, next_moves, alpha, beta, depth):
if depth == 0:
return turn_multiplier * scoreMaterial(game_state)
v = -inf
for move in valid_moves:
next_moves = game_state.getAllMoves()
v = min(v, max_value(game_state, next_moves, alpha, beta, depth - 1))
game_state.undoMove()
if v <= alpha:
return v
beta = min(beta, v)
return v
# Body of alpha_beta_search:
best_score = -inf
beta = inf
best_action = None
for move in valid_moves:
v = min_value(game_state, valid_moves, best_score, beta, depth)
if v > best_score:
best_score = v
best_action = move
return best_action
def find_BestMove(game_state, valid_moves, depth_p):
global next_move
DEPTH = depth_p
next_move = None
global last_moves
last_moves = collections.deque(maxlen=12)
random.shuffle(valid_moves)
ordered_valid_moves = orderby_GreadyMove(game_state, valid_moves)
# for i in valid_moves:
# print(f"Possible: {i}")
# for i in ordered_valid_moves:
# print(f"New possible: {i}")
findMove_NegaMaxAlphaBeta(game_state, ordered_valid_moves, depth_p, DEPTH, -DEN_CONQUESTED, DEN_CONQUESTED,
1 if game_state.red_to_move else -1)
last_moves.append(next_move)
return next_move
def orderby_GreadyMove(game_state, valid_moves):
turnMultiplier = 1 if game_state.red_to_move else -1
maxScore = -DEN_CONQUESTED
de = collections.deque([])
for playerMove in valid_moves:
game_state.makeMove(playerMove)
score = turnMultiplier * scoreMaterial(game_state)
if score > maxScore:
maxScore = score
de.appendleft(playerMove)
else:
de.append(playerMove)
game_state.undoMove()
return de

@ -2,21 +2,68 @@
SafariChess_Classes.py
Classes for button and keyboard events
"""
import pygame as pg
class Button:
def _init_(self, image, position, callback=None):
def __init__(self, image, position, callback=None):
self.image = image
self.rect = image.get_rect(topleft=position)
self.callback = callback
def on_click(self, mouse_pos):
if self.rect.collidepoint(mouse_pos):
if self.callback:
self.callback()
return True
return False
def on_click(self, event):
if event.button == 1:
if self.rect.collidepoint(event.pos):
self.callback(self)
#class DropDown():
#下拉选择框
#下拉框
class DropDown():
def __init__(self, color_menu, color_option, x, y, w, h, font, main, options):
self.color_menu = color_menu
self.color_option = color_option
self.rect = pg.Rect(x, y, w, h)
self.font = font
self.main = main
self.options = options
self.draw_menu = False
self.menu_active = False
self.active_option = -1
def draw(self, surf):
pg.draw.rect(surf, self.color_menu[self.menu_active], self.rect, 0)
msg = self.font.render(self.main, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = self.rect.center))
if self.draw_menu:
for i, text in enumerate(self.options):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
pg.draw.rect(surf, self.color_option[1 if i == self.active_option else 0], rect, 0)
msg = self.font.render(text, 1, (0, 0, 0))
surf.blit(msg, msg.get_rect(center = rect.center))
def update(self, event_list):
mpos = pg.mouse.get_pos()
self.menu_active = self.rect.collidepoint(mpos)
self.active_option = -1
for i in range(len(self.options)):
rect = self.rect.copy()
rect.y += (i+1) * self.rect.height
if rect.collidepoint(mpos):
self.active_option = i
break
if not self.menu_active and self.active_option == -1:
self.draw_menu = False
for event in event_list:
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
if self.menu_active:
self.draw_menu = not self.draw_menu
elif self.draw_menu and self.active_option >= 0:
self.draw_menu = False
return self.active_option
return -1

@ -10,21 +10,26 @@ it handle user input and GUI
"""
import colorsys
from json import load
import json
from multiprocessing import connection
import pygame as pg
import sys
from time import process_time
import socket
import SafariChess_backend as backend
import SafariChess_Classes as classes
import SafariChess_AI as AI
from SafariChess_Classes import Button, DropDown
from threading import Thread, Lock
from itertools import count, cycle
import time
import random
#设置棋盘的参数
WIDTH = 800
HEIGHT = 1000
BOARD_WIDTH = 600
BOARD_HEIGHT = 800
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 800
WIDTH = 1000
HEIGHT = 800
BOARD_WIDTH = 800
BOARD_HEIGHT = 600
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
MOVE_LOG_PANEL_WIDTH = 0
MOVE_LOG_PANEL_HEIGHT = BOARD_HEIGHT
DIMENSION = 8
@ -32,8 +37,46 @@ DIMENSION_ROWS = 7
DIMENSION_COLUMNS = 9
SIZE = 64
IMAGES = {}
bias_top = 200 #棋盘的上边距
bias_left = 10 #棋盘的左边距
bias_top = 100 #棋盘的上边距
bias_left = 20 #棋盘的左边距
#网络道具
general_countdown = 40
client = None
networkMsg = None
server = None
port = None
addr = None
connection = None
DEPTH = 3
#网络版更新需要注意多线程的问题?
server_died_message = 'fucking server died and quit'
#尝试写一个函数分配一个新线程供监听
def startNewThread(target):
thread = Thread(target=target)
thread.daemon =True
thread.start()
def listenFromServer():
global networkMsg
try:
while True:
recvMsg = client.recv(1024).decode('utf-8')
if len(recvMsg) != 0:
networkMsg = json.loads(recvMsg)
#print('receive thread catch: ',networkMsg)
except socket.error as e:
#print('Error encountered in connection. Quit.')
print(e)
return False
def startNetworkServices():
global client,server,port,addr,connection
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server = '192.168.43.115'
port = 50005
addr = (server,port)
connection = client.connect(addr)
startNewThread(target=listenFromServer)
#等待敌人移动
def loadImages():#加载图片,a,b双方分别有象狮豹狼狐鹰鼠七个角色
pieces = ["r7", "r6", "r5", "r4", "r3", "r2", "r1",
@ -43,7 +86,7 @@ def loadImages():#加载图片,a,b双方分别有象狮豹狼狐鹰鼠七个角
IMAGES[piece] = pg.transform.scale(pg.image.load("./Image/pieces/pieces_rb/" + piece + ".png"), (SIZE - 10, SIZE - 10))
tools = ["trap", "home1","home2"]
for tool in tools:
for tool in tools:
IMAGES[tool] = pg.transform.scale(pg.image.load("./Image/tools/" + tool + ".jpg"), (SIZE-10, SIZE-10))
#screen = pg.display.set_mode((BOARD_WIDTH + MOVE_LOG_PANEL_WIDTH, BOARD_HEIGHT))#设置窗口大小
#screen.fill(pg.Color("white"))
@ -54,12 +97,12 @@ def drawBoard(screen):#绘制棋盘:格子,河流,陷阱,巢穴
#colors = [(255, 255, 255), (0, 0, 0)]
for row in range(DIMENSION_ROWS):
for column in range(DIMENSION_COLUMNS):
pg.draw.rect(screen, pg.Color(10,100,10), pg.Rect(bias_left+column * SIZE, bias_top+row * SIZE, SIZE, SIZE))
pg.draw.rect(screen, pg.Color(199,237,204), pg.Rect(bias_left+column * SIZE, bias_top+row * SIZE, SIZE, SIZE))
pg.draw.rect(screen, pg.Color(90,150,75), pg.Rect((column * SIZE) + bias_left+3, (row * SIZE) + 3+bias_top, SIZE - 4, SIZE - 4))
#画河流
for row in [1,2,4,5]:
for column in [3,4,5]:
pg.draw.rect(screen, pg.Color(5,5,100),pg.Rect((column * SIZE) +bias_left+3, (row * SIZE) +bias_top +3, SIZE,SIZE))
pg.draw.rect(screen, pg.Color(67,142,219),pg.Rect((column * SIZE) +bias_left+2, (row * SIZE) +bias_top +3, SIZE-2,SIZE-2))
#画陷阱
for row in [2,4]:
@ -81,6 +124,7 @@ def drawPieces(screen,board):
screen.blit(IMAGES[piece], pg.Rect((column * SIZE) + bias_left + 5, (row * SIZE) + bias_top + 5, SIZE - 10, SIZE - 10))
# square_selected 是当前选中的可以移动的棋子的位置
def protrudeSquares(screen,game_state,square_selected,valid_moves):
#高亮选中的棋子接下来可行的位置
@ -99,7 +143,7 @@ def protrudeSquares(screen,game_state,square_selected,valid_moves):
if move.start_row == row and move.start_col == column:
screen.blit(s, (move.end_col * SIZE + bias_left, move.end_row * SIZE + bias_top))
def ShowGameState(screen,game_state,valid_moves,square_selected):#游戏状态
drawBoard(screen)
@ -112,8 +156,8 @@ def drawMovelog(screen):#绘制移动日志
pass
def startGamePage():#开始游戏界面Human/AI
startpage = '''
def startGamePage(mode = 1):#开始游戏界面Human/AI
startpage = ('''
WELCOME TO
@ -129,13 +173,13 @@ def startGamePage():#开始游戏界面Human/AI
################################################################
Please type one of the numbers below to choose a playing mode:
( 1 ) - Human vs Human
( 2 ) - Human vs AI
( 3 ) - AI vs AI
( 4 ) - Quit
( 1 ) - 单机游戏
( 2 ) - 网络对战
( 3 ) - 人机对战
( 4 ) - AI代打
################################################################
'''
Current Mode is: %s
'''%['单机游戏','网络对战','人机对战','AI代打'][mode-1])
print(startpage)
# 选择游戏模式
not_selected = True
@ -143,7 +187,7 @@ Please type one of the numbers below to choose a playing mode:
while not_selected:
try:
#mode = int(input())
mode = 1
#mode = 1
if mode in [1,2,3,4]:
if mode == 1:
p1 = 1
@ -152,10 +196,10 @@ Please type one of the numbers below to choose a playing mode:
not_selected = False
else:
print("Please type one of the numbers below to choose a playing mode:")
print(" ( 1 ) - Human vs Human")
print(" ( 2 ) - Human vs AI")
print(" ( 3 ) - AI vs AI")
print(" ( 4 ) - Quit")
print(" ( 1 ) - 单机游戏")
print(" ( 2 ) - 网络对战")
print(" ( 3 ) - 人机对战")
print(" ( 4 ) - 退出游戏")
except ValueError:
print("Wrong Input")
return p1, p2, mode
@ -164,93 +208,576 @@ Please type one of the numbers below to choose a playing mode:
def showGameOverText(screen, text):
#游戏结束,出现的文字 text是字符串“Left Win”或“Right Win”
font = pg.font.SysFont("comicsansms", 32)
text_object = font.render(text, True, pg.Color("red"))
text_object = font.render(text, True, pg.Color("grey"))
text_location = pg.Rect(0, 0, BOARD_WIDTH, BOARD_HEIGHT).move(BOARD_WIDTH / 2 - text_object.get_width() / 2,
BOARD_HEIGHT / 2 - text_object.get_height() / 2)
screen.blit(text_object, text_location)
text_object = font.render(text, False, pg.Color('black'))
screen.blit(text_object, text_location.move(5, 5))
text_object = font.render(text, False, pg.Color('red'))
screen.blit(text_object, text_location.move(-2 , -2))
pg.display.flip()
def MainMenu():
#显示主菜单
pg.init()
mainClock = pg.time.Clock()
#ui
screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.display.set_caption("SafariChess by ZY&&DQ")
bg = pg.image.load("images/MenuBg.jpg")
bg = pg.transform.scale(pg.image.load("Image/MenuBg.bmp"), (SCREEN_WIDTH, SCREEN_HEIGHT))
bt1 = pg.transform.scale(pg.image.load("Image/button1.jpg"),(150,40))
bt2 = pg.transform.scale(pg.image.load("Image/button2.jpg"),(150,40))
bt3 = pg.transform.scale(pg.image.load("Image/button3.jpg"),(150,40))
bt4 = pg.transform.scale(pg.image.load("Image/button4.jpg"),(150,40))
music_settings = pg.transform.scale(pg.image.load("Image/music.bmp"),(100,40))
#按钮设置
return 1
#主菜单包含如下按钮:
#单机游戏,双人游戏,人机对战,退出游戏
button_1 = Button(bt1,(300,350))
button_2 = Button(bt2,(300,400))
button_3 = Button(bt3,(300,450))
button_4 = Button(bt4,(300,500))
music_button = Button(music_settings,(700,0))
#音乐设置
file=r'./Image/music1.mp3' #要播放的歌曲本地地址
pg.mixer.init() #mixer的初始化
music = pg.mixer.music.load(file) #载入一个音乐文件用于播放
pg.mixer.music.play(-1) #播放音乐,-1表示循环播放
pg.mixer.music.set_volume(0)
COLOR_INACTIVE = (255, 108, 0)
COLOR_ACTIVE = (255, 208, 0)
COLOR_LIST_INACTIVE = (0, 143, 90)
COLOR_LIST_ACTIVE = (3, 252, 140)
def main():
# music_ops = DropDown([COLOR_INACTIVE, COLOR_ACTIVE],[COLOR_LIST_INACTIVE, COLOR_LIST_ACTIVE],
# 600,30, 100, 20,
# pg.font.SysFont("Courier", 20),
# "Music",
# music_lists)
mode = -1
run = True
while run:
mainClock.tick(60)
mode = 0
events = pg.event.get()
for event in events:
if event.type == pg.QUIT:
pg.quit()
sys.exit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
pg.quit()
sys.exit()
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1: #左键
if button_1.rect.collidepoint(event.pos):
mode = 1
run = False
if button_2.rect.collidepoint(event.pos):
activate_AI = input("activate_AI? y/[n]: ")
mode = 2
if activate_AI == 'y':
mode = 4
run = False
if button_3.rect.collidepoint(event.pos):
mode = 3
run = False
if button_4.rect.collidepoint(event.pos):
pg.quit()
sys.exit()
if music_button.rect.collidepoint(event.pos):
if pg.mixer.music.get_volume() == 0:
pg.mixer.music.set_volume(0.5)
else:
pg.mixer.music.set_volume(0)
pg.display.flip()
screen.blit(bg, (0, 0))
screen.blit(button_1.image, button_1.rect)
screen.blit(button_2.image, button_2.rect)
screen.blit(button_3.image, button_3.rect)
screen.blit(button_4.image, button_4.rect)
screen.blit(music_button.image, music_button.rect)
pg.display.flip()
return mode
#绘制Text在屏幕上
def showText(screen,fontObj,text,x,y):
textSurfaceObj = fontObj.render(text, True, pg.Color('red'),pg.Color('white'))# 配置要显示的文字
textRectObj = textSurfaceObj.get_rect()# 获得要显示的对象的rect
textRectObj.center = (x, y)# 设置显示对象的坐标
screen.blit(textSurfaceObj, textRectObj)# 绘制字体
def drawButton22(screen):
##一些按钮的实例化
button_stop = Button(pg.transform.scale(pg.image.load("Image/叫停.bmp"),(100,50)),(400,0))
screen.blit(button_stop.image, button_stop.rect)
button_quit = Button(pg.transform.scale(pg.image.load("Image/退出.bmp"),(100,50)),(300,0))
screen.blit(button_quit.image, button_quit.rect)
button_fail = Button(pg.transform.scale(pg.image.load("Image/认输.bmp"),(100,50)),(500,0))
screen.blit(button_fail.image, button_fail.rect)
button_restart = Button(pg.transform.scale(pg.image.load("Image/restart.bmp"),(100,50)),(100,0))
screen.blit(button_restart.image, button_restart.rect)
def main(mode):
global networkMsg
networkMsg = None
pg.init()
screen = pg.display.set_mode((BOARD_WIDTH + MOVE_LOG_PANEL_WIDTH, BOARD_HEIGHT))
pg.display.set_caption("Safafi Chess Game")
pg.display.set_caption("Safari Chess Game")
clock = pg.time.Clock()
screen.fill(pg.Color("white"))
loadImages()
drawBoard(screen)
isOnline = bool(mode == 2 or mode == 4)
game_state=backend.GameState()
#* cancelled args: MySide=True if not isOnline else bool(NewPlayerMessage['side']),game_id=NewPlayerMessage['game_id']
valid_moves=game_state.getAllMoves()
running = True
other_joined = False #network element
other_stage = False #network element
game_id = None #network element
MySide = None #network element
time_out_side = None #network element
foul = None #network element
mademove = False
game_over = False
square_selected = ()#刚开始没有选择任何一个棋子
click_queue = []#点击队列,记录第一次点击和第二次点击位置,方便移动棋子
square_selected = () #刚开始没有选择任何一个棋子
click_queue = [] #点击队列,记录第一次点击和第二次点击位置,方便移动棋子
##音乐按钮,均有
button_music = Button(pg.transform.scale(pg.image.load("Image/music.bmp"),(80,40)),(650,0))
screen.blit(button_music.image, button_music.rect)
pg.display.update()
startGamePage()
while running:
for e in pg.event.get():
#接下来处理游戏中的事件
if e.type == pg.QUIT:
pg.quit()
sys.exit()
elif e.type == pg.MOUSEBUTTONDOWN:
#鼠标点击事件:用于选择棋子,移动棋子
if not game_over:
mouse_loc = pg.mouse.get_pos()
row = int((mouse_loc[1] - bias_top) / SIZE)
col = int((mouse_loc[0] - bias_left) / SIZE) #* get position of mouse click
if square_selected == (row,col) or col >=DIMENSION_COLUMNS or row >= DIMENSION_ROWS:
square_selected = ()
click_queue = []
else:
square_selected = (row,col)
click_queue.append(square_selected)
if(len(click_queue) == 2):
cur_piece_loc = click_queue[0]
nxt_piece_loc = click_queue[1]
move = backend.Move(cur_piece_loc,nxt_piece_loc,game_state.board)
if move in valid_moves:
game_state.makeMove(move)
mademove = True
startGamePage(mode) #游戏进入前,可以进一步优化一些显示细节
if mode == 1 or mode == 3:
if mode == 3:
AI_side = random.randint(0,1)
print('AI side:{}'.format('b' if AI_side == 1 else 'r'))
while running:
for e in pg.event.get():
#接下来处理游戏中的事件
if e.type == pg.QUIT:
pg.quit()
sys.exit()
elif e.type == pg.MOUSEBUTTONDOWN:
#鼠标点击事件:用于选择棋子,移动棋子
if not game_over:
mouse_loc = pg.mouse.get_pos()
row = int((mouse_loc[1] - bias_top) / SIZE)
col = int((mouse_loc[0] - bias_left) / SIZE) #* get position of mouse click
if square_selected == (row,col) or col >=DIMENSION_COLUMNS or row >= DIMENSION_ROWS:
square_selected = ()
click_queue = []
else:
click_queue = [square_selected]
square_selected = (row,col)
click_queue.append(square_selected)
if(len(click_queue) == 2):
cur_piece_loc = click_queue[0]
nxt_piece_loc = click_queue[1]
move = backend.Move(cur_piece_loc,nxt_piece_loc,game_state.board)
if move in valid_moves:
game_state.makeMove(move)
mademove = True
square_selected = ()
click_queue = []
valid_moves = game_state.getAllMoves()
else:
click_queue = [square_selected]
elif e.type == pg.KEYDOWN:
#设置某些按键用于悔棋,重新开始游戏,机器提示,退出游戏等功能
if e.key == pg.K_z: # undo when 'z' is pressed
game_state.undoMove()
mademove = True
game_over = False
if mademove:
valid_moves = game_state.getAllMoves()
mademove = False
if not game_over and mode == 3:
if AI_side == 1 and not game_state.red_to_move:
ai_move = AI.find_BestMove(game_state,valid_moves,DEPTH)
if ai_move is not None:
game_state.makeMove(ai_move)
mademove = True
else:
ai_move = AI.find_GreadyMove(game_state,valid_moves)
game_state.makeMove(ai_move)
mademove = True
elif AI_side == 0 and game_state.red_to_move:
ai_move = AI.find_BestMove(game_state,valid_moves,DEPTH)
if ai_move is not None:
game_state.makeMove(ai_move)
mademove = True
else:
ai_move = AI.find_GreadyMove(game_state,valid_moves)
game_state.makeMove(ai_move)
mademove = True
ShowGameState(screen,game_state,valid_moves,square_selected)
if game_state.conquer():
showGameOverText(screen,"player "+game_state.win_person+" wins")
game_over = True
clock.tick(60)
pg.display.flip()
elif mode == 2 or mode == 4:
counts=general_countdown #双方倒计时
startNetworkServices()
#! start login
myPlayerData = {
'type': 0, # game state type ?
'msg': {
'name': 'myName'
}
}
login_packet = json.dumps(myPlayerData)
if (sys.version[:1] == '3'):
login_packet = login_packet.encode('utf-8')
lastNetworkMsg = networkMsg
client.send(login_packet)
login_font = pg.font.SysFont("comicsansms", 32)
text_object = login_font.render('waiting 4 connection...', True, pg.Color("grey"))
login_text_location = pg.Rect(0, 0, BOARD_WIDTH, BOARD_HEIGHT).move(BOARD_WIDTH / 2 - text_object.get_width() / 2,BOARD_HEIGHT / 2 - text_object.get_height() / 2)
while networkMsg == None:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
screen.blit(text_object,login_text_location)
clock.tick(20)
pg.display.update()
elif e.type == pg.KEYDOWN:
#设置某些按键用于悔棋,重新开始游戏,机器提示,退出游戏等功能
pass
if mademove:
valid_moves = game_state.getAllMoves()
ShowGameState(screen,game_state,valid_moves,square_selected)
if game_state.conquer():
showGameOverText(screen,"player "+game_state.win_person+" wins")
game_over = True
clock.tick(60)
pg.display.flip()
#! end login
# 自定义计时事件-----计时器实现
COUNTS = pg.USEREVENT +1
# 每隔1秒发送一次自定义事件
pg.time.set_timer(COUNTS,1000) #以毫秒为单位
bigfont = pg.font.SysFont("Consolas", 20)
minfont = pg.font.SysFont("Courier", 18)
showText(screen,bigfont,"RemainTime:",680,200) #650,250为时间650350为对方轮数
showText(screen,minfont,"Turn:",650,300)
clock = pg.time.Clock()
clock.tick(60)
#drawButton22(screen)
button_stop = Button(pg.transform.scale(pg.image.load("Image/叫停.bmp"), (100, 50)), (400, 0))
screen.blit(button_stop.image, button_stop.rect)
button_quit = Button(pg.transform.scale(pg.image.load("Image/退出.bmp"), (100, 50)), (300, 0))
screen.blit(button_quit.image, button_quit.rect)
button_fail = Button(pg.transform.scale(pg.image.load("Image/认输.bmp"), (100, 50)), (500, 0))
screen.blit(button_fail.image, button_fail.rect)
button_restart = Button(pg.transform.scale(pg.image.load("Image/restart.bmp"), (100, 50)), (100, 0))
screen.blit(button_restart.image, button_restart.rect)
while running:
#! 特别注意红方是0蓝方是1
#print('current moving color: ',game_state.color())
if lastNetworkMsg != networkMsg:#handle
#! 无法解决的问题:判断延迟导致无法同时接收来自服务器的两条转发消息。
#! 尝试让输方发送消息解决上述问题。
print('handling new msg: ',networkMsg)
lastNetworkMsg = networkMsg #networkMsg中保存当前字典
if 'status' in networkMsg.keys():
#Login_handler
if networkMsg['status'] == 1: #Finished Login process
if other_joined == False:
other_joined = True
print('Game start 2 play!')
game_id = networkMsg['game_id']
MySide = networkMsg['side']
print('my side:{}'.format('b' if MySide==1 else 'r'))
other_stage = True if networkMsg['side']==1 else False #一开始如果我是蓝方则other_stage为真
if(other_stage == True):
print('waiting for other player to move...')
#state_game_handler
elif networkMsg['status'] == 2 : # Stopping request
print('other player returned special message: ',networkMsg['request'])
if networkMsg['request']=='quit':
other_joined = False
elif networkMsg['request'] == 'stop':
game_state.win_person = 'b' if networkMsg['side'] == 1 else 'r'
elif networkMsg['request'] == 'report':
foul = MySide
otherQuitJson = {
'type': 3,
'side': MySide
}
client.send(json.dumps(otherQuitJson).encode('utf-8'))
game_over = True
#timeout handler
elif networkMsg['status'] == 3 :
time_out_side = networkMsg['side']
otherQuitJson = {
'type': 3,
'side': MySide
}
client.send(json.dumps(otherQuitJson).encode('utf-8'))
game_over = True
#Move handler
elif 'src' and 'dst' in networkMsg.keys():# 接受对手行棋
theMove = backend.Move([networkMsg['src']['y'],networkMsg['src']['x']],[networkMsg['dst']['y'],networkMsg['dst']['x']],game_state.board)
#print(theMove)
violate_175 = game_state.seventeen_five_violation(goal=theMove.get_goal(),color=theMove.get_turn(), showTrace= True)
violate_73 = game_state.seventy_three_violation(goal=theMove.get_goal(), color=theMove.get_turn(), showTrace= True)
if not type(violate_175) == bool:
print("player {} foul 17-5".format(theMove.get_turn()))
print("Recent Moves: {}".format(violate_175))
reportJson = {
"type": 2,
"msg": {
"request": "report",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(reportJson).encode('utf-8'))
foul = theMove.get_turn()
if not type(violate_73) == bool:
print("player {} foul 7-3".format(theMove.get_turn()))
print("Recent Moves: {}".format(violate_73))
reportJson = {
"type": 2,
"msg": {
"request": "report",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(reportJson).encode('utf-8'))
foul = theMove.get_turn()
game_state.makeMove(theMove)
valid_moves = game_state.getAllMoves()
other_stage = not other_stage
counts=general_countdown
thisMove = None
for e in pg.event.get():
#接下来处理游戏中的事件
if e.type == pg.QUIT:
quitJson = {
"type": 2,
"msg": {
"request": "quit",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(quitJson).encode('utf-8'))
pg.quit()
sys.exit()
elif e.type == pg.MOUSEBUTTONDOWN:
#鼠标点击事件:用于选择棋子,移动棋子
if game_over and other_joined:
if e.button == 1:
if button_quit.rect.collidepoint(e.pos):
quitJson = {
"type": 2,
"msg": {
"request": "quit",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(quitJson).encode('utf-8'))
pg.quit()
sys.exit()
if not game_over and not other_stage and other_joined:
if e.button == 1:
if button_quit.rect.collidepoint(e.pos):
quitJson = {
"type": 2,
"msg": {
"request": "quit",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(quitJson).encode('utf-8'))
pg.quit()
sys.exit()
if button_stop.rect.collidepoint(e.pos):
winJson = {
'type': 2,
'msg': {
'request': 'stop',
'game_id': game_id,
'side': MySide
}
}
client.send(json.dumps(winJson).encode("utf-8"))
if button_fail.rect.collidepoint(e.pos):
quitJson = {
"type": 2,
"msg": {
"request": "quit",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(quitJson).encode('utf-8'))
if button_restart.rect.collidepoint(e.pos):
reportJson = {
"type": 2,
"msg": {
"request": "report",
"game_id": game_id,
"side": MySide
}
}
client.send(json.dumps(reportJson).encode('utf-8'))
mouse_loc = pg.mouse.get_pos()
row = int((mouse_loc[1] - bias_top) / SIZE)
col = int((mouse_loc[0] - bias_left) / SIZE) #* get position of mouse click
if square_selected == (row,col) or col >=DIMENSION_COLUMNS or row >= DIMENSION_ROWS:
square_selected = ()
click_queue = []
else:
square_selected = (row,col)
click_queue.append(square_selected)
if(len(click_queue) == 2):
cur_piece_loc = click_queue[0]
nxt_piece_loc = click_queue[1]
move = backend.Move(cur_piece_loc,nxt_piece_loc,game_state.board)
thisMove = move
if move in valid_moves:
game_state.makeMove(move)
mademove = True
square_selected = ()
click_queue = []
else:
click_queue = [square_selected]
elif e.type == COUNTS:# 判断事件是否为计时事件
if not other_stage and counts > 0 and not game_over: counts -= 1
text = str(counts).rjust(3)
showText(screen,bigfont,text,650,250)
pg.display.update()
if not game_over and mode == 4:
#! 特别注意红方是0蓝方是1
if MySide == 1 and not game_state.red_to_move:
thisMove = AI.find_BestMove(game_state,valid_moves,DEPTH)
if thisMove is not None:
game_state.makeMove(thisMove)
mademove = True
else:
thisMove = AI.find_GreadyMove(game_state,valid_moves)
game_state.makeMove(thisMove)
mademove = True
elif MySide == 0 and game_state.red_to_move:
thisMove = AI.find_BestMove(game_state,valid_moves,DEPTH)
if thisMove is not None:
game_state.makeMove(thisMove)
mademove = True
else:
thisMove = AI.find_GreadyMove(game_state,valid_moves)
game_state.makeMove(thisMove)
mademove = True
square_selected = ()
click_queue = []
if mademove:
#print(thisMove)
valid_moves = game_state.getAllMoves()
mademove = False
if isOnline:
print('waiting for the other player to move...')
other_stage = not other_stage
thisMoveJson = {
'type': 1,
'msg': {
"game_id": game_id,
"side": MySide,
"chessman": int(thisMove.cur_piece[1:])-1,
"src": {
"y": thisMove.start_row,
"x": thisMove.start_col
},
"dst": {
"y": thisMove.end_row,
"x": thisMove.end_col
}
}
}
client.send(json.dumps(thisMoveJson).encode('utf-8'))
#思路变成:定时对敌方进行扫描,若收到更新相关包则进行局面更新。
ShowGameState(screen,game_state,valid_moves,square_selected)
if game_state.conquer() and not game_over:
print("GAME OVER!")
#
if game_state.win_person == ('r' if MySide == 1 else 'b'): #测试:我输了再发
winJson = {
'type': 2,
'msg': {
'request': 'stop',
'game_id': game_id,
'side': MySide
}
}
client.send(json.dumps(winJson).encode("utf-8"))
print('i lost, and game over message sent.')
game_over = True
#print('game over label confirmed.')
if time_out_side != None:
showGameOverText(screen,"player "+ ("b" if time_out_side==1 else "r")+" timed out") #超时了
elif foul is not None:
showGameOverText(screen,"foul play")
game_over = True
elif game_over and other_joined:
if (game_state.win_person == ''): game_state.conquer()
showGameOverText(screen,"player "+game_state.win_person+" wins")
elif game_over and not other_joined:
showGameOverText(screen,"player "+ ("b" if MySide==0 else "r") + " quitted the game") #对手退出了
showText(screen,bigfont,game_state.color(),700,350)
clock.tick(240)
pg.display.flip()
if __name__ == '__main__':
print("Loading...")
print("Initialing game...")
main()
#print("Loading...")
#print("Initialing game...")
mode = MainMenu()
# mode = 4
main(mode)

@ -1,11 +1,56 @@
class GameState:
def __init__(self):
import socket
import this
import requests
import sys
import json
from threading import Thread
from queue import Queue
#multithreading?
class Move:
def __init__(self,start_loc,end_loc,board):
self.start_row = start_loc[0]
self.start_col = start_loc[1]
self.end_row = end_loc[0]
self.end_col = end_loc[1]
self.nxt_piece = board[self.end_row][self.end_col]
self.cur_piece = board[self.start_row][self.start_col]
self.attack = self.nxt_piece != '00'
self.moveID = self.start_row + self.start_col*10 + self.end_row *100 +self.end_col * 1000 #Hash
def __eq__(self, __o: object) -> bool:
if isinstance(__o, Move):
return self.moveID == __o.moveID
return False
def __str__(self):
return f"{self.start_row=}, "+f"{self.start_col=}, "+f"{self.end_row=}, "+f"{self.end_col=}"
def get_goal(self):
return (self.end_row, self.end_col)
def get_start(self):
return (self.start_row, self.start_col)
def get_goal_chess_comb(self):
return (self.end_row, self.end_col, self.cur_piece)
def get_turn(self):
return self.cur_piece[0]
def get_piece(self):
return self.cur_piece
def startNewThread(target):
thread = Thread(target=target)
thread.daemon = True
thread.start()
class GameState:
def __init__(self):
'''
有关信息
1. 棋盘尺寸为7*9横向
2. 按照以下命名格式对棋盘中的棋子进行标注
(b|r)(1|2|3|4|5|6|7)
3.
3. 左方为蓝方对应的网络服务器中的side项目编号为0
右方为红方对应的网络服务器中的side项目编号为1
4. 网络版需要额外的类变量type, game_id, side, chessman(piece), src, dst
'''
self.board = [
['00','00','b7','00','00','00','r1','00','00'],
@ -32,16 +77,101 @@ class GameState:
self.red_home=(3,8)
self.blue_pieces=[7,6,5,4,3,2,1]
self.red_pieces=[7,6,5,4,3,2,1]
#蓝方(左)先行
self.red_to_move=False
#红方(右)先行
self.red_to_move=True
self.conquered=False
self.win_person=''
self.MASSACRE=False
self.isStarted = False
self.move_log = []
def color(self):
return 'r' if self.red_to_move else 'b'
def exchange(self):
self.red_to_move = not self.red_to_move
def seventy_three_violation(self, goal, color, showTrace = False):
#输入goal为三元组(格坐标x格坐标y棋子)
# color为颜色
# showTrace为是否查看轨迹
past7Moves = []
#寻找该动物的移动轨迹
for i in range(len(self.move_log)-1, -1, -1):
if self.move_log[i].get_turn() == color:
past7Moves.append(self.move_log[i])
if len(past7Moves) > 7:
break
# print("past7Moves= ",[move.get_goal_chess_comb() for move in past7Moves])
if len(past7Moves) > 7:
past7Moves = past7Moves[-7:]
visit_counts = dict()
for move in past7Moves:
if move.get_goal() in (self.blue_trap_loc + self.red_trap_loc):
return True
try:
visit_counts[move.get_goal_chess_comb()] += 1
except KeyError:
visit_counts[move.get_goal_chess_comb()] = 1
try:
if visit_counts[goal] >= 3:
if showTrace:
return (goal, [move.get_goal() for move in past7Moves])
else:
return False
else:
return True
except:
return True
def seventeen_five_violation(self, goal, color, showTrace = False):
# 输入goal为三元组(格坐标x格坐标y棋子)
# color为颜色
# showTrace为是否查看轨迹
#获取近17步操作
RECENT_COUNT = 17
PERMIT_RANGE = 5
# print("using params: {}, {}, {}".format(goal, color, showTrace))
past17Moves = []
for i in range(len(self.move_log) - 1, -1, -1):
if self.move_log[i].get_turn() == color:
past17Moves.append(self.move_log[i])
if len(past17Moves) > RECENT_COUNT:
break
if len(past17Moves) >= RECENT_COUNT:
past17Moves = past17Moves[-RECENT_COUNT:]
else:
return True
#print("past17Movesloc: {}".format([move.get_goal() for move in past17Moves]))
#判断是否为同一棋子的移动
piece = past17Moves[0].get_piece()
for i in range(len(past17Moves)):
if past17Moves[i].get_piece() != piece:
return True
#当为同一棋子时……
visit_locs = []
for move in past17Moves:
if move.get_goal() in (self.blue_trap_loc + self.red_trap_loc):
return True
this_loc = move.get_goal()
if this_loc not in visit_locs:
visit_locs.append(this_loc)
try:
if showTrace and len(visit_locs) <= PERMIT_RANGE:
if goal in visit_locs:
return (goal,visit_locs)
else:
return True
elif len(visit_locs)<=PERMIT_RANGE:
if goal in visit_locs:
return False
else:
return True
else:
return True
except:
return True
# 判断特殊位置
def inHome(self,row,col,color):
if color=="b":
@ -85,8 +215,15 @@ class GameState:
for col in range(len(self.board[row])):
player = self.board[row][col][0]
if (player == 'r' and self.red_to_move or player == 'b' and not self.red_to_move):
#print('what color is the valid move? ',player)
self.moveFunctions[self.board[row][col][1]](row,col,moves)
return moves
valid_moves = []
for move in moves: # 己方躲避17-5和7-3
if self.seventy_three_violation(goal=move.get_goal_chess_comb(), color=self.color()) and self.seventeen_five_violation(goal=move.get_goal_chess_comb(), color=self.color()):
valid_moves.append(move)
if len(valid_moves) == 0:
print('warning: no available moves at side {}'.format('b' if not self.red_to_move else 'r'))
return valid_moves
def getStdMoves(self,row,col,moves):#输入当前的位置将可行路径输出给moves
@ -127,37 +264,37 @@ class GameState:
#U&D direction
while new_row >= 0:
nxt_piece = self.board[new_row][col]
if nxt_piece == '00' and not self.inTrap(new_row,col,enemy_color):
if nxt_piece == '00' and not self.inTrap(new_row,col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
if not self.inHome(new_row,col,self.color()) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
else:
if self.Eliminate(row,col,new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
if self.Eliminate(row,col,new_row,col) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
if new_row != row: break
new_row -= 1
new_row = row
while new_row <= 6:
nxt_piece = self.board[new_row][col]
if nxt_piece == '00' and not self.inTrap(new_row,new_col,enemy_color):
if nxt_piece == '00' and not self.inTrap(new_row,new_col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
if not self.inHome(new_row,col,self.color()) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
else:
if self.Eliminate(row,col,new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
if self.Eliminate(row,col,new_row,col) and not self.inWater(new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
if new_row != row: break
new_row += 1
#L&R Direction
while new_col >= 0:
nxt_piece = self.board[row][new_col]
if nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color):
if nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
if not self.inHome(row,new_col,self.color()) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
else:
if self.Eliminate(row,col,row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
if self.Eliminate(row,col,row,new_col) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
if new_col != col: break
new_col -= 1
new_col = col
while new_col <= 8:
nxt_piece = self.board[row][new_col]
if nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color):
if nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color) and not self.inTrap(new_row,col,'r' if enemy_color == 'b' else 'b'):
if not self.inHome(row,new_col,self.color()) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
else:
if self.Eliminate(row,col,row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
if self.Eliminate(row,col,row,new_col) and not self.inWater(row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
if new_col != col: break
new_col += 1
return moves
@ -239,6 +376,7 @@ class GameState:
#移动操作
def makeMove(self,move):#cur是当前位置nxt是下一个位置,参数传入为元组
# makeMove假设这个move一定是合法的
# 为什么添加toUpload防止使用makeMove更新己方棋盘时把敌方棋盘错误上传导致“棋子消失”。
if self.board[move.end_row][move.end_col] != '00':
nxt_piece = self.board[move.end_row][move.end_col]
if nxt_piece[0] == 'r':
@ -248,18 +386,16 @@ class GameState:
self.board[move.end_row][move.end_col] = self.board[move.start_row][move.start_col]
self.board[move.start_row][move.start_col] = '00'
self.red_to_move = not self.red_to_move
class Move:
def __init__(self,start_loc,end_loc,board):
self.start_row = start_loc[0]
self.start_col = start_loc[1]
self.end_row = end_loc[0]
self.end_col = end_loc[1]
self.nxt_piece = board[self.end_row][self.end_col]
self.cur_piece = board[self.start_row][self.start_col]
self.attack = self.nxt_piece != '00'
self.moveID = self.start_row + self.start_col*10 + self.end_row *100 +self.end_col * 1000 #Hash
def __eq__(self, __o: object) -> bool:
if isinstance(__o, Move):
return self.moveID == __o.moveID
return False
self.move_log.append(move)
#由于使用多线程考虑让另一个线程去更新red_to_move
def undoMove(self):
if len(self.move_log) != 0:
move = self.move_log.pop()
self.board[move.start_row][move.start_col] = move.cur_piece
self.board[move.end_row][move.end_col] = move.nxt_piece
if move.nxt_piece != '00' and move.nxt_piece not in (self.blue_pieces if move.nxt_piece[0]=='b' else self.red_pieces):
(self.blue_pieces if move.nxt_piece[0]=='b' else self.red_pieces).append(int(move.nxt_piece[1]))
self.red_to_move = not self.red_to_move

@ -0,0 +1,5 @@
SOCKET_HOST=192.168.43.183
SOCKET_PORT=50005
MAX_WAITING_TIME=600
MAX_THNIKING_TIME=400
MAX_TOTAL_TIME=1000

@ -0,0 +1,247 @@
# coding:utf-8
import socket
import sys
import json
import uuid
import time
from threading import Thread, Lock
if(sys.version[:1] == "3"):
import queue as Queue
from _thread import *
else:
import Queue
from thread import *
class Config:
SOCKET_HOST = '127.0.0.1' # Symbolic name meaning all available interfaces
SOCKET_PORT = 50005 # Arbitrary non-privileged port
MAX_WAITING_TIME = 180
MAX_THNIKING_TIME = 60
MAX_TOTAL_TIME = 600
class Client:
def __init__(self, conn, addr, name, side, time, total):
self.conn = conn
self.addr = addr
self.name = name
self.side = side
self.time = time
self.total = total
mutex = Lock()
mutex_playing = Lock()
playing_ones = {}
waiting_players = Queue.Queue()
#init queues
#for games in range(1, 10):
# waiting_players[games] = Queue.Queue()
def load_config():
with open('config.txt', 'r') as f:
for line in f.readlines():
line = line.strip()
if line.find('SOCKET_HOST=') >= 0:
Config.SOCKET_HOST = line[line.index('=') + 1 : ]
elif line.find('SOCKET_PORT=') >= 0:
Config.SOCKET_PORT = int(line[line.index('=') + 1 : ])
elif line.find('MAX_WAITING_TIME=') >= 0:
Config.MAX_WAITING_TIME = int(line[line.index('=') + 1 : ])
elif line.find('MAX_THNIKING_TIME=') >= 0:
Config.MAX_THNIKING_TIME = int(line[line.index('=') + 1 : ])
elif line.find('MAX_TOTAL_TIME=') >= 0:
Config.MAX_TOTAL_TIME = int(line[line.index('=') + 1 : ])
else:
pass
def get_counterpart_from(waiting_ones):
counterpart = None
mutex.acquire()
if not waiting_ones.empty():
counterpart = waiting_ones.get()
mutex.release()
return counterpart
def to_wait_in_queue(client, the_queue):
mutex.acquire()
the_queue.put(client)
mutex.release()
def remove_one_from_queue(the_queue):
mutex.acquire()
if (the_queue.qsize() == 1):
the_queue.get()
mutex.release()
def send_msg_to(client, msg):
packet = json.dumps(msg)
if (sys.version[:1] == "3"):
packet = packet.encode('utf-8')
#print(client.addr[0] + ":" + str(client.addr[1]) + "\t" + str(msg))
client.conn.send(packet)
def __start_match_between(client0, client1):
match_uuid = str(uuid.uuid4())
mutex_playing.acquire()
playing_ones[match_uuid] = (client0, client1)
mutex_playing.release()
client0.side = 0
client0.time = Config.MAX_THNIKING_TIME
client0.total = Config.MAX_TOTAL_TIME
client1.side = 1
client1.time = -1
client1.total = Config.MAX_TOTAL_TIME
msg0 = {
"status": 1,
"counterpart_name": client1.name,
"game_id": match_uuid,
"side": client0.side,
"think_time": Config.MAX_THNIKING_TIME,
"total_time": Config.MAX_TOTAL_TIME,
}
send_msg_to(client0, msg0)
msg1 = {
"status": 1,
"counterpart_name": client0.name,
"game_id": match_uuid,
"side": client1.side,
"think_time": Config.MAX_THNIKING_TIME,
"total_time": Config.MAX_TOTAL_TIME,
}
send_msg_to(client1, msg1)
def join_game_handler(msg, addr, conn):
new_client = Client(conn, addr, msg['name'], -1, -1, -1)
#game_type = msg["id"]
#the_queue = waiting_players[game_type]
counterpart = get_counterpart_from(waiting_players)
if not counterpart: #wait
to_wait_in_queue(new_client, waiting_players)
return
else:
#counterpart=get_counterpart_from(waiting_players[game_type])
__start_match_between(new_client, counterpart)
def quit_game_handler(msg):
match_uuid = msg['game_id']
if match_uuid is None:
remove_one_from_queue(waiting_players)
return
pairs = None
mutex_playing.acquire()
if (match_uuid in playing_ones):
pairs = playing_ones[match_uuid]
del playing_ones[match_uuid]
mutex_playing.release()
if pairs is not None:
if(pairs[0].side == msg['side']):
to_notify = pairs[1]
else:
to_notify = pairs[0]
msg = {
"status": 2, #exit
"game_id": match_uuid,
"side": msg['side'],
"request": msg['request'],
}
send_msg_to(to_notify, msg)
def timer_thread():
while True:
time.sleep(1)
mutex_playing.acquire()
for game_id in list(playing_ones.keys()):
(client0, client1) = playing_ones[game_id]
#print("%d - %d" % (client0.time, client1.time))
if (client0.time < 0):
client = client1
else:
client = client0
if (client.time == 0 or client.total == 0):
msg = {
"status": 3, #timeout exit
"game_id": game_id,
"side": client.side,
}
send_msg_to(client0, msg)
send_msg_to(client1, msg)
del playing_ones[game_id]
else:
client.time -= 1
client.total -= 1
mutex_playing.release()
def transfer_message(msg):
match_uuid = msg['game_id']
pairs = playing_ones[match_uuid]
if(pairs[0].side == msg['side']):
to_notify = pairs[1]
pairs[0].time = -1
else:
to_notify = pairs[0]
pairs[1].time = -1
to_notify.time = Config.MAX_THNIKING_TIME
send_msg_to(to_notify, msg)
def client_thread(conn, addr):
while True:
data = conn.recv(1024)
if not data:
break
print(data)
data = json.loads(data)
if not 'type' in data:
transfer_message(data['msg'])
continue
if data['type'] == 0:
join_game_handler(data['msg'], addr, conn)
continue
elif data['type'] == 1:
transfer_message(data['msg'])
continue
elif data['type'] == 2:
quit_game_handler(data['msg'])
break
elif data['type'] == 3:
break
else:
#delivering message between the two clients
transfer_message(data['msg'])
continue
#came out of loop
conn.close()
def main():
load_config()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Socket server created')
#try:
server.bind((Config.SOCKET_HOST, Config.SOCKET_PORT))
#except (socket.error, msg):
# print ('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
# sys.exit()
print('Socket bind complete')
server.listen(10)
print('Socket now listening')
#now keep talking with the client
start_new_thread(timer_thread, ())
while True: #! 监听本体
#wait to accept a connection - blocking call
conn, addr = server.accept()
print ('Connected with ' + addr[0] + ':' + str(addr[1]))
#start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function.
start_new_thread(client_thread, (conn, addr))
server.close()
if __name__ == '__main__':
main()
Loading…
Cancel
Save