Commit 398d2925 authored by EN1AK's avatar EN1AK Committed by GitHub

Add files via upload

parent 2f291359
......@@ -3,7 +3,7 @@ import pandas as pd
import numbers
from pathlib import Path
import sys
from map import RACE_MAP, TYPE_MAP, CATEGORY_TAGS, TYPE_LINK, LINK_MARKERS, SETNAME_MAP, ATTR_MAP, TYPE_PENDULUM
from map import RACE_MAP, TYPE_MAP, CATEGORY_TAGS, TYPE_LINK, LINK_MARKERS, SETNAME_MAP, ATTR_MAP, TYPE_PENDULUM,TYPE_MONSTER
def parse_flags(value, mapping):
......@@ -91,23 +91,42 @@ def load_card_database(path: str = None) -> pd.DataFrame:
def card_to_tags(row):
type_names = parse_flags(row["type"], TYPE_MAP)
is_link = bool(row["type"] & TYPE_LINK)
is_pendulum = bool(row["type"] & TYPE_PENDULUM)
# 链接怪兽的“守备”清空
defense = "" if is_link else row["def"]
is_monster = bool(row["type"] & TYPE_MONSTER)
if not is_monster:
atk_val = ""
def_val = ""
level = ""
scale = ""
attr = ""
race = ""
else:
# 怪兽卡才处理 -2 → “?”
atk_val = "?" if row["atk"] == -2 else row["atk"]
# 链接怪兽没有守备,其它怪兽按 -2 转换
if is_link:
def_val = ""
else:
def_val = "?" if row["def"] == -2 else row["def"]
# 等级/阶级
level = row["level"] & 0xFF
# 刻度只有灵摆怪兽才有
scale = (row["level"] >> 24) & 0xFF if is_pendulum else ""
attr = ATTR_MAP.get(row["attribute"], f"0x{row['attribute']:X}")
race = RACE_MAP.get(row["race"], f"0x{row['race']:X}")
arrows = extract_arrows(row["def"]) if is_link else []
scale = (row["level"] >> 24) & 0xFF if is_pendulum else ""
return {
"卡名": row["name"],
"攻击": row["atk"],
"守备": defense,
"等级/阶级": row["level"] & 0xFF,
"攻击": atk_val,
"守备": def_val,
"等级/阶级": level,
"箭头": arrows,
"刻度": scale,
"类型": parse_flags(row["type"], TYPE_MAP),
"属性": ATTR_MAP.get(row["attribute"], f"0x{row['attribute']:X}"),
"种族": RACE_MAP.get(row["race"], f"0x{row['race']:X}"),
"刻度": scale,
"类型": type_names,
"属性": attr,
"种族": race,
"效果标签": parse_category(row["category"]),
"系列": parse_setcode(row["setcode"], SETNAME_MAP),
}
......@@ -117,9 +136,14 @@ def compare_tags(guess_tags, answer_tags):
def cmp(key, val1, val2):
if (val1 == "" or val1 is None) and (val2 == "" or val2 is None):
return '<span class="tag tag-gray">—</span>'
# 如果其中一个没——黄色“部分”
if val1 == "" or val1 is None or val2 == "" or val2 is None:
return '<span class="partial">—</span>'
if (val1 == "" or val1 is None) and (val2 != "" or val2 is not None):
num = val1
return '<span class="tag tag-gray">—</span>'
if (val1 != "" or val1 is not None) and (val2 == "" or val2 is None):
num = val2
return f'<span class="tag tag-gray">{num}</span>'
if key == "箭头":
pills = []
......
......@@ -22,7 +22,6 @@ def filter_db(mode):
mode: 'monster' | 'spell' | 'trap' | 'hot' | 'all'
"""
if mode == 'monster':
# 怪兽卡 & 排除通常怪兽
mask = ((db['type'] & 0x1) > 0) & ((db['type'] & 0x10) == 0)
return db[mask]
if mode == 'spell':
......@@ -98,6 +97,7 @@ def game():
session['mode'] = new_mode
# 直接把上一行 target_id 删掉,触发上面自动重置
session.pop('target_id', None)
session.pop('guess_count', None)
return redirect(url_for("game"))
if action == "surrender":
......@@ -115,6 +115,7 @@ def game():
session.pop('history', None)
session.pop('hints', None)
session.pop('hinted_chars', None)
session.pop('guess_count', None)
elif action == "restart":
# 重新开始
......@@ -123,22 +124,35 @@ def game():
session.pop('history', None)
session.pop('hints', None)
session.pop('hinted_chars', None)
session.pop('guess_count', None)
return redirect(url_for("game"))
else:
# 普通猜测
guess_count += 1
guess_count = session.get('guess_count', 0) + 1
session['guess_count'] = guess_count
if guess_count > max_attempts:
feedback = {
"error": f"😢 猜测次数已用尽!答案是【{target['name']}】",
"giveup": True,
"answer": target["name"],
"hints": hints
}
for key in ('target_id', 'history', 'hints', 'hinted_chars', 'guess_count'):
session.pop(key, None)
user_input = request.form.get("guess", "").strip()
guess_id = request.form.get("guess_id")
if guess_id:
try:
guess = db.loc[int(guess_id)]
except Exception:
guess = None
feedback = {"error": "无效的卡片选择。", "hints": hints}
else:
user_input = request.form.get("guess", "").strip()
match = filtered[filtered["name"]
.str.contains(user_input, case=False, na=False)]
if match.empty:
guess = None
feedback = {"error": f"未找到包含“{user_input}”的卡片。", "hints": hints}
else:
guess = match.iloc[0]
# 如果 guess 还是 None,直接跳过下面逻辑
if guess is None:
return render_template(
"index.html",
feedback=feedback,
......@@ -146,17 +160,9 @@ def game():
hints=hints,
mode=mode,
guess_count=guess_count,
max_attempts=max_attempts
max_attempts=max_attempts,
)
user_input = request.form.get("guess", "").strip()
match = filtered[filtered["name"].str.contains(user_input, case=False, na=False)]
if match.empty:
feedback = {"error": f"未找到包含“{user_input}”的卡片。", "hints": hints}
else:
guess = match.iloc[0]
if guess.name == target.name:
# 1. 先做一次对比
compare = compare_tags(card_to_tags(guess), card_to_tags(target))
......@@ -176,48 +182,59 @@ def game():
session.pop('history', None)
session.pop('hints', None)
session.pop('hinted_chars', None)
session.pop('guess_count', None)
else:
# 对比并入历史
compare = compare_tags(card_to_tags(guess), card_to_tags(target))
history.append({
"guess_name": guess['name'],
"compare": compare
})
# —— 第二次猜测,给一个新的“效果标签”提示 —— #
if len(history) == 2:
target_tags = set(card_to_tags(target)["效果标签"])
guessed_tags = set()
for h in history:
# history 里保存的 compare 里没有原始 list,
# 所以直接重新取一次 guess 的原始标签:
row = db[db["name"] == h["guess_name"]].iloc[0]
guessed_tags |= set(card_to_tags(row)["效果标签"])
remaining = list(target_tags - guessed_tags)
if remaining:
tag_hint = random.choice(remaining)
hints.append(f"提示:目标卡有效果标签 “{tag_hint}”")
# —— 第五次猜测,给一个新的名称字符提示 —— #
if len(history) == 5:
name_chars = [c for c in target["name"] if c.strip()]
candidates = [c for c in name_chars if c not in hinted_chars]
if candidates:
char_hint = random.choice(candidates)
hinted_chars.append(char_hint)
hints.append(f"提示:目标卡名称中包含 “{char_hint}” 这个字")
# 更新 session
session['history'] = history
session['hints'] = hints
session['hinted_chars'] = hinted_chars
feedback = {
"compare": compare,
"guess_name": guess['name'],
"hints": hints
}
if guess_count >= max_attempts:
feedback = {
"error": f"😢 猜测次数已用尽!答案是【{target['name']}】",
"giveup": True,
"answer": target["name"],
"hints": hints
}
for key in ('target_id', 'history', 'hints', 'hinted_chars', 'guess_count'):
session.pop(key, None)
else:
compare = compare_tags(card_to_tags(guess), card_to_tags(target))
history.append({
"guess_name": guess['name'],
"compare": compare
})
# —— 第二次猜测,给一个新的“效果标签”提示 —— #
if len(history) == 2:
target_tags = set(card_to_tags(target)["效果标签"])
guessed_tags = set()
for h in history:
# history 里保存的 compare 里没有原始 list,
# 所以直接重新取一次 guess 的原始标签:
row = db[db["name"] == h["guess_name"]].iloc[0]
guessed_tags |= set(card_to_tags(row)["效果标签"])
remaining = list(target_tags - guessed_tags)
if remaining:
tag_hint = random.choice(remaining)
hints.append(f"提示:目标卡有效果标签 “{tag_hint}”")
# —— 第五次猜测,给一个新的名称字符提示 —— #
if len(history) == 5:
name_chars = [c for c in target["name"] if c.strip()]
candidates = [c for c in name_chars if c not in hinted_chars]
if candidates:
char_hint = random.choice(candidates)
hinted_chars.append(char_hint)
hints.append(f"提示:目标卡名称中包含 “{char_hint}” 这个字")
# 更新 session
session['history'] = history
session['hints'] = hints
session['hinted_chars'] = hinted_chars
feedback = {
"compare": compare,
"guess_name": guess['name'],
"hints": hints
}
return render_template(
"index.html",
......@@ -237,11 +254,10 @@ def suggest():
return jsonify([])
mode = session.get('mode', 'all')
pool = filter_db(mode)
matches = pool[
pool["name"].str.contains(q, case=False, na=False)
]["name"].tolist()
return jsonify(matches)
# 只取 name 中包含 q 的行,并把 id 和 name 拼成字典列表
df = pool[pool["name"].str.contains(q, case=False, na=False)][["name"]].reset_index()
records = [{"id": int(r["id"]), "name": r["name"]} for _, r in df.iterrows()]
return jsonify(records)
if __name__ == "__main__":
host = "0.0.0.0"
......
......@@ -26,6 +26,7 @@ CATEGORY_TAGS = {
}
TYPE_LINK = 0x4000000
TYPE_PENDULUM = 0x1000000
TYPE_MONSTER = 0x1
LINK_MARKERS = {
0x040: "↖", # TOP_LEFT
0x080: "↑", # TOP
......
......@@ -123,7 +123,11 @@
<!-- 历史猜测 -->
{% if history %}
<h2>历史猜测</h2>
<h2>历史猜测
<small style="font-size:0.8em; color:#666;">
({{ guess_count }} / {{ max_attempts }} )
</small>
</h2>
<table>
<thead>
<tr>
......@@ -154,6 +158,7 @@
<!-- 输入与按钮 -->
<form method="POST" autocomplete="off" style="position: relative; margin-top: 1em;">
<input type="text" id="guess" name="guess" placeholder="输入卡名" oninput="fetchSuggestions()" onfocus="fetchSuggestions()" />
<input type="hidden" name="guess_id" id="guess_id" value="">
<!-- 三个动作按钮 -->
<button type="submit" name="action" value="guess">提交猜测</button>
<button type="submit" name="action" value="surrender">投降</button>
......@@ -193,17 +198,23 @@
<script>
async function fetchSuggestions() {
const input = document.getElementById("guess");
const sug = document.getElementById("suggestions");
const kw = input.value.trim();
if (!kw) { sug.innerHTML=''; sug.hidden=true; return; }
const hid = document.getElementById("guess_id");
const sug = document.getElementById("suggestions");
const kw = input.value.trim();
if (!kw) { hid.value=""; sug.innerHTML=''; sug.hidden=true; return; }
const resp = await fetch(`/suggest?q=${encodeURIComponent(kw)}`);
const names = await resp.json();
const items = await resp.json();
sug.innerHTML = '';
if (!names.length) { sug.hidden = true; return; }
for (const name of names) {
if (!items.length) { sug.hidden = true; return; }
for (const item of items) {
const li = document.createElement('li');
li.textContent = name;
li.onclick = () => { input.value = name; sug.hidden = true; };
li.textContent = item.name;
li.dataset.id = item.id;
li.onclick = () => {
input.value = item.name;
hid.value = item.id;
sug.hidden = true;
};
sug.appendChild(li);
}
sug.hidden = false;
......@@ -214,5 +225,6 @@
}
});
</script>
</body>
</html>
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