You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SafariChess/SafariChess_Gamev1.0.py

598 lines
23 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
Course: Programming practice
Students: Qu Qian /Tan Zhengyuan
"""
"""
Safafi Chess Game/SFF Chess Game
(Animal Chess Game)
Main code for run the game
it handle user input and GUI
"""
import colorsys
import json
from multiprocessing import connection
import pygame as pg
import sys
import socket
import SafariChess_backend as backend
from SafariChess_Classes import Button, DropDown
from threading import Thread, Lock
import time
#设置棋盘的参数
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
DIMENSION_ROWS = 7
DIMENSION_COLUMNS = 9
SIZE = 64
IMAGES = {}
bias_top = 100 #棋盘的上边距
bias_left = 100 #棋盘的左边距
#网络道具
client = None
networkMsg = None
server = None
port = None
addr = None
connection = None
#网络版更新需要注意多线程的问题?
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(e)
return e
except OSError:
networkMsg = server_died_message
return None
def startNetworkServices():
global client,server,port,addr,connection
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server = '127.0.0.1'
port = 50005
addr = (server,port)
connection = client.connect(addr)
startNewThread(target=listenFromServer)
#等待敌人移动
def loadImages():#加载图片,a,b双方分别有象狮豹狼狐鹰鼠七个角色
pieces = ["r7", "r6", "r5", "r4", "r3", "r2", "r1",
"b7", "b6", "b5", "b4", "b3", "b2", "b1",
]
for piece in pieces:#字典的形式存储图片
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:
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"))
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(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(67,142,219),pg.Rect((column * SIZE) +bias_left+2, (row * SIZE) +bias_top +3, SIZE-2,SIZE-2))
#画陷阱
for row in [2,4]:
for column in [0,8]:
screen.blit(IMAGES["trap"], pg.Rect((column * SIZE) +bias_left+6, (row * SIZE) +bias_top +6, SIZE ,SIZE ))
for column in [1,7]:
screen.blit(IMAGES["trap"], pg.Rect((column * SIZE) +bias_left+6, (3 * SIZE) +bias_top +6, SIZE ,SIZE ))
#画巢穴
screen.blit(IMAGES["home1"], pg.Rect((bias_left+6, (3 * SIZE) +bias_top +6, SIZE ,SIZE )))
screen.blit(IMAGES["home2"], pg.Rect( 8*SIZE+bias_left+6, (3 * SIZE) +bias_top +6, SIZE ,SIZE ))
def drawPieces(screen,board):
#画棋子的位置
for row in range(DIMENSION_ROWS):
for column in range(DIMENSION_COLUMNS):
piece = board[row][column]
if piece != "00":
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):
#高亮选中的棋子接下来可行的位置
if square_selected != ():
row = square_selected[0]
column = square_selected[1]
if game_state.board[row][column][0] == ('r' if game_state.red_to_move else 'b'):
# highlight selected square
s = pg.Surface((SIZE, SIZE))
s.set_alpha(100)
s.fill(pg.Color('blue'))
screen.blit(s, (column *SIZE + bias_left, row * SIZE + bias_top))
# highlight moves from that square
s.fill(pg.Color('yellow'))
for move in 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)
protrudeSquares(screen,game_state,square_selected,valid_moves)
drawPieces(screen,game_state.board)
#待开发
def drawMovelog(screen):#绘制移动日志
pass
def startGamePage(mode = 1):#开始游戏界面Human/AI
startpage = ('''
WELCOME TO
____ ____________ _____ _
/ __/ ___/ __ | / __ \ |
|__| |___| |__| | | / / |___ ___ ___ ___
\__ | ___| _ / | | | _ \/ _ \/ __/ __|
__| | | | | \ \ | \__/\ | | | __/\__ \__ |
|____/_| |_| \__| \_____/_| |_|\___||___/___/
################################################################
Please type one of the numbers below to choose a playing mode:
( 1 ) - 单机游戏
( 2 ) - 网络对战
( 3 ) - 人机对战
( 4 ) - 退出游戏
################################################################
Current Mode is: %s
'''%['单机游戏','网络对战','人机对战','退出游戏'][mode-1])
print(startpage)
# 选择游戏模式
not_selected = True
p1 , p2 = 0, 0
while not_selected:
try:
#mode = int(input())
#mode = 1
if mode in [1,2,3,4]:
if mode == 1:
p1 = 1
p2 = 1
##其余版本还未写
not_selected = False
else:
print("Please type one of the numbers below to choose a playing mode:")
print(" ( 1 ) - 单机游戏")
print(" ( 2 ) - 网络对战")
print(" ( 3 ) - 人机对战")
print(" ( 4 ) - 退出游戏")
except ValueError:
print("Wrong Input")
return p1, p2, mode
def showGameOverText(screen, text):
#游戏结束,出现的文字 text是字符串“Left Win”或“Right Win”
font = pg.font.SysFont("comicsansms", 32)
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('red'))
screen.blit(text_object, text_location.move(-2 , -2))
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.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))
#按钮设置
#主菜单包含如下按钮:
#单机游戏,双人游戏,人机对战,退出游戏
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,(600,20))
#音乐设置
pg.mixer.init()
music = pg.mixer.Sound("./Image/music1.mp3")
music.set_volume(0)
music.play()
pg.display.flip()
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],
# 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):
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()
if music_button.rect.collidepoint(event.pos):
if music.get_volume() == 0:
music.set_volume(0.5)
else:
music.set_volume(0)
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 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("Safari Chess Game")
clock = pg.time.Clock()
screen.fill(pg.Color("white"))
loadImages()
drawBoard(screen)
isOnline = bool(mode == 2)
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 = [] #点击队列,记录第一次点击和第二次点击位置,方便移动棋子
button_music = Button(pg.transform.scale(pg.image.load("Image/music.bmp"),(100,50)),(500,0))
screen.blit(button_music.image, button_music.rect)
pg.display.update()
startGamePage(mode)
if mode == 1:
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
square_selected = ()
click_queue = []
else:
click_queue = [square_selected]
elif e.type == pg.KEYDOWN:
#设置某些按键用于悔棋,重新开始游戏,机器提示,退出游戏等功能
pass
elif e.type == COUNT:# 判断事件是否为计时事件
counts=counts+1
countstext=str(counts)
showText(screen,bigfont,countstext,200,350)
pg.display.update()
if mademove:
valid_moves = game_state.getAllMoves()
mademove = False
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:
##一些按钮实现
button_stop = Button(pg.transform.scale(pg.image.load("Image/叫停.bmp"),(100,50)),(600,0))
screen.blit(button_stop.image, button_stop.rect)
# 初始化计时器
counts=0
# 自定义计时事件
COUNT = pg.USEREVENT +1
# 每隔1秒发送一次自定义事件
pg.time.set_timer(COUNT,1000)
now = time.ctime()# 获得系统当前时间
countdown_clock = now[11:19]# 格式化形式为时分秒
bigfont = pg.font.SysFont("Courier", 32)
minfont = pg.font.SysFont("Courier", 24)
showText(screen,bigfont,"Time:",600,600)
showText(screen,minfont,"Count:",600,500)
showText(screen,bigfont,countdown_clock,600,650)
pg.display.update()
startNetworkServices()
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)
while networkMsg == None: pass
while running:
#print('current moving color: ',game_state.color())
if lastNetworkMsg != networkMsg:#handle
if(networkMsg == server_died_message):
print("server died")
return None
print('catch 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']
other_stage = True if networkMsg['side']==0 else False
if(other_stage == True):
print('waiting for other player to move...')
#quick_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':
pass
elif networkMsg['request'] == 'report':
foul = networkMsg['side']
otherQuitJson = {
'type': 3,
'side': 0
}
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': 0
}
client.send(json.dumps(otherQuitJson).encode('utf-8'))
game_over = True
elif 'src' and 'dst' in networkMsg.keys():
theMove = backend.Move([networkMsg['src']['x'],networkMsg['src']['y']],[networkMsg['dst']['x'],networkMsg['dst']['y']],game_state.board)
game_state.makeMove(theMove)
valid_moves = game_state.getAllMoves()
other_stage = not other_stage
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 not game_over and not other_stage and other_joined:
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]
if mademove:
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": thisMove.cur_piece,
"src": {
"x": thisMove.start_row,
"y": thisMove.start_col
},
"dst": {
"x": thisMove.end_row,
"y": thisMove.end_col
}
}
}
client.send(json.dumps(thisMoveJson).encode('utf-8'))
#思路变成:定时对敌方进行扫描,若收到更新相关包则进行局面更新。
ShowGameState(screen,game_state,valid_moves,square_selected)
if game_state.conquer():
print("GAME OVER!")
showGameOverText(screen,"player "+game_state.win_person+" wins")
if not game_over and game_state.win_person == ('b' if MySide == 0 else 'r' ):
winJson = {
'type': 2,
'msg': {
'request': 'stop',
'game_id': game_id,
'side': MySide
}
}
client.send(json.dumps(winJson).encode("utf-8"))
game_over = True
if game_over and not other_joined:
showGameOverText(screen,"player "+ ("b" if MySide==1 else "r") + " quitted the game")
if time_out_side != None:
showGameOverText(screen,"player "+ ("b" if time_out_side==0 else "r")+" timed out")
clock.tick(50)
pg.display.flip()
if __name__ == '__main__':
print("Loading...")
print("Initialing game...")
mode = MainMenu()
#mode = 2
main(mode)