Commit c8746b10 authored by EN1AK's avatar EN1AK Committed by GitHub

Add files via upload

parent 8fcf3768
...@@ -9,33 +9,7 @@ db = None ...@@ -9,33 +9,7 @@ db = None
target_row = None target_row = None
app.secret_key = "你自己的随机 Secret Key" app.secret_key = "你自己的随机 Secret Key"
# --- 与原 CLI 版本相同的配置 --- from map import SETNAME_MAP,RACE_MAP,TYPE_MAP,CATEGORY_TAGS,TYPE_LINK,LINK_MARKERS,SETNAME_MAP ,ATTR_MAP
ATTR_MAP = {
0x01: "地", 0x02: "水", 0x04: "炎", 0x08: "风",
0x10: "光", 0x20: "暗", 0x40: "神"
}
RACE_MAP = {
0x1: "战士", 0x2: "魔法师", 0x4: "天使", 0x8: "恶魔", 0x10: "不死", 0x20: "机械",
0x40: "水", 0x80: "炎", 0x100: "岩石", 0x200: "鸟兽", 0x400: "植物", 0x800: "昆虫",
0x1000: "雷", 0x2000: "龙", 0x4000: "兽", 0x8000: "兽战士", 0x10000: "恐龙",
0x20000: "鱼", 0x40000: "海龙", 0x80000: "爬虫", 0x100000: "念动力", 0x200000: "幻神兽",
0x400000: "创造神", 0x800000: "幻龙", 0x1000000: "电子界", 0x2000000: "幻想魔",
}
TYPE_MAP = {
0x1: "怪兽", 0x2: "魔法", 0x4: "陷阱", 0x10: "通常", 0x20: "效果", 0x40: "融合", 0x80: "仪式",
0x100: "陷阱怪兽", 0x200: "灵魂", 0x400: "同盟", 0x800: "二重", 0x1000: "调整", 0x2000: "同调",
0x4000: "衍生物", 0x10000: "速攻", 0x20000: "永续", 0x40000: "装备", 0x80000: "场地",
0x100000: "反击", 0x200000: "翻转", 0x400000: "卡通", 0x800000: "超量",
0x1000000: "灵摆", 0x2000000: "特殊召唤", 0x4000000: "连接"
}
CATEGORY_TAGS = {
1100: '魔陷破坏', 1101: '怪兽破坏', 1102: '卡片除外', 1103: '送去墓地', 1104: '返回手卡', 1105: '返回卡组',
1106: '手卡破坏', 1107: '卡组破坏', 1108: '抽卡辅助', 1109: '卡组检索', 1110: '卡片回收', 1111: '表示形式',
1112: '控制权', 1113: '攻守变化', 1114: '穿刺伤害', 1115: '多次攻击', 1116: '攻击限制', 1117: '直接攻击',
1118: '特殊召唤', 1119: '衍生物', 1120: '种族相关', 1121: '属性相关', 1122: 'LP伤害', 1123: 'LP回复',
1124: '破坏耐性', 1125: '效果耐性', 1126: '指示物', 1127: '幸运', 1128: '融合相关', 1129: '同调相关',
1130: '超量相关', 1131: '效果无效'
}
def parse_flags(value, mapping): def parse_flags(value, mapping):
return [name for bit, name in mapping.items() if value & bit] return [name for bit, name in mapping.items() if value & bit]
...@@ -43,11 +17,38 @@ def parse_flags(value, mapping): ...@@ -43,11 +17,38 @@ def parse_flags(value, mapping):
def parse_category(cat): def parse_category(cat):
return [CATEGORY_TAGS[1100 + i] for i in range(64) if (cat >> i) & 1 and (1100 + i) in CATEGORY_TAGS] return [CATEGORY_TAGS[1100 + i] for i in range(64) if (cat >> i) & 1 and (1100 + i) in CATEGORY_TAGS]
def parse_setcode(setcode, name_map):
# 1. 转成大写十六进制字符串
hex_str = f"{setcode:X}"
# 2. 左侧补零,使长度成为 4 的倍数
pad_len = (-len(hex_str)) % 4
if pad_len:
hex_str = hex_str.zfill(len(hex_str) + pad_len)
# 3. 每 4 位一组
names = []
for i in range(0, len(hex_str), 4):
segment = hex_str[i:i+4]
# 全 0 的段跳过
if segment == "0000":
continue
code = int(segment, 16)
if code in name_map:
names.append(name_map[code])
return names
def extract_arrows(def_value):
"""
从 link_marker 的整数值中提取出 所有 生效的箭头符号,返回一个列表。
"""
return [sym for bit, sym in LINK_MARKERS.items() if def_value & bit]
def load_card_database(path): def load_card_database(path):
conn = sqlite3.connect(path) conn = sqlite3.connect(path)
# 先把两个表读进 DataFrame # 先把两个表读进 DataFrame
datas = pd.read_sql_query( datas = pd.read_sql_query(
"SELECT id, type, atk, def, level, race, attribute, category ,hot FROM datas", "SELECT id, type, atk, def, level, race, attribute, category ,hot,setcode FROM datas",
conn, index_col="id" conn, index_col="id"
) )
texts = pd.read_sql_query( texts = pd.read_sql_query(
...@@ -64,20 +65,44 @@ def load_card_database(path): ...@@ -64,20 +65,44 @@ def load_card_database(path):
return df return df
def card_to_tags(row): def card_to_tags(row):
is_link = bool(row["type"] & TYPE_LINK)
# 链接怪兽的“守备”清空
defense = "" if is_link else row["def"]
# 如果是链接怪兽,从 link_marker 提取箭头
arrows = extract_arrows(row["def"]) if is_link else []
return { return {
"卡名": row["name"], "卡名": row["name"],
"攻击": row["atk"], "攻击": row["atk"],
"守备": row["def"], "守备": defense,
"等级": row["level"] & 0xFF, "等级": row["level"] & 0xFF,
"箭头": arrows,
"刻度": (row["level"] >> 24) & 0xFF, "刻度": (row["level"] >> 24) & 0xFF,
"类型": parse_flags(row["type"], TYPE_MAP), "类型": parse_flags(row["type"], TYPE_MAP),
"属性": ATTR_MAP.get(row["attribute"], f"0x{row['attribute']:X}"), "属性": ATTR_MAP.get(row["attribute"], f"0x{row['attribute']:X}"),
"种族": RACE_MAP.get(row["race"], f"0x{row['race']:X}"), "种族": RACE_MAP.get(row["race"], f"0x{row['race']:X}"),
"效果标签": parse_category(row["category"]) "效果标签": parse_category(row["category"]),
"系列": parse_setcode(row["setcode"], SETNAME_MAP),
} }
def compare_tags(guess_tags, answer_tags): def compare_tags(guess_tags, answer_tags):
def cmp(key, val1, val2): def cmp(key, val1, val2):
if val1 is None or val1 == "" or val2 is None or val2 == "":
# 要么是用户没猜,要么目标也无该字段,都算“未猜”
return '<span class="partial">—</span>'
if key == "箭头":
pills = []
# 对八个方向都展示一个小标签
for bit, sym in LINK_MARKERS.items():
if sym in val1:
# 猜的里有
cls = "tag-green" if sym in val2 else "tag-red"
else:
# 猜的里没有
cls = "tag-gray"
pills.append(f'<span class="tag {cls}">{sym}</span>')
return " ".join(pills)
# 数值型字段:攻击、守备、等级、刻度 # 数值型字段:攻击、守备、等级、刻度
if isinstance(val1, numbers.Number): if isinstance(val1, numbers.Number):
diff = abs(val1 - val2) diff = abs(val1 - val2)
...@@ -106,13 +131,13 @@ def compare_tags(guess_tags, answer_tags): ...@@ -106,13 +131,13 @@ def compare_tags(guess_tags, answer_tags):
pills = [] pills = []
for t in val1: for t in val1:
# 猜的 tag 在目标里才 green,否则 red # 猜的 tag 在目标里才 green,否则 red
cls = "tag-green" if t in val2 else "tag-red" cls = "tag-green" if t in val2 else "tag-gray"
pills.append(f'<span class="tag {cls}">{t}</span>') pills.append(f'<span class="tag {cls}">{t}</span>')
return " ".join(pills) or '<span class="tag tag-gray">—</span>' return " ".join(pills) or '<span class="tag tag-gray">—</span>'
# 其它(字符串等)完全匹配才 green,否则 gray # 其它(字符串等)完全匹配才 green,否则 gray
else: else:
cls = "tag-green" if val1 == val2 else "tag-gray" cls = "tag-green" if val1 == val2 else "tag-gray "
return f'<span class="tag {cls}">{val1}</span>' return f'<span class="tag {cls}">{val1}</span>'
return { return {
...@@ -150,6 +175,7 @@ def start(): ...@@ -150,6 +175,7 @@ def start():
# 随机选一个 target_id # 随机选一个 target_id
pool = filter_db(mode) pool = filter_db(mode)
session['target_id'] = int(pool.sample(1).index[0]) session['target_id'] = int(pool.sample(1).index[0])
#session['target_id'] = 71818935
# 重置本局提示相关状态 # 重置本局提示相关状态
session['guess_count'] = 0 session['guess_count'] = 0
session['hints_shown'] = [] session['hints_shown'] = []
...@@ -160,9 +186,16 @@ def start(): ...@@ -160,9 +186,16 @@ def start():
def game(): def game():
feedback = None feedback = None
mode = session.get('mode') mode = session.get('mode')
if not mode or 'target_id' not in session: if not mode :
return redirect(url_for("start")) return redirect(url_for("start"))
if 'target_id' not in session:
pool = filter_db(mode)
session['target_id'] = int(pool.sample(1).index[0])
session['history'] = []
session['hints'] = []
session['hinted_chars'] = []
filtered = filter_db(mode) filtered = filter_db(mode)
target = db.loc[session['target_id']] target = db.loc[session['target_id']]
...@@ -174,6 +207,13 @@ def game(): ...@@ -174,6 +207,13 @@ def game():
if request.method == "POST": if request.method == "POST":
action = request.form.get("action", "guess") action = request.form.get("action", "guess")
if action == "change_mode":
new_mode = request.form.get("mode")
session['mode'] = new_mode
# 直接把上一行 target_id 删掉,触发上面自动重置
session.pop('target_id', None)
return redirect(url_for("game"))
if action == "surrender": if action == "surrender":
# 认输 # 认输
feedback = {"giveup": True, "answer": target["name"], "hints": hints} feedback = {"giveup": True, "answer": target["name"], "hints": hints}
...@@ -189,7 +229,7 @@ def game(): ...@@ -189,7 +229,7 @@ def game():
session.pop('history', None) session.pop('history', None)
session.pop('hints', None) session.pop('hints', None)
session.pop('hinted_chars', None) session.pop('hinted_chars', None)
return redirect(url_for("start")) return redirect(url_for("game"))
else: else:
# 普通猜测 # 普通猜测
...@@ -202,9 +242,17 @@ def game(): ...@@ -202,9 +242,17 @@ def game():
else: else:
guess = match.iloc[0] guess = match.iloc[0]
if guess.name == target.name: if guess.name == target.name:
# 猜中 # 1. 先做一次对比
compare = compare_tags(card_to_tags(guess), card_to_tags(target))
# 2. 把这条全绿记录追加到本局历史
history.append({
"guess_name": guess['name'],
"compare": compare
})
# 3. 带上 compare 和 hints 给模板渲染
feedback = { feedback = {
"success": f"🎉 恭喜你猜中了!答案就是【{guess['name']}】", "success": f"🎉 恭喜你猜中了!答案就是【{guess['name']}】",
"compare": compare,
"hints": hints "hints": hints
} }
# 清理本局 session # 清理本局 session
...@@ -258,7 +306,8 @@ def game(): ...@@ -258,7 +306,8 @@ def game():
return render_template("index.html", return render_template("index.html",
feedback=feedback, feedback=feedback,
history=history, history=history,
hints=hints) hints=hints,
mode=mode)
@app.route("/suggest") @app.route("/suggest")
def suggest(): def suggest():
......
This diff is collapsed.
...@@ -34,20 +34,85 @@ ...@@ -34,20 +34,85 @@
.partial { background-color: #fff9c4; } .partial { background-color: #fff9c4; }
.mismatch { background-color: #ffcdd2; } .mismatch { background-color: #ffcdd2; }
button { margin-left: 8px; } button { margin-left: 8px; }
.hint-box {
margin: 1em 0;
padding: 0.5em;
background: #fff3cd;
border: 1px solid #ffeeba;
border-radius: 4px;
color: #856404;
}
.arrow-grid {
display: grid;
grid-template-rows: repeat(3, 1.5em);
grid-template-columns: repeat(3, 1.5em);
gap: 2px;
}
.arrow-cell {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2em;
border: 1px solid #ccc;
border-radius: 4px;
}
.arrow-cell.empty {
border: none;
background: transparent;
}
.arrow-cell.cell-green { background: #c8e6c9; }
.arrow-cell.cell-red { background: #ffcdd2; }
.arrow-cell.cell-black { background: #eeeeee; }
.tag {
display: inline-block;
padding: 2px 6px;
margin: 1px;
border-radius: 4px;
font-size: 0.9em;
}
.tag-green { background: #c8e6c9; }
.tag-yellow { background: #fff9c4; }
.tag-gray { background: #eee; }
.tag-red { background: #ffcdd2; }
</style> </style>
</head> </head>
<body> <body>
<h1>🎴 游戏王CCB</h1> <h1>🎴 游戏王CCB</h1>
<form method="POST" style="margin-bottom:1em;">
<label>题库:
<select name="mode">
<option value="monster" {% if mode =='monster' %}selected{% endif %}>
怪兽卡(无通常)
</option>
<option value="spell" {% if mode =='spell' %}selected{% endif %}>
魔法卡
</option>
<option value="trap" {% if mode =='trap' %}selected{% endif %}>
陷阱卡
</option>
<option value="hot" {% if mode =='hot' %}selected{% endif %}>
热门卡
</option>
<option value="all" {% if mode =='all' %}selected{% endif %}>
所有卡
</option>
</select>
</label>
<button type="submit" name="action" value="change_mode">切换题库</button>
</form>
<!-- 历史猜测 --> <!-- 历史猜测 -->
{% if history %} {% if history %}
<h2>历史猜测</h2> <h2>历史猜测</h2>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>卡名</th><th>攻击</th><th>守备</th><th>等级</th><th>刻度</th> <th>卡名</th><th>攻击</th><th>守备</th><th>等级</th><th>刻度</th><th>箭头</th>
<th>类型</th><th>属性</th><th>种族</th><th>效果标签</th> <th>类型</th><th>属性</th><th>种族</th><th>效果标签</th><th>系列</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -58,10 +123,12 @@ ...@@ -58,10 +123,12 @@
<td>{{ entry.compare['守备']|safe }}</td> <td>{{ entry.compare['守备']|safe }}</td>
<td>{{ entry.compare['等级']|safe }}</td> <td>{{ entry.compare['等级']|safe }}</td>
<td>{{ entry.compare['刻度']|safe }}</td> <td>{{ entry.compare['刻度']|safe }}</td>
<td>{{ entry.compare['箭头']|safe }}</td>
<td style="white-space: nowrap;">{{ entry.compare['类型']|safe }}</td> <td style="white-space: nowrap;">{{ entry.compare['类型']|safe }}</td>
<td style="white-space: nowrap;">{{ entry.compare['属性']|safe }}</td> <td style="white-space: nowrap;">{{ entry.compare['属性']|safe }}</td>
<td style="white-space: nowrap;">{{ entry.compare['种族']|safe }}</td> <td style="white-space: nowrap;">{{ entry.compare['种族']|safe }}</td>
<td style="white-space: nowrap;">{{ entry.compare['效果标签']|safe }}</td> <td style="white-space: nowrap;">{{ entry.compare['效果标签']|safe }}</td>
<td style="white-space: nowrap;">{{ entry.compare['系列']|safe }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
...@@ -78,6 +145,14 @@ ...@@ -78,6 +145,14 @@
<ul id="suggestions" hidden></ul> <ul id="suggestions" hidden></ul>
</form> </form>
{% if hints %}
<div class="hint-box">
{% for h in hints %}
<p>{{ h }}</p>
{% endfor %}
</div>
{% endif %}
<!-- 本次反馈 --> <!-- 本次反馈 -->
{% if feedback %} {% if feedback %}
{% if feedback.error %} {% if feedback.error %}
...@@ -87,9 +162,6 @@ ...@@ -87,9 +162,6 @@
{% elif feedback.giveup %} {% elif feedback.giveup %}
<p style="color:blue; font-weight:bold;">💡 放弃了!正确答案是:{{ feedback.answer }}</p> <p style="color:blue; font-weight:bold;">💡 放弃了!正确答案是:{{ feedback.answer }}</p>
{% elif feedback.compare %} {% elif feedback.compare %}
{% if feedback.hint %}
<p style="color:blue;">💡 提示:卡名中包含 “{{ feedback.hint }}” 这个字</p>
{% endif %}
<h3>你猜的是:<strong>{{ feedback.guess_name }}</strong></h3> <h3>你猜的是:<strong>{{ feedback.guess_name }}</strong></h3>
<table> <table>
<thead><tr><th>属性</th><th>结果</th></tr></thead> <thead><tr><th>属性</th><th>结果</th></tr></thead>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment