diff --git a/.vscode/launch.json b/.vscode/launch.json index e8b9b31..7e5da93 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,6 +12,15 @@ "cwd": "${workspaceFolder}", "console": "integratedTerminal", "justMyCode": true + }, + { + "name": "run single server", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/network/server.py", + "cwd": "${fileDirname}", + "console": "integratedTerminal", + "justMyCode": true } ] } \ No newline at end of file diff --git a/AboutGame.md b/AboutGame.md index 690750f..970aeef 100644 --- a/AboutGame.md +++ b/AboutGame.md @@ -124,5 +124,12 @@ ex.大致图像 +#### 二、开发流程 +(一)UI设计 +​ -----增加相关按钮,增加提示,增加每一步的提示 + +(二)功能实现 + +​ -----与服务器数据交换 diff --git a/Image/button1.jpg b/Image/button1.jpg new file mode 100644 index 0000000..31539a6 Binary files /dev/null and b/Image/button1.jpg differ diff --git a/Image/button2.jpg b/Image/button2.jpg new file mode 100644 index 0000000..ad30cbf Binary files /dev/null and b/Image/button2.jpg differ diff --git a/Image/button3.jpg b/Image/button3.jpg new file mode 100644 index 0000000..859914e Binary files /dev/null and b/Image/button3.jpg differ diff --git a/Image/button4.jpg b/Image/button4.jpg new file mode 100644 index 0000000..76023fc Binary files /dev/null and b/Image/button4.jpg differ diff --git a/SafariChess_Classes.py b/SafariChess_Classes.py index f6e305d..207c2f6 100644 --- a/SafariChess_Classes.py +++ b/SafariChess_Classes.py @@ -4,18 +4,15 @@ """ 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(): #下拉选择框 diff --git a/SafariChess_Gamev1.0.py b/SafariChess_Gamev1.0.py index ee0db8d..f35b54c 100644 --- a/SafariChess_Gamev1.0.py +++ b/SafariChess_Gamev1.0.py @@ -15,16 +15,16 @@ import pygame as pg import sys from time import process_time import SafariChess_backend as backend -import SafariChess_Classes as classes - +from SafariChess_Classes import Button +import network.server as Server #设置棋盘的参数 -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 +32,8 @@ DIMENSION_ROWS = 7 DIMENSION_COLUMNS = 9 SIZE = 64 IMAGES = {} -bias_top = 200 #棋盘的上边距 -bias_left = 10 #棋盘的左边距 +bias_top = 100 #棋盘的上边距 +bias_left = 100 #棋盘的左边距 def loadImages():#加载图片,a,b双方分别有象狮豹狼狐鹰鼠七个角色 pieces = ["r7", "r6", "r5", "r4", "r3", "r2", "r1", @@ -80,6 +80,8 @@ def drawPieces(screen,board): if piece != "00": screen.blit(IMAGES[piece], pg.Rect((column * SIZE) + bias_left + 5, (row * SIZE) + bias_top + 5, SIZE - 10, SIZE - 10)) +def uploadtoServer(move : backend.Move): + pass # square_selected 是当前选中的可以移动的棋子的位置 def protrudeSquares(screen,game_state,square_selected,valid_moves): @@ -112,8 +114,8 @@ def drawMovelog(screen):#绘制移动日志 pass -def startGamePage():#开始游戏界面,Human/AI - startpage = ''' +def startGamePage(mode = 1):#开始游戏界面,Human/AI + startpage = (''' WELCOME TO @@ -129,13 +131,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 ) - 退出游戏 ################################################################ - - ''' +Current Mode is: %s + '''%['单机游戏','网络对战','人机对战','退出游戏'][mode-1]) print(startpage) # 选择游戏模式 not_selected = True @@ -143,7 +145,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 +154,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 @@ -178,16 +180,61 @@ 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.jpg"), (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)) #按钮设置 - return 1 + #主菜单包含如下按钮: + #单机游戏,双人游戏,人机对战,退出游戏 + button_1 = Button(bt1,(350,350)) + button_2 = Button(bt2,(350,400)) + button_3 = Button(bt3,(350,450)) + button_4 = Button(bt4,(350,500)) + mode = -1 + p1,p2 = 0,0 + 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): + mode = 2 + run = False + if button_3.rect.collidepoint(event.pos): + mode = 3 + run = False + if button_4.rect.collidepoint(event.pos): + pg.quit() + sys.exit() + 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) + pg.display.flip() + return mode - -def main(): + +def main(mode,p1,p2): pg.init() screen = pg.display.set_mode((BOARD_WIDTH + MOVE_LOG_PANEL_WIDTH, BOARD_HEIGHT)) pg.display.set_caption("Safafi Chess Game") @@ -252,6 +299,7 @@ def main(): if __name__ == '__main__': print("Loading...") print("Initialing game...") + mode = MainMenu() main() diff --git a/__pycache__/SafariChess_Classes.cpython-39.pyc b/__pycache__/SafariChess_Classes.cpython-39.pyc index 3a53c35..fc2a4cd 100644 Binary files a/__pycache__/SafariChess_Classes.cpython-39.pyc and b/__pycache__/SafariChess_Classes.cpython-39.pyc differ diff --git a/__pycache__/SafariChess_backend.cpython-39.pyc b/__pycache__/SafariChess_backend.cpython-39.pyc index c7d7eff..3f19128 100644 Binary files a/__pycache__/SafariChess_backend.cpython-39.pyc and b/__pycache__/SafariChess_backend.cpython-39.pyc differ diff --git a/network/config.txt b/network/config.txt new file mode 100644 index 0000000..1ead9da --- /dev/null +++ b/network/config.txt @@ -0,0 +1,5 @@ +SOCKET_HOST=127.0.0.1 +SOCKET_PORT=50005 +MAX_WAITING_TIME=600 +MAX_THNIKING_TIME=15 +MAX_TOTAL_TIME=1000 diff --git a/network/server.py b/network/server.py new file mode 100644 index 0000000..9770956 --- /dev/null +++ b/network/server.py @@ -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() + \ No newline at end of file diff --git a/network/transfer.py b/network/transfer.py new file mode 100644 index 0000000..fa66b15 --- /dev/null +++ b/network/transfer.py @@ -0,0 +1,26 @@ +def uploadtoServer(move,game_state): + #将move转换为json格式 + + #将json格式的move上传到服务器 + + pass + +def getEnemyMove(): + #从服务器获取json信息 + + #将json信息转化成move + + #返回move + pass + +def updatefromServer(game_state): + move=getEnemyMove() + game_state.makeMove(move) + pass + +def getLoginInfo(): + #从服务器获取登录信息 + + #返回登录信息 + pass +