Spaces:
Runtime error
Runtime error
sci-m-wang
commited on
Commit
•
2dc48af
1
Parent(s):
523104e
Upload 3 files
Browse files- prompt.txt +46 -0
- who_is_the_spy.py +226 -0
- word_list.json +52 -0
prompt.txt
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Role: 谁是卧底游戏玩家
|
2 |
+
|
3 |
+
## Profile
|
4 |
+
- author: Mingle
|
5 |
+
- version: 0.1
|
6 |
+
- language: 中文
|
7 |
+
- description: 谁是卧底游戏玩家,能够用简短的一句话描述自己得到的关键词,分析场上的描述历史以判断自己和其他玩家的身份,投票敌对玩家出局,并在发现自己是卧底时伪装成平民身份。
|
8 |
+
|
9 |
+
## Background
|
10 |
+
- 你是“谁是卧底”游戏中的一个玩家;
|
11 |
+
- 你获得的关键词是:{};
|
12 |
+
|
13 |
+
## Skills
|
14 |
+
- 熟悉“谁是卧底”的游戏规则,了解游戏的胜利条件;
|
15 |
+
- 了解“谁是卧底”游戏的制胜技巧;
|
16 |
+
|
17 |
+
## Commands
|
18 |
+
- /describe:用简短的一句话描述自己得到的关键词,禁止直接说出关键词。
|
19 |
+
- /vote:从玩家列表中选择一个,得票最多的玩家将会被投票出局。
|
20 |
+
|
21 |
+
## Constraints
|
22 |
+
- 不能直接说出或暗示自己的关键词;
|
23 |
+
- 描述的内容必须符合关键词;
|
24 |
+
- 描述的内容不能与已有的描述相同;
|
25 |
+
- 在不确定自己的身份时,描述应该尽可能模糊,避免暴露;
|
26 |
+
- 描述内容必须小于20字,禁止输出与描述无关的额外内容;
|
27 |
+
- 投票时只能回复玩家id,不能输出任何额外内容;
|
28 |
+
|
29 |
+
## Workflows
|
30 |
+
1. 判断需要执行的动作
|
31 |
+
1.1 如果命令为"/describe",则需要描述关键词
|
32 |
+
a. 接收关键词,代表在游戏中的身份。
|
33 |
+
b. 构思一句话来描述自己的关键词。这句话应尽量模糊或广义,避免直接暴露具体信息,但也要足够合理,以免引起其他玩家的怀疑。
|
34 |
+
- 例如,如果关键词是“苹果”,玩家可以描述为:“这是一个很常见的水果。”
|
35 |
+
c. 分析其他玩家对其关键词的描述,尝试找出其中的模糊之处或与自己关键词的差异点。
|
36 |
+
- 注意关键词之间的微妙差异,例如“苹果”和“橙子”,可能有类似的描述,但在细节上会有区别。
|
37 |
+
d. 根据其他玩家的描述和场上的讨论情况,调整自己的策略,如果有必要,稍微修改自己的描述以避免暴露。
|
38 |
+
e. 判断自己是否是卧底,如果怀疑自己是卧底,在描述关键词时应更加小心,尽量确保描述内容符合卧底关键词并符合你判断出的平民关键词,避免被其他玩家识破。
|
39 |
+
f. 生成最终不超过20字的描述,避免直接暴露关键词。
|
40 |
+
1.2 如果命令为"/vote",则需要投票,将你认为敌对阵营的玩家投票出局
|
41 |
+
a. 接收所有玩家的描述历史记录;
|
42 |
+
- 特别关注与自己描述相似的玩家,这些玩家有可能是同一阵营。
|
43 |
+
b. 分析历史记录,描述比较模糊的玩家,尤其是描述与自己的关键词存在明显不同的玩家,就有可能是卧底;
|
44 |
+
c. 基于自己的分析,做出投票决定,从场上存活的玩家列表中,选出你认为敌对阵营的玩家;
|
45 |
+
d. 回复投票玩家的id,不要回复额外内容。
|
46 |
+
|
who_is_the_spy.py
ADDED
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from random import choices, randint
|
3 |
+
from openai import OpenAI
|
4 |
+
import json
|
5 |
+
|
6 |
+
"""
|
7 |
+
谁是卧底游戏
|
8 |
+
"""
|
9 |
+
|
10 |
+
state = st.session_state
|
11 |
+
|
12 |
+
# title
|
13 |
+
st.title("谁是卧底😎")
|
14 |
+
|
15 |
+
# read the word list from the json file
|
16 |
+
with open("word_list.json", "r") as f:
|
17 |
+
word_list = json.load(f)
|
18 |
+
pass
|
19 |
+
|
20 |
+
# define the avatar dict for the players
|
21 |
+
avatar_dict = {
|
22 |
+
"host": "🐼",
|
23 |
+
"P1": "🚀",
|
24 |
+
"P2": "🚄",
|
25 |
+
"P3": "🚁",
|
26 |
+
"P4": "🚂",
|
27 |
+
"P5": "🚢",
|
28 |
+
"P6": "🚤",
|
29 |
+
"P7": "🚙",
|
30 |
+
"P8": "🚠",
|
31 |
+
"P9": "🚲",
|
32 |
+
"P10": "🚜",
|
33 |
+
"H": "🤹♂️",
|
34 |
+
}
|
35 |
+
|
36 |
+
# define the state of the game and save some data
|
37 |
+
## 全局消息栈
|
38 |
+
if "messages" not in state:
|
39 |
+
state.messages = []
|
40 |
+
pass
|
41 |
+
## 玩家列表
|
42 |
+
if "players" not in state:
|
43 |
+
state.players = []
|
44 |
+
pass
|
45 |
+
## 玩家系统提示
|
46 |
+
if "prompt" not in state:
|
47 |
+
with open("prompt.txt", "r") as f:
|
48 |
+
state.prompt = f.read()
|
49 |
+
pass
|
50 |
+
pass
|
51 |
+
|
52 |
+
# create a new OpenAI client and define the generation function
|
53 |
+
if "client" not in state:
|
54 |
+
state.client = OpenAI(
|
55 |
+
api_key="internlm2",
|
56 |
+
base_url="http://0.0.0.0:23333/v1"
|
57 |
+
)
|
58 |
+
state.model_name = state.client.models.list().data[0].id
|
59 |
+
pass
|
60 |
+
def response(messages):
|
61 |
+
return state.client.chat.completions.create(
|
62 |
+
model=state.model_name,
|
63 |
+
messages=messages,
|
64 |
+
temperature=0.5,
|
65 |
+
).choices[0].message.content
|
66 |
+
|
67 |
+
# settings
|
68 |
+
## 记录轮次
|
69 |
+
if "max_round" not in state:
|
70 |
+
state.max_round = 100
|
71 |
+
pass
|
72 |
+
if "round" not in state:
|
73 |
+
state.round = 0
|
74 |
+
pass
|
75 |
+
## 侧边栏设置游戏,包括关键词,人数,回合数等
|
76 |
+
with st.sidebar:
|
77 |
+
st.write("游戏设置")
|
78 |
+
with st.form(key="game_setting"):
|
79 |
+
if "words" not in state:
|
80 |
+
words_num = randint(0, len(word_list)-1)
|
81 |
+
state.words = word_list[words_num]
|
82 |
+
total_num = st.number_input("总人数", 5, 10, 5)
|
83 |
+
spy_num = st.number_input("卧底人数", 1, total_num//2, 1)
|
84 |
+
max_round = st.number_input("最大回合数", 5, 10, 10)
|
85 |
+
|
86 |
+
submitted = st.form_submit_button("保存设置")
|
87 |
+
|
88 |
+
## 提交后保存设置,初始化玩家、消息栈等
|
89 |
+
if submitted:
|
90 |
+
# print("游戏设置已保存")
|
91 |
+
state.spy_word = state.words["spy_word"] # 卧底关键词
|
92 |
+
state.civilian_word = state.words["civilian_word"] # 平民关键词
|
93 |
+
state.total_num = total_num # 总人数
|
94 |
+
state.spy_num = spy_num # 卧底人数
|
95 |
+
state.max_round = max_round # 最大回合数
|
96 |
+
## 初始化玩家列表,人类玩家和AI玩家分开存
|
97 |
+
human_dignity = randint(0,1) # 人类玩家的身份,0: 平民 1: 卧底
|
98 |
+
if human_dignity == 0:
|
99 |
+
state.players = [{"id": "H", "dignity": "civilian"}]
|
100 |
+
st.write("你的关键词是{}".format(state.civilian_word))
|
101 |
+
pass
|
102 |
+
else:
|
103 |
+
state.players = [{"id": "H", "dignity": "spy"}]
|
104 |
+
st.write("你的关键词是{}".format(state.spy_word))
|
105 |
+
pass
|
106 |
+
state.players += [{"id":"P"+str(i+1)} for i in range(total_num-1)]
|
107 |
+
if human_dignity == 1 and state.spy_num-1 == 0:
|
108 |
+
spy_id = []
|
109 |
+
pass
|
110 |
+
else:
|
111 |
+
spy_id = choices([f"P{a}" for a in list(range(1,total_num))], k=state.spy_num)
|
112 |
+
pass
|
113 |
+
for each in state.players:
|
114 |
+
if each["id"] in spy_id:
|
115 |
+
each["dignity"] = "spy"
|
116 |
+
pass
|
117 |
+
else:
|
118 |
+
each["dignity"] = "civilian"
|
119 |
+
pass
|
120 |
+
pass
|
121 |
+
pass
|
122 |
+
pass
|
123 |
+
pass
|
124 |
+
|
125 |
+
# 消息显示窗口
|
126 |
+
with st.container(height=300):
|
127 |
+
for message in state.messages:
|
128 |
+
with st.chat_message(message["id"], avatar=avatar_dict[message["id"]]):
|
129 |
+
st.text(message["id"])
|
130 |
+
st.write(message["message"])
|
131 |
+
pass
|
132 |
+
# 游戏主体环节
|
133 |
+
if state.round < state.max_round:
|
134 |
+
if "description" not in state:
|
135 |
+
state.description = []
|
136 |
+
pass
|
137 |
+
## 控制游戏轮次及开始
|
138 |
+
start = st.button("开始第{}轮游戏".format(state.round+1))
|
139 |
+
if start:
|
140 |
+
if "round" not in state:
|
141 |
+
state.round = 0
|
142 |
+
pass
|
143 |
+
state.messages.append({"id":"host", "message":f"第{state.round+1}轮游戏开始"})
|
144 |
+
## 生成描述环节
|
145 |
+
for player in state.players:
|
146 |
+
## 如果是人类玩家,跳过
|
147 |
+
if player["id"] == "H":
|
148 |
+
continue
|
149 |
+
if player["dignity"] == "spy":
|
150 |
+
text = response([
|
151 |
+
{"role":"system", "content":state.prompt.format(state.spy_word)},
|
152 |
+
{"role":"system", "content":"/describe "+"请根据描述历史记录描述你的关键词,需要注意不能与已有的描述重复,下面是描述历史记录:\n"+"\n".join(state.description)}
|
153 |
+
])
|
154 |
+
else:
|
155 |
+
text = response([
|
156 |
+
{"role":"system", "content":state.prompt.format(state.civilian_word)},
|
157 |
+
{"role":"system", "content":"/describe "+"请根据描述历史记录描述你的关键词,下面是描述历史记录:\n"+"\n".join(state.description)}
|
158 |
+
])
|
159 |
+
pass
|
160 |
+
state.messages.append({"id":player["id"], "message": text})
|
161 |
+
state.description.append(player["id"] + ":" + text)
|
162 |
+
pass
|
163 |
+
st.rerun()
|
164 |
+
pass
|
165 |
+
## 投票环节, AI玩家生成回复,最后由人类玩家统计并选择投票对象
|
166 |
+
col1, col2 = st.columns([8,2])
|
167 |
+
with col1:
|
168 |
+
vote_id = st.selectbox("投票对象", [a["id"] for a in state.players])
|
169 |
+
pass
|
170 |
+
with col2:
|
171 |
+
if st.button("开始投票"):
|
172 |
+
for player in state.players:
|
173 |
+
if player["id"] == "H":
|
174 |
+
continue
|
175 |
+
text = response([
|
176 |
+
{"role":"system", "content":state.prompt.format(state.spy_word)},
|
177 |
+
{"role":"system", "content":"/vote "+"请根据描述历史记录选择要投出的玩家,下面是描述历史记录:\n"+"\n".join(state.description)+"\n"+"当前场上存活玩家id为:\n"+",".join([a["id"] for a in state.players])}
|
178 |
+
])
|
179 |
+
state.messages.append({"id":player["id"], "message": text})
|
180 |
+
pass
|
181 |
+
st.rerun()
|
182 |
+
pass
|
183 |
+
pass
|
184 |
+
if st.button("投出玩家"):
|
185 |
+
state.messages.append({"id":"host", "message":f"玩家{vote_id}被投票出局"})
|
186 |
+
state.round += 1
|
187 |
+
for player in state.players:
|
188 |
+
if player["id"] == vote_id:
|
189 |
+
state.players.remove(player)
|
190 |
+
break
|
191 |
+
pass
|
192 |
+
## 验证是否还有卧底存活
|
193 |
+
spy_live = False
|
194 |
+
for player in state.players:
|
195 |
+
if player["dignity"] == "spy":
|
196 |
+
spy_live = True
|
197 |
+
break
|
198 |
+
pass
|
199 |
+
if not spy_live and state.players:
|
200 |
+
state.messages.append({"id":"host", "message":"平民胜利!"})
|
201 |
+
pass
|
202 |
+
elif spy_live:
|
203 |
+
## 统计当前卧底人数,如果占据一半以上则卧底胜利
|
204 |
+
spy_num = 0
|
205 |
+
for player in state.players:
|
206 |
+
if player["dignity"] == "spy":
|
207 |
+
spy_num += 1
|
208 |
+
pass
|
209 |
+
pass
|
210 |
+
if spy_num >= len(state.players)//2:
|
211 |
+
state.messages.append({"id":"host", "message":"卧底胜利!"})
|
212 |
+
pass
|
213 |
+
st.rerun()
|
214 |
+
pass
|
215 |
+
human_live = False
|
216 |
+
for player in state.players:
|
217 |
+
if player["id"] == "H":
|
218 |
+
human_live = True
|
219 |
+
break
|
220 |
+
pass
|
221 |
+
if human_live:
|
222 |
+
if des := st.chat_input():
|
223 |
+
state.messages.append({"id":"H", "message":des})
|
224 |
+
state.description.append("H:"+des)
|
225 |
+
st.rerun()
|
226 |
+
|
word_list.json
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[
|
2 |
+
{"spy_word": "苹果", "civilian_word": "橙子"},
|
3 |
+
{"spy_word": "饺子", "civilian_word": "包子"},
|
4 |
+
{"spy_word": "公交车", "civilian_word": "地铁"},
|
5 |
+
{"spy_word": "铅笔", "civilian_word": "圆珠笔"},
|
6 |
+
{"spy_word": "面包", "civilian_word": "蛋糕"},
|
7 |
+
{"spy_word": "白菜", "civilian_word": "青菜"},
|
8 |
+
{"spy_word": "火车", "civilian_word": "高铁"},
|
9 |
+
{"spy_word": "自行车", "civilian_word": "电动车"},
|
10 |
+
{"spy_word": "西瓜", "civilian_word": "哈密瓜"},
|
11 |
+
{"spy_word": "鞋子", "civilian_word": "袜子"},
|
12 |
+
{"spy_word": "葡萄", "civilian_word": "提子"},
|
13 |
+
{"spy_word": "牛奶", "civilian_word": "酸奶"},
|
14 |
+
{"spy_word": "电脑", "civilian_word": "手机"},
|
15 |
+
{"spy_word": "冬天", "civilian_word": "夏天"},
|
16 |
+
{"spy_word": "红酒", "civilian_word": "白酒"},
|
17 |
+
{"spy_word": "台灯", "civilian_word": "吊灯"},
|
18 |
+
{"spy_word": "篮球", "civilian_word": "足球"},
|
19 |
+
{"spy_word": "洗衣机", "civilian_word": "烘干机"},
|
20 |
+
{"spy_word": "桌子", "civilian_word": "椅子"},
|
21 |
+
{"spy_word": "手表", "civilian_word": "钟表"},
|
22 |
+
{"spy_word": "书包", "civilian_word": "钱包"},
|
23 |
+
{"spy_word": "枕头", "civilian_word": "被子"},
|
24 |
+
{"spy_word": "雨伞", "civilian_word": "雨衣"},
|
25 |
+
{"spy_word": "咖啡", "civilian_word": "茶"},
|
26 |
+
{"spy_word": "牙刷", "civilian_word": "牙膏"},
|
27 |
+
{"spy_word": "饼干", "civilian_word": "面包"},
|
28 |
+
{"spy_word": "收音机", "civilian_word": "电视"},
|
29 |
+
{"spy_word": "打印机", "civilian_word": "复印机"},
|
30 |
+
{"spy_word": "口香糖", "civilian_word": "薄荷糖"},
|
31 |
+
{"spy_word": "牛排", "civilian_word": "烤肉"},
|
32 |
+
{"spy_word": "汤", "civilian_word": "粥"},
|
33 |
+
{"spy_word": "花生", "civilian_word": "核桃"},
|
34 |
+
{"spy_word": "漫画", "civilian_word": "动画"},
|
35 |
+
{"spy_word": "日历", "civilian_word": "时钟"},
|
36 |
+
{"spy_word": "糖果", "civilian_word": "巧克力"},
|
37 |
+
{"spy_word": "吉他", "civilian_word": "小提琴"},
|
38 |
+
{"spy_word": "空调", "civilian_word": "风扇"},
|
39 |
+
{"spy_word": "牛仔裤", "civilian_word": "短裤"},
|
40 |
+
{"spy_word": "咖喱", "civilian_word": "火锅"},
|
41 |
+
{"spy_word": "面条", "civilian_word": "米饭"},
|
42 |
+
{"spy_word": "耳机", "civilian_word": "音响"},
|
43 |
+
{"spy_word": "摩托车", "civilian_word": "电动车"},
|
44 |
+
{"spy_word": "键盘", "civilian_word": "鼠标"},
|
45 |
+
{"spy_word": "钟", "civilian_word": "表"},
|
46 |
+
{"spy_word": "口红", "civilian_word": "唇膏"},
|
47 |
+
{"spy_word": "蛋挞", "civilian_word": "蛋糕"},
|
48 |
+
{"spy_word": "香水", "civilian_word": "花露水"},
|
49 |
+
{"spy_word": "西装", "civilian_word": "礼服"},
|
50 |
+
{"spy_word": "毛巾", "civilian_word": "浴巾"},
|
51 |
+
{"spy_word": "电饭锅", "civilian_word": "微波炉"}
|
52 |
+
]
|