Compare commits

..

No commits in common. 'master' and 'tzy_branch' have entirely different histories.

BIN
.gitignore vendored

Binary file not shown.

8
.idea/.gitignore vendored

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

@ -1,8 +0,0 @@
<?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>

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

@ -1,4 +0,0 @@
<?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>

@ -1,8 +0,0 @@
<?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>

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

@ -12,24 +12,6 @@
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"console": "integratedTerminal", "console": "integratedTerminal",
"justMyCode": true "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,10 +68,6 @@ ex.大致图像
(a方在左 默认为红色b方在右 默认为蓝色) (a方在左 默认为红色b方在右 默认为蓝色)
#### 二、后端逻辑 #### 二、后端逻辑
行棋规则实现 行棋规则实现
@ -80,56 +76,3 @@ 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.

Before

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

@ -1,256 +0,0 @@
'''
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,68 +2,21 @@
SafariChess_Classes.py SafariChess_Classes.py
Classes for button and keyboard events Classes for button and keyboard events
""" """
import pygame as pg
class Button: class Button:
def __init__(self, image, position, callback=None): def _init_(self, image, position, callback=None):
self.image = image self.image = image
self.rect = image.get_rect(topleft=position) self.rect = image.get_rect(topleft=position)
self.callback = callback self.callback = callback
def on_click(self, event): def on_click(self, mouse_pos):
if event.button == 1: if self.rect.collidepoint(mouse_pos):
if self.rect.collidepoint(event.pos): if self.callback:
self.callback(self) self.callback()
return True
return False
#下拉框 #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,26 +10,21 @@ it handle user input and GUI
""" """
import colorsys import colorsys
import json from json import load
from multiprocessing import connection
import pygame as pg import pygame as pg
import sys import sys
import socket from time import process_time
import SafariChess_backend as backend import SafariChess_backend as backend
import SafariChess_AI as AI import SafariChess_Classes as classes
from SafariChess_Classes import Button, DropDown
from threading import Thread, Lock
from itertools import count, cycle
import time
import random
#设置棋盘的参数 #设置棋盘的参数
WIDTH = 1000 WIDTH = 800
HEIGHT = 800 HEIGHT = 1000
BOARD_WIDTH = 800 BOARD_WIDTH = 600
BOARD_HEIGHT = 600 BOARD_HEIGHT = 800
SCREEN_WIDTH = 800 SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600 SCREEN_HEIGHT = 800
MOVE_LOG_PANEL_WIDTH = 0 MOVE_LOG_PANEL_WIDTH = 0
MOVE_LOG_PANEL_HEIGHT = BOARD_HEIGHT MOVE_LOG_PANEL_HEIGHT = BOARD_HEIGHT
DIMENSION = 8 DIMENSION = 8
@ -37,46 +32,8 @@ DIMENSION_ROWS = 7
DIMENSION_COLUMNS = 9 DIMENSION_COLUMNS = 9
SIZE = 64 SIZE = 64
IMAGES = {} IMAGES = {}
bias_top = 100 #棋盘的上边距 bias_top = 200 #棋盘的上边距
bias_left = 20 #棋盘的左边距 bias_left = 10 #棋盘的左边距
#网络道具
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双方分别有象狮豹狼狐鹰鼠七个角色 def loadImages():#加载图片,a,b双方分别有象狮豹狼狐鹰鼠七个角色
pieces = ["r7", "r6", "r5", "r4", "r3", "r2", "r1", pieces = ["r7", "r6", "r5", "r4", "r3", "r2", "r1",
@ -86,7 +43,7 @@ def loadImages():#加载图片,a,b双方分别有象狮豹狼狐鹰鼠七个角
IMAGES[piece] = pg.transform.scale(pg.image.load("./Image/pieces/pieces_rb/" + piece + ".png"), (SIZE - 10, SIZE - 10)) IMAGES[piece] = pg.transform.scale(pg.image.load("./Image/pieces/pieces_rb/" + piece + ".png"), (SIZE - 10, SIZE - 10))
tools = ["trap", "home1","home2"] 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)) 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 = pg.display.set_mode((BOARD_WIDTH + MOVE_LOG_PANEL_WIDTH, BOARD_HEIGHT))#设置窗口大小
#screen.fill(pg.Color("white")) #screen.fill(pg.Color("white"))
@ -97,12 +54,12 @@ def drawBoard(screen):#绘制棋盘:格子,河流,陷阱,巢穴
#colors = [(255, 255, 255), (0, 0, 0)] #colors = [(255, 255, 255), (0, 0, 0)]
for row in range(DIMENSION_ROWS): for row in range(DIMENSION_ROWS):
for column in range(DIMENSION_COLUMNS): for column in range(DIMENSION_COLUMNS):
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(10,100,10), 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)) 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 row in [1,2,4,5]:
for column in [3,4,5]: for column in [3,4,5]:
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)) pg.draw.rect(screen, pg.Color(5,5,100),pg.Rect((column * SIZE) +bias_left+3, (row * SIZE) +bias_top +3, SIZE,SIZE))
#画陷阱 #画陷阱
for row in [2,4]: for row in [2,4]:
@ -124,7 +81,6 @@ def drawPieces(screen,board):
screen.blit(IMAGES[piece], pg.Rect((column * SIZE) + bias_left + 5, (row * SIZE) + bias_top + 5, SIZE - 10, SIZE - 10)) screen.blit(IMAGES[piece], pg.Rect((column * SIZE) + bias_left + 5, (row * SIZE) + bias_top + 5, SIZE - 10, SIZE - 10))
# square_selected 是当前选中的可以移动的棋子的位置 # square_selected 是当前选中的可以移动的棋子的位置
def protrudeSquares(screen,game_state,square_selected,valid_moves): def protrudeSquares(screen,game_state,square_selected,valid_moves):
#高亮选中的棋子接下来可行的位置 #高亮选中的棋子接下来可行的位置
@ -143,7 +99,7 @@ def protrudeSquares(screen,game_state,square_selected,valid_moves):
if move.start_row == row and move.start_col == column: 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)) screen.blit(s, (move.end_col * SIZE + bias_left, move.end_row * SIZE + bias_top))
def ShowGameState(screen,game_state,valid_moves,square_selected):#游戏状态 def ShowGameState(screen,game_state,valid_moves,square_selected):#游戏状态
drawBoard(screen) drawBoard(screen)
@ -156,8 +112,8 @@ def drawMovelog(screen):#绘制移动日志
pass pass
def startGamePage(mode = 1):#开始游戏界面Human/AI def startGamePage():#开始游戏界面Human/AI
startpage = (''' startpage = '''
WELCOME TO WELCOME TO
@ -173,13 +129,13 @@ def startGamePage(mode = 1):#开始游戏界面Human/AI
################################################################ ################################################################
Please type one of the numbers below to choose a playing mode: Please type one of the numbers below to choose a playing mode:
( 1 ) - 单机游戏 ( 1 ) - Human vs Human
( 2 ) - 网络对战 ( 2 ) - Human vs AI
( 3 ) - 人机对战 ( 3 ) - AI vs AI
( 4 ) - AI代打 ( 4 ) - Quit
################################################################ ################################################################
Current Mode is: %s
'''%['单机游戏','网络对战','人机对战','AI代打'][mode-1]) '''
print(startpage) print(startpage)
# 选择游戏模式 # 选择游戏模式
not_selected = True not_selected = True
@ -187,7 +143,7 @@ Current Mode is: %s
while not_selected: while not_selected:
try: try:
#mode = int(input()) #mode = int(input())
#mode = 1 mode = 1
if mode in [1,2,3,4]: if mode in [1,2,3,4]:
if mode == 1: if mode == 1:
p1 = 1 p1 = 1
@ -196,10 +152,10 @@ Current Mode is: %s
not_selected = False not_selected = False
else: else:
print("Please type one of the numbers below to choose a playing mode:") print("Please type one of the numbers below to choose a playing mode:")
print(" ( 1 ) - 单机游戏") print(" ( 1 ) - Human vs Human")
print(" ( 2 ) - 网络对战") print(" ( 2 ) - Human vs AI")
print(" ( 3 ) - 人机对战") print(" ( 3 ) - AI vs AI")
print(" ( 4 ) - 退出游戏") print(" ( 4 ) - Quit")
except ValueError: except ValueError:
print("Wrong Input") print("Wrong Input")
return p1, p2, mode return p1, p2, mode
@ -208,576 +164,93 @@ Current Mode is: %s
def showGameOverText(screen, text): def showGameOverText(screen, text):
#游戏结束,出现的文字 text是字符串“Left Win”或“Right Win” #游戏结束,出现的文字 text是字符串“Left Win”或“Right Win”
font = pg.font.SysFont("comicsansms", 32) font = pg.font.SysFont("comicsansms", 32)
text_object = font.render(text, True, pg.Color("grey")) text_object = font.render(text, True, pg.Color("red"))
text_location = pg.Rect(0, 0, BOARD_WIDTH, BOARD_HEIGHT).move(BOARD_WIDTH / 2 - text_object.get_width() / 2, 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) BOARD_HEIGHT / 2 - text_object.get_height() / 2)
screen.blit(text_object, text_location) screen.blit(text_object, text_location)
text_object = font.render(text, False, pg.Color('red')) text_object = font.render(text, False, pg.Color('black'))
screen.blit(text_object, text_location.move(-2 , -2)) screen.blit(text_object, text_location.move(5, 5))
pg.display.flip()
def MainMenu(): def MainMenu():
#显示主菜单 #显示主菜单
pg.init() pg.init()
mainClock = pg.time.Clock() mainClock = pg.time.Clock()
#ui
screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pg.display.set_caption("SafariChess by ZY&&DQ") pg.display.set_caption("SafariChess by ZY&&DQ")
bg = pg.transform.scale(pg.image.load("Image/MenuBg.bmp"), (SCREEN_WIDTH, SCREEN_HEIGHT)) bg = pg.image.load("images/MenuBg.jpg")
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)
# music_ops = DropDown([COLOR_INACTIVE, COLOR_ACTIVE],[COLOR_LIST_INACTIVE, COLOR_LIST_ACTIVE], def main():
# 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() pg.init()
screen = pg.display.set_mode((BOARD_WIDTH + MOVE_LOG_PANEL_WIDTH, BOARD_HEIGHT)) screen = pg.display.set_mode((BOARD_WIDTH + MOVE_LOG_PANEL_WIDTH, BOARD_HEIGHT))
pg.display.set_caption("Safari Chess Game") pg.display.set_caption("Safafi Chess Game")
clock = pg.time.Clock() clock = pg.time.Clock()
screen.fill(pg.Color("white")) screen.fill(pg.Color("white"))
loadImages() loadImages()
drawBoard(screen) drawBoard(screen)
isOnline = bool(mode == 2 or mode == 4)
game_state=backend.GameState() 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() valid_moves=game_state.getAllMoves()
running = True 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 mademove = False
game_over = False game_over = False
square_selected = () #刚开始没有选择任何一个棋子 square_selected = ()#刚开始没有选择任何一个棋子
click_queue = [] #点击队列,记录第一次点击和第二次点击位置,方便移动棋子 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() pg.display.update()
startGamePage(mode) #游戏进入前,可以进一步优化一些显示细节 startGamePage()
if mode == 1 or mode == 3: while running:
if mode == 3: for e in pg.event.get():
AI_side = random.randint(0,1) #接下来处理游戏中的事件
print('AI side:{}'.format('b' if AI_side == 1 else 'r')) if e.type == pg.QUIT:
while running: pg.quit()
for e in pg.event.get(): sys.exit()
#接下来处理游戏中的事件
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
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()
pg.display.flip() elif e.type == pg.MOUSEBUTTONDOWN:
#! end login #鼠标点击事件:用于选择棋子,移动棋子
# 自定义计时事件-----计时器实现 if not game_over:
COUNTS = pg.USEREVENT +1 mouse_loc = pg.mouse.get_pos()
# 每隔1秒发送一次自定义事件 row = int((mouse_loc[1] - bias_top) / SIZE)
pg.time.set_timer(COUNTS,1000) #以毫秒为单位 col = int((mouse_loc[0] - bias_left) / SIZE) #* get position of mouse click
bigfont = pg.font.SysFont("Consolas", 20) if square_selected == (row,col) or col >=DIMENSION_COLUMNS or row >= DIMENSION_ROWS:
minfont = pg.font.SysFont("Courier", 18) square_selected = ()
showText(screen,bigfont,"RemainTime:",680,200) #650,250为时间650350为对方轮数 click_queue = []
showText(screen,minfont,"Turn:",650,300) else:
clock = pg.time.Clock() square_selected = (row,col)
clock.tick(60) click_queue.append(square_selected)
if(len(click_queue) == 2):
cur_piece_loc = click_queue[0]
#drawButton22(screen) nxt_piece_loc = click_queue[1]
button_stop = Button(pg.transform.scale(pg.image.load("Image/叫停.bmp"), (100, 50)), (400, 0)) move = backend.Move(cur_piece_loc,nxt_piece_loc,game_state.board)
screen.blit(button_stop.image, button_stop.rect) if move in valid_moves:
button_quit = Button(pg.transform.scale(pg.image.load("Image/退出.bmp"), (100, 50)), (300, 0)) game_state.makeMove(move)
screen.blit(button_quit.image, button_quit.rect) mademove = True
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 = () square_selected = ()
click_queue = [] click_queue = []
else: else:
square_selected = (row,col) click_queue = [square_selected]
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: elif e.type == pg.KEYDOWN:
print("GAME OVER!") #设置某些按键用于悔棋,重新开始游戏,机器提示,退出游戏等功能
# pass
if game_state.win_person == ('r' if MySide == 1 else 'b'): #测试:我输了再发
winJson = { if mademove:
'type': 2, valid_moves = game_state.getAllMoves()
'msg': { ShowGameState(screen,game_state,valid_moves,square_selected)
'request': 'stop', if game_state.conquer():
'game_id': game_id, showGameOverText(screen,"player "+game_state.win_person+" wins")
'side': MySide game_over = True
} clock.tick(60)
} pg.display.flip()
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__': if __name__ == '__main__':
#print("Loading...") print("Loading...")
#print("Initialing game...") print("Initialing game...")
mode = MainMenu() main()
# mode = 4
main(mode)

@ -1,56 +1,11 @@
import socket class GameState:
import this def __init__(self):
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横向 1. 棋盘尺寸为7*9横向
2. 按照以下命名格式对棋盘中的棋子进行标注 2. 按照以下命名格式对棋盘中的棋子进行标注
(b|r)(1|2|3|4|5|6|7) (b|r)(1|2|3|4|5|6|7)
3. 左方为蓝方对应的网络服务器中的side项目编号为0 3.
右方为红方对应的网络服务器中的side项目编号为1
4. 网络版需要额外的类变量type, game_id, side, chessman(piece), src, dst
''' '''
self.board = [ self.board = [
['00','00','b7','00','00','00','r1','00','00'], ['00','00','b7','00','00','00','r1','00','00'],
@ -77,101 +32,16 @@ class GameState:
self.red_home=(3,8) self.red_home=(3,8)
self.blue_pieces=[7,6,5,4,3,2,1] self.blue_pieces=[7,6,5,4,3,2,1]
self.red_pieces=[7,6,5,4,3,2,1] self.red_pieces=[7,6,5,4,3,2,1]
#红方(右)先行 #蓝方(左)先行
self.red_to_move=True self.red_to_move=False
self.conquered=False self.conquered=False
self.win_person='' self.win_person=''
self.MASSACRE=False self.MASSACRE=False
self.isStarted = False
self.move_log = []
def color(self): def color(self):
return 'r' if self.red_to_move else 'b' 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): def inHome(self,row,col,color):
if color=="b": if color=="b":
@ -215,15 +85,8 @@ class GameState:
for col in range(len(self.board[row])): for col in range(len(self.board[row])):
player = self.board[row][col][0] player = self.board[row][col][0]
if (player == 'r' and self.red_to_move or player == 'b' and not self.red_to_move): 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) self.moveFunctions[self.board[row][col][1]](row,col,moves)
valid_moves = [] return 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 def getStdMoves(self,row,col,moves):#输入当前的位置将可行路径输出给moves
@ -264,37 +127,37 @@ class GameState:
#U&D direction #U&D direction
while new_row >= 0: while new_row >= 0:
nxt_piece = self.board[new_row][col] nxt_piece = self.board[new_row][col]
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 nxt_piece == '00' and not self.inTrap(new_row,col,enemy_color):
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)) 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: else:
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 self.Eliminate(row,col,new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
if new_row != row: break if new_row != row: break
new_row -= 1 new_row -= 1
new_row = row new_row = row
while new_row <= 6: while new_row <= 6:
nxt_piece = self.board[new_row][col] nxt_piece = self.board[new_row][col]
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 nxt_piece == '00' and not self.inTrap(new_row,new_col,enemy_color):
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)) 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: else:
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 self.Eliminate(row,col,new_row,col): moves.append(Move((row,col),(new_row,col),self.board))
if new_row != row: break if new_row != row: break
new_row += 1 new_row += 1
#L&R Direction #L&R Direction
while new_col >= 0: while new_col >= 0:
nxt_piece = self.board[row][new_col] nxt_piece = self.board[row][new_col]
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 nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color):
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)) 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: else:
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 self.Eliminate(row,col,row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
if new_col != col: break if new_col != col: break
new_col -= 1 new_col -= 1
new_col = col new_col = col
while new_col <= 8: while new_col <= 8:
nxt_piece = self.board[row][new_col] nxt_piece = self.board[row][new_col]
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 nxt_piece == '00' and not self.inTrap(row,new_col,enemy_color):
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)) 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: else:
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 self.Eliminate(row,col,row,new_col): moves.append(Move((row,col),(row,new_col),self.board))
if new_col != col: break if new_col != col: break
new_col += 1 new_col += 1
return moves return moves
@ -376,7 +239,6 @@ class GameState:
#移动操作 #移动操作
def makeMove(self,move):#cur是当前位置nxt是下一个位置,参数传入为元组 def makeMove(self,move):#cur是当前位置nxt是下一个位置,参数传入为元组
# makeMove假设这个move一定是合法的 # makeMove假设这个move一定是合法的
# 为什么添加toUpload防止使用makeMove更新己方棋盘时把敌方棋盘错误上传导致“棋子消失”。
if self.board[move.end_row][move.end_col] != '00': if self.board[move.end_row][move.end_col] != '00':
nxt_piece = self.board[move.end_row][move.end_col] nxt_piece = self.board[move.end_row][move.end_col]
if nxt_piece[0] == 'r': if nxt_piece[0] == 'r':
@ -386,16 +248,18 @@ class GameState:
self.board[move.end_row][move.end_col] = self.board[move.start_row][move.start_col] 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.board[move.start_row][move.start_col] = '00'
self.red_to_move = not self.red_to_move self.red_to_move = not self.red_to_move
self.move_log.append(move)
class Move:
#由于使用多线程考虑让另一个线程去更新red_to_move def __init__(self,start_loc,end_loc,board):
self.start_row = start_loc[0]
def undoMove(self): self.start_col = start_loc[1]
if len(self.move_log) != 0: self.end_row = end_loc[0]
move = self.move_log.pop() self.end_col = end_loc[1]
self.board[move.start_row][move.start_col] = move.cur_piece self.nxt_piece = board[self.end_row][self.end_col]
self.board[move.end_row][move.end_col] = move.nxt_piece self.cur_piece = board[self.start_row][self.start_col]
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.attack = self.nxt_piece != '00'
(self.blue_pieces if move.nxt_piece[0]=='b' else self.red_pieces).append(int(move.nxt_piece[1])) self.moveID = self.start_row + self.start_col*10 + self.end_row *100 +self.end_col * 1000 #Hash
self.red_to_move = not self.red_to_move def __eq__(self, __o: object) -> bool:
if isinstance(__o, Move):
return self.moveID == __o.moveID
return False

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

@ -1,247 +0,0 @@
# 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