遊戲規則
交互式遊戲設計(RemoteBet) (基於TCP)
通過在遠端主機上搭建一個遠程骰寶服務器,其它主機可以通過客戶端程序RemoteBet與遠程的骰寶服務器聯繫,進行交互式遊戲設計。命令行格式如下:
RemoteBet <ServerIP>
如: RemoteBet 127.0.0.1
規則如下:
ya tc <數量> <coin|silver|gold> 押頭彩(兩數順序及點數均正確) 一賠三十五
ya dc <數量> <coin|silver|gold> 押大彩(兩數點數正確) 一賠十七
ya kp <數量> <coin|silver|gold> 押空盤(兩數不同且均爲偶數) 一賠五
ya qx <數量> <coin|silver|gold> 押七星(兩數之和爲七) 一賠五
ya dd <數量> <coin|silver|gold> 押單對(兩數均爲奇數) 一賠三
ya sx <數量> <coin|silver|gold> 押散星(兩數之和爲三、五、九、十一) 一賠二
每盤按從上到下的順序只出現一種點型(頭彩和大彩可同時出現),其他情況都算莊家贏。
莊家唱道:新開盤!預叫頭彩!
莊家將兩枚玉骰往銀盤中一撒。
┌───┐ ┌───┐
│● ● │ │ │
│● ● │ │ ● │
│● ● │ │ │
└───┘ └───┘
莊家唱道:頭彩骰號是六、一!
輸入你壓的值:
ya tc 10 gold
莊家將兩枚玉骰扔進兩個金盅,一手持一盅搖將起來。
莊家將左手的金盅倒扣在銀盤上,玉骰滾了出來。
┌───┐
│● ●│
│ ● │
│● ●│
└───┘
莊家將右手的金盅倒扣在銀盤上,玉骰滾了出來。
┌───┐
│ ● │
│ │
│ ● │
└───┘
莊家叫道:五、二……七星。
你贏了多少?or 你輸了多少?
假設服務器的IP地址爲127.0.0.1,客戶端程序連接到服務器進行遠程骰寶遊戲,然後按照服務器發回的遊戲規則和提示進行遊戲。
程序設計思路
客戶端:
- 用戶運行程序後,客戶端在CMD中輸出程序的規則
- 當用戶點擊開始後,程序首先輸出頭彩(這個頭彩由客戶端輸出,然後開始等待用戶輸入)
- 當用戶輸入自己想要的選擇後,發送給服務器,服務器處理,然後將用戶的信息存儲起來(需要發送他的地址),對用戶發出博彩信息,先是第一個(存儲),然後第二個(存儲),然後根據與用戶發過來的信息進行對比,然後再次發送給用戶結果
- 客戶端界面顯示結果
服務端:
- 處於不斷接收用戶傳來的信息的狀態
- 將用戶發來的信息存儲,然後進行處理(因爲用戶是使用空格分離,所以取出對應的信息)然後發送數字(1~6,讓客戶端對應數字輸出對應的圖案,這裏一次發送兩個數字避免出現問題)並存儲這兩個數字並與客戶端進行對比
- 然後發送給客戶端結果
應用層協議設計
1、 先使用TCP協議將客戶端同服務端連接
2、 然後服務端在服務器中將客戶信息記錄下來
3、 然後客戶端開始傳輸信息
4、 服務器接收信息並處理
5、 處理結束後反饋給客戶端
所選用的Python庫介紹
一共選了以下幾個庫:
1、 Socket:這個就是套接字庫,進行通訊
2、 Threading:這個庫的作用是在服務器進行多線程的時候使用的
3、 Json:這個庫是爲了在信息傳輸加密的時候使用的
4、 Random:這個庫的作用是選取隨機數
5、 Datetime:這個庫的作用是輸出當前的時間
測試流程
1、 首先是客戶端的登入界面:
2、 然後用戶輸入自己的選擇:
3、 然後用戶界面會顯示用戶的結果
4、 如果用戶選擇退出的話:
5、 服務端的顯示界面:
6、 程序支持多線程連入:
分析總結及心得
1、 我認識到了多線程的重要性和便攜性,上次的UDP實驗我記得當時只能一方面,如果當時用上了多線程的話,就可以同時進行了
2、 TCP相對於UDP來說方便多了,而且個人也感覺更容易實現
3、 而且這個對於實現遠程服務器來說,難度不大,親測
源代碼
client.py:
# 這個爲客戶端代碼,大部分功能是在客戶端代碼上實現的
import socket
import random
import json
# 配置基礎信息
HOST = '127.0.0.1' # 服務器IP
PORT = 8022 # 服務器端口IP
BUFSIZE = 65535 # 緩衝區大小(1K)
ADDR = (HOST, PORT) # 這是一個雙元組
# rule 這個函數的作用是向用戶輸出這個遊戲的規則
def rule():
print("ya tc <數量> <coin|silver|gold> 押頭彩(兩數順序及點數均正確) 一賠三十五\n"
"ya dc <數量> <coin|silver|gold> 押大彩(兩數點數正確) 一賠十七\n"
"ya kp <數量> <coin|silver|gold> 押空盤(兩數不同且均爲偶數) 一賠五\n"
"ya qx <數量> <coin|silver|gold> 押七星(兩數之和爲七) 一賠五\n"
"ya dd <數量> <coin|silver|gold> 押單對(兩數均爲奇數) 一賠三\n"
"ya sx <數量> <coin|silver|gold> 押散星(兩數之和爲三、五、九、十一) 一賠二\n"
"每盤按從上到下的順序只出現一種點型(頭彩和大彩可同時出現),其他情況都算莊家贏\n")
# judge函數是用來根據服務發送過來的數字輸出對應的圖案
def judge(number):
if number == 1:
print(" ┌───────┐ \n"
" │ │ \n"
" │ ● │ \n"
" │ │ \n"
" └───────┘ \n")
elif number == 2:
print(" ┌───────┐ \n"
" │ │ \n"
" │ ● ●│ \n"
" │ │ \n"
" └───────┘ \n")
elif number == 3:
print(" ┌───────┐ \n"
" │ ● │ \n"
" │ ● │ \n"
" │ ●│ \n"
" └───────┘ \n")
elif number == 4:
print(" ┌───────┐ \n"
" │ ● ●│ \n"
" │ │ \n"
" │ ● ●│ \n"
" └───────┘ \n")
elif number == 5:
print(" ┌───────┐ \n"
" │ ● ●│ \n"
" │ ● │ \n"
" │ ● ●│ \n"
" └───────┘ \n")
elif number == 6:
print(" ┌───────┐ \n"
" │ ● ●│ \n"
" │ ● ●│ \n"
" │ ● ●│ \n"
" └───────┘ \n")
# result 這個函數的作用是判斷服務發送過來的結果參數,然後輸出對應的結果 1 表示勝利, 0 表示失敗
def result(parameter, grade):
if parameter == 1:
print("Congratulation! You are Winner!")
print("You got the grade is {}".format(grade))
else:
print("Sorry!You lose the game...")
print("You lose the grade is {}".format(grade))
# theFirstPrice 這個頭彩函數是用來在客戶端輸出兩個隨機數,減輕服務端的負擔
def thefirstprice():
# 首先獲得兩個隨機數
theFirstRandom = random.randint(1, 6)
theSecondRandom = random.randint(1, 6)
# 然後莊家開始預叫頭彩
print("新開盤,預叫頭彩!")
judge(theFirstRandom)
judge(theSecondRandom)
# encodeoperate 這個函數的作用是將消息加密和json化統一操作
def encodeoperate(msg):
msg = json.dumps(msg)
msg = msg.encode('utf-8')
return msg
# decodeoperate 這個函數的作用是將消息解碼和json化統一操作
def decodeoperate(msg):
msg = msg.decode('utf-8')
msg = json.loads(msg)
return msg
# inputoperate 這個函數的作用是將用戶的操作集中
def inputoperate():
# 開啓一個無限循環如果用戶想要一直玩的話
while True:
# 輸入用戶的押注
choice = input("Please input your choice!//If you want to quit Please input quit!\n")
# 判斷是否要退出
if choice == "quit":
print("thank you for playing......")
break
# 對消息進行加密編碼
data = encodeoperate(choice)
# 將消息發送給服務器
tcpCliSock.sendall(data)
# 然後等待服務的迴應
information = tcpCliSock.recv(BUFSIZE)
msgFromSever = decodeoperate(information)
# 從服務器接收來的是一個含有四個元素的列表
# 然後客戶端開始一個一個輸出骰子
print("The first one is :")
judge(msgFromSever[0])
print("The second one is :")
judge(msgFromSever[1])
# 輸出用戶的結果
result(msgFromSever[2], msgFromSever[3])
if __name__ == '__main__':
# 下面這個是創建tcp連接
tcpCliSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpCliSock.connect(ADDR)
# 下面開始了程序部分
# 首先先向客戶輸出規則
rule()
# 利用input函數的中斷,當用戶明白規則後按回車繼續
input("Please input Enter to continue!")
# 輸出頭彩
thefirstprice()
# 用戶操作集合
inputoperate()
server.py:
# 這段代碼表示服務器
import socket
import threading
import json
import random
import datetime
# 配置基礎信息
HOST = '127.0.0.1' # 服務器IP
PORT = 8022 # 服務器端口IP
BUFSIZE = 65535 # 緩衝區大小(1K)
ADDR = (HOST, PORT) # 這是一個雙元組
socketList = [] # 這裏用存儲
# encodeoperate 這個函數的作用是將消息加密和json化統一操作
def encodeoperate(msg):
msg = json.dumps(msg)
msg = msg.encode('utf-8')
return msg
# decodeoperate 這個函數的作用是將消息解碼和json化統一操作
def decodeoperate(msg):
msg = msg.decode('utf-8')
msg = json.loads(msg)
return msg
# targetoperate 這個函數的作用不斷接收來自客戶端的消息並處理
def targetoperate(target, address):
try:
# 採用循環不斷地從socket中讀取客戶端發送過來的數據
while True:
# 接收客戶端的請求
content = readfromclient(target)
# 首先先隨機兩個隨機數
theFirstRandom = random.randint(1, 6)
theSecondRandom = random.randint(1, 6)
# 然後對客戶端的請求進行處理
data = solve(content, theFirstRandom, theSecondRandom, address)
# 對得到的data送至客戶端
target.sendall(data)
except Exception:
print("The client has error")
return
# readfromclient 這個函數的作用是不斷處於接受某個客戶端的狀態,如果客戶端退出,就刪除這個線程
def readfromclient(target):
try:
return decodeoperate(target.recv(BUFSIZE))
# 如果捕獲到異常,則表明該socket對應的客戶端已經關閉
except Exception:
# 刪除該socket
target.close()
# solve 這個函數是對客戶端發送過來的信息進行處理
def solve(content, first, second, address):
# 因爲客戶端發送過來的格式是以空格來分開的如:XX XX XX XX,將這個格式變成列表
content = content.split()
# 得到用戶壓的數量
count = content[2]
# 首先得到用戶選擇的類型
operate = content[1]
# 初始化一個result
result = 0
# 初始化當前的時間
now = datetime.datetime.now()
# 下面開始對用戶的選擇進行判斷
if operate == 'tc':
# 下面這個表示中了頭彩
if content[3] == first & content[4] == second:
result = 1
count = count * 35
if operate == 'dc':
if content[4] == first & content[4] == second:
result = 1
if content[4] == first & content[3] == second:
result = 1
count = count * 17
if operate == 'kp':
if first != second:
if (first + second) % 2 == 0:
result = 1
count = count * 5
if operate == 'qx':
if first + second == 7:
result = 1
count = count * 5
if operate == 'dd':
if first % 2 == 1:
if second % 2 == 1:
result = 1
count = count * 3
if operate == 'sx':
sumofrandom = first + second
if sumofrandom % 2 == 1:
result = 1
count = count * 2
if result == 1:
print("{} has win the game at {}.".format(address, now.strftime("%Y-%m-%d %H:%M:%S")))
else:
print("{} has lose the game at {}".format(address, now.strftime("%Y-%m-%d %H:%M:%S")))
# 將結果發送回客戶端
return encodeoperate([first, second, result, count])
if __name__ == '__main__':
# 下面這個是創建tcp連接
tcpCliSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpCliSock.bind(ADDR)
tcpCliSock.listen(16)
# 服務開始運行
print("The sever is beginning")
while True:
# 此行代碼會阻塞,將一直等待別人的連接
clientTarget, addr = tcpCliSock.accept()
socketList.append(clientTarget)
print("{} has connected".format(addr))
# 每當客戶端連接後啓動一個線程爲該客戶端服務
threading.Thread(target=targetoperate, args=(clientTarget, addr)).start()