Skip to content

Commit b4260ca

Browse files
committed
commit #1
1 parent 857b195 commit b4260ca

File tree

5 files changed

+146
-42
lines changed

5 files changed

+146
-42
lines changed

baseplayer.py

+70-12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def __init__(self, sender):
88
self.user_id = sender["user_id"]
99
self.name = sender.get("card") or sender["nickname"]
1010
self.role = "unassigned"
11+
self.player_id = None
1112

1213
# def __hash__(self):
1314
# return hash(self.user_id)
@@ -19,6 +20,14 @@ def assignrole(self, role):
1920
def rolename(self):
2021
return self.ROLE[self.role]
2122

23+
@property
24+
def string(self):
25+
return f"[{self.player_id}] {self.name}"
26+
27+
@classmethod
28+
def updaterolenames(cls, roles: dict):
29+
cls.ROLE.update(roles)
30+
2231

2332
class BaseCampaign:
2433
PlayerConfig = dict()
@@ -40,26 +49,43 @@ def playernum(self):
4049
"""返回玩家数量"""
4150
return len(self.players)
4251

52+
@property
53+
def allplayerset(self):
54+
"""返回是否已收到所有玩家的指令"""
55+
return all((cmd is not None for cmd in self.commands.values()))
56+
4357
def assignroles(self):
44-
"""给玩家分配角色"""
58+
"""给玩家分配角色,并打乱座次"""
4559
if self.playernum not in self.PlayerConfig:
46-
print("玩家数量不够或超出。")
60+
print("玩家数量不够或超出。") # TODO
4761
return
4862
roles = self.PlayerConfig[self.playernum].copy()
63+
player_ids = list(range(1, self.playernum + 1))
4964
random.shuffle(roles)
65+
random.shuffle(player_ids)
5066
for i, p in enumerate(self.players):
5167
p.assignrole(roles[i])
68+
p.player_id = player_ids[i]
69+
self.players.sort(key=lambda p: p.player_id)
5270

5371
def _start(self):
5472
yield NotImplemented
5573
raise NotImplementedError
5674

5775
def resume(self):
58-
return next(self._game)
76+
try:
77+
return next(self._game)
78+
except StopIteration:
79+
print("游戏结束") # TODO
80+
return None
5981

6082
def addprivatemsg(self, player, msg):
6183
self.messages.append(({"user_id": player.user_id}, msg))
6284

85+
def addprivatemsgforall(self, msg):
86+
for p in self.players:
87+
self.addprivatemsg(p, msg)
88+
6389
def addgroupmsg(self, msg):
6490
self.messages.append(({"group_id": self.group_id}, msg))
6591

@@ -69,19 +95,51 @@ def yieldmessages(self):
6995
return messages
7096

7197
def acceptcommandfrom(self, acceptedplayers, commandparsers):
72-
if isinstance(acceptedplayers, str) and acceptedplayers == "all":
73-
self.acceptedplayers = self.players
74-
elif isinstance(acceptedplayers, (set, tuple, list)):
75-
self.acceptedplayers = acceptedplayers
98+
"""设置允许接收指令的玩家。
99+
100+
Arguments:
101+
acceptedplayers {str, iterable[Player]} -- 允许发送指令的玩家,'all'代表所有玩家
102+
commandparsers {callable, iterable[callable]} -- 设置玩家指令的解析器,单个代表解析器设置给所有玩家
103+
"""
104+
if isinstance(acceptedplayers, str) and acceptedplayers == "all": # 所有玩家
105+
acceptedplayers = self.players
106+
elif isinstance(acceptedplayers, (set, tuple, list)): # 部分玩家
107+
# acceptedplayers = acceptedplayers
108+
pass
76109
else:
77110
raise NotImplementedError
78-
self.commands = dict.fromkeys(acceptedplayers, None)
79-
if isinstance(commandparsers, callable):
80-
self.commandparsers = dict.fromkeys(acceptedplayers, commandparsers)
81-
elif isinstance(commandparsers, (tuple, list)):
82-
self.commandparsers = dict(zip(acceptedplayers, commandparsers))
111+
112+
self.acceptedplayers = (p.user_id for p in acceptedplayers) # 转换为QQ号(int)
113+
acceptedplayers = None # Debug: 防止下面代码误用
114+
115+
self.commands = dict.fromkeys(self.acceptedplayers, None) # 清空指令缓存
116+
117+
if callable(commandparsers): # 单个解析器分配给所有acceptedplayers
118+
self.commandparsers = dict.fromkeys(self.acceptedplayers, commandparsers)
119+
elif isinstance(commandparsers, (tuple, list)): # 解析器列表按顺序分配给acceptedplayers
120+
if len(self.acceptedplayers) != len(commandparsers): # 长度不匹配,抛出ValueError异常
121+
raise ValueError
122+
if isinstance(self.acceptedplayers, set): # 集合无顺序,抛出ValueError异常
123+
raise ValueError
124+
self.commandparsers = dict(zip(self.acceptedplayers, commandparsers))
83125
else:
84126
raise NotImplementedError
85127

86128
def handlemessage(self, context):
87129
raise NotImplementedError
130+
131+
@property
132+
def allplayerstring(self):
133+
return "\n".join((p.string for p in self.players))
134+
135+
def getplayerbyid(self, id):
136+
for p in self.players:
137+
if p.player_id == id:
138+
return p
139+
140+
def playerstringat(self, player_ids):
141+
if isinstance(player_ids, int):
142+
return self.getplayerbyid(player_ids).string
143+
144+
player_ids = list(player_ids).sort()
145+
return "\n".join((self.getplayerbyid(player_id) for player_id in player_ids))

bot.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
import asyncio
2-
import threading
3-
from time import sleep
4-
51
from aiocqhttp import CQHttp
62

73
from theresistance import TheResistanceCampaign
@@ -18,6 +14,8 @@ async def handle_msg(context):
1814

1915

2016
def test_setup():
17+
from time import sleep
18+
2119
campaign = TheResistanceCampaign(719231968)
2220
campaign.addplayer({"nickname": "一号玩家", "user_id": 498533576})
2321
campaign.addplayer({"nickname": "二号玩家", "user_id": 498533576})
@@ -27,21 +25,29 @@ def test_setup():
2725
campaign.addplayer({"nickname": "六号玩家", "user_id": 498533576})
2826
sendmessages(campaign.resume())
2927

28+
sleep(5)
29+
3030

3131
def sendmessages(messages):
32+
import threading
33+
3234
print(messages)
3335
for context, message in messages:
3436
threading.Thread(target=sendmessage, args=(context, message)).start()
3537

3638

3739
def sendmessage(context, message):
40+
import asyncio
41+
3842
print()
3943
print(context)
4044
print(message)
4145
asyncio.run(bot.send(context, message))
4246

4347

4448
if __name__ == "__main__":
49+
import threading
50+
4551
try:
4652
threading.Timer(5, test_setup).start()
4753
bot.run(host="127.0.0.1", port=8090)

test.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
1-
from theresistance import TheResistancePlayer
1+
from itertools import count
22

3-
player = TheResistancePlayer(498533576)
4-
print(player.ROLE)
3+
4+
class Test:
5+
counter = count()
6+
7+
def __init__(self):
8+
self.id = next(self.counter)
9+
10+
def __repr__(self):
11+
return f"Test({self.id})"
12+
13+
def __hash__(self):
14+
return hash(self.id)
15+
16+
17+
if __name__ == "__main__":
18+
t1 = Test()
19+
t2 = Test()
20+
di = {t1: "t1", t2: "t2"}
21+
print(t1)
22+
print(t2)
23+
print(di)
24+
print(di[t1])
25+
print(t1 in di)
26+
print(0 in di)

theresistance.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
{
2+
"ROLENAMES": {
3+
"blue": "抵抗组织",
4+
"red": "政府卧底"
5+
},
26
"ROLE_NOTIFICATION": "您的身份是【{rolename}】。",
37
"RED_PLAYER_NOTIFICATION": "政府卧底的玩家分别是:\n",
48
"GAME_START": "游戏开始!",
59
"THIS_ROUND_STARTUP": "这一轮的任务首领是{player.name}。\n请在讨论后选择{missionplayernum}名玩家执行第{gameround}次任务。",
6-
"LEADER_CHOOSE_PLAYER": "首领选择指派{}号玩家执行任务,请投票选择是否通过。",
10+
"LEADER_CHOOSE_PLAYER": "请回复玩家前的数字选择执行任务的玩家:\n{}",
11+
"VOTE_LEADER_CHOOSE_PLAYER": "首领选择指派以下玩家执行任务,请投票选择是否通过:\n{}",
712
"INVALID_MESSAGE": "没看懂你在说啥……",
813
"DUPLICATED_NUMBERS": "请不要选择重复的玩家。",
914
"VOTE_AGREE": [
@@ -18,5 +23,8 @@
1823
"不通过",
1924
"不是",
2025
""
21-
]
26+
],
27+
"VOTE_BEGIN": "请回复是否通过这一轮的投票。",
28+
"VOTE_NOT_PASSED": "这一轮投票未被通过,请继续讨论并由下一位首领选择执行任务的玩家。",
29+
"VOTE_PASSED": "投票通过!"
2230
}

theresistance.py

+31-21
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
from commandparser import getnumberparser, getboolparser, ErrorString
77

88
with open("theresistance.json", encoding="utf8") as f:
9-
texts = json.load(f)
9+
TEXT = json.load(f)
1010

1111

1212
class TheResistancePlayer(BasePlayer):
1313
pass
1414

1515

16-
TheResistancePlayer.ROLE.update({"blue": "抵抗组织", "red": "政府卧底"})
16+
TheResistancePlayer.updaterolenames(TEXT["ROLENAMES"])
1717

1818

1919
class TheResistanceCampaign(BaseCampaign):
@@ -57,13 +57,12 @@ def _start(self):
5757
"""游戏流程"""
5858
self.assignroles() # 分发身份
5959
for p in self.players: # 告诉玩家自己的身份
60-
self.addprivatemsg(
61-
p, texts["ROLE_NOTIFICATION"].format(rolename=p.rolename)
62-
)
63-
msg = texts["RED_PLAYER_NOTIFICATION"] + "\n".join(self.redplayername)
60+
self.addprivatemsg(p, TEXT["ROLE_NOTIFICATION"].format(rolename=p.rolename))
61+
62+
msg = TEXT["RED_PLAYER_NOTIFICATION"] + "\n".join(self.redplayername)
6463
for p in self.redplayers: # 告诉卧底玩家其它卧底身份
6564
self.addprivatemsg(p, msg)
66-
self.addgroupmsg(texts["GAME_START"])
65+
self.addgroupmsg(TEXT["GAME_START"])
6766
leadergen = cycle(self.players)
6867
for _ in range(random.randint(0, self.playernum - 1)): # 随机初始首领玩家
6968
next(leadergen)
@@ -73,38 +72,57 @@ def _start(self):
7372
missionrequire = self.MissionRequire[self.playernum] # 任务所需玩家数量
7473
for leader in leadergen: # 无限循环,用 break 跳出
7574
self.addgroupmsg(
76-
texts["THIS_ROUND_STARTUP"].format(
75+
TEXT["THIS_ROUND_STARTUP"].format(
7776
player=leader,
7877
gameround=gameround,
7978
missionplayernum=missionrequire[gameround - 1],
8079
)
8180
)
81+
self.addprivatemsg(
82+
leader, TEXT["LEADER_CHOOSE_PLAYER"].format(self.allplayerstring)
83+
)
8284
self.acceptcommandfrom(
8385
(leader,),
8486
getnumberparser(
8587
maxnum=self.playernum, commandnumber=missionrequire[gameround - 1]
8688
),
8789
)
8890
yield self.yieldmessages()
91+
8992
self.addgroupmsg(
90-
texts["LEADER_CHOOSE_PLAYER"].format("、".join(self.commands[leader]))
93+
TEXT["VOTE_LEADER_CHOOSE_PLAYER"].format(
94+
self.getplayerbyid(self.commands[leader])
95+
)
9196
)
97+
self.addprivatemsgforall(TEXT["VOTE_BEGIN"])
9298
self.acceptcommandfrom(
93-
"all", getboolparser(yes=texts["VOTE_AGREE"], no=texts["VOTE_DISAGREE"])
99+
"all", getboolparser(yes=TEXT["VOTE_AGREE"], no=TEXT["VOTE_DISAGREE"])
94100
)
95101
yield self.yieldmessages()
102+
103+
voteyescount = sum(self.commands.values()) # 通过的玩家数
104+
if voteyescount <= self.playernum / 2: # 如果一半及以上的玩家否决了本回合首领任命的人选
105+
self.addgroupmsg(TEXT["VOTE_NOT_PASSED"])
106+
continue
107+
108+
self.addgroupmsg(TEXT["VOTE_PASSED"])
109+
96110
# TODO: 游戏流程
97111

98112
def handlemessage(self, context):
99113
player = context["user_id"]
100114
if player not in self.acceptedplayers:
101115
return None
116+
102117
cmd = self.commandparsers[player](context["message"])
103118
if isinstance(cmd, ErrorString):
104-
return {"reply": texts[cmd]} # TODO
119+
return {"reply": TEXT[cmd]}
105120
else:
106121
self.commands[player] = cmd
107-
if all((cmd is not None for cmd in self.commands.values())): # 收到所有指令
122+
123+
# TODO: 回复指令已收到
124+
125+
if self.allplayerset: # 收到所有指令
108126
return self.resume()
109127
else:
110128
return None
@@ -127,12 +145,4 @@ def blueplayername(self):
127145

128146

129147
if __name__ == "__main__":
130-
missionrequire = TheResistanceCampaign.MissionRequire[6]
131-
gameround = 1
132-
print(
133-
texts["THIS_ROUND_STARTUP"].format(
134-
player=TheResistancePlayer({"nickname": "六号玩家", "user_id": 498533576}),
135-
gameround=gameround,
136-
missionplayernum=missionrequire[gameround - 1],
137-
)
138-
)
148+
pass

0 commit comments

Comments
 (0)