Spaces:
Sleeping
Sleeping
jiang buqing commited on
Commit ·
e6cc357
1
Parent(s): dc256b3
init
Browse files- Dockerfile +12 -0
- README.md +31 -0
- app.py +98 -0
- requirements.txt +4 -0
- space.yaml +9 -0
- static/css/style.css +305 -0
- static/js/app.js +360 -0
- templates/index.html +49 -0
- words.txt +510 -0
Dockerfile
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
COPY . /app
|
| 5 |
+
|
| 6 |
+
RUN pip install --upgrade pip
|
| 7 |
+
RUN pip install -r requirements.txt
|
| 8 |
+
|
| 9 |
+
EXPOSE 7860
|
| 10 |
+
ENV PORT=7860
|
| 11 |
+
|
| 12 |
+
CMD ["python", "app.py"]
|
README.md
CHANGED
|
@@ -9,3 +9,34 @@ short_description: Semantic Hunter Game
|
|
| 9 |
---
|
| 10 |
|
| 11 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 12 |
+
|
| 13 |
+
# Semantic Hunter (词猎人)
|
| 14 |
+
|
| 15 |
+
A semantic word guessing game using Hugging Face Transformers. The game chooses a random word, and players try to guess it based on semantic similarity.
|
| 16 |
+
|
| 17 |
+
## How to Play
|
| 18 |
+
|
| 19 |
+
1. The game selects a random word from the word list
|
| 20 |
+
2. You try to guess the word
|
| 21 |
+
3. After each guess, you get a semantic similarity percentage
|
| 22 |
+
4. Try to find the secret word with the fewest guesses!
|
| 23 |
+
|
| 24 |
+
## Deploy to Hugging Face Spaces
|
| 25 |
+
|
| 26 |
+
This application is configured to run on Hugging Face Spaces with Docker:
|
| 27 |
+
|
| 28 |
+
1. Create a new Space on [Hugging Face Spaces](https://huggingface.co/spaces)
|
| 29 |
+
2. Select Docker as the SDK
|
| 30 |
+
3. Link your GitHub repository or upload files directly
|
| 31 |
+
4. The Space will automatically build and deploy your app
|
| 32 |
+
|
| 33 |
+
## Development
|
| 34 |
+
|
| 35 |
+
To run the application locally:
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
pip install -r requirements.txt
|
| 39 |
+
python app.py
|
| 40 |
+
```
|
| 41 |
+
|
| 42 |
+
The application will be available at http://localhost:7860
|
app.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, render_template, request, jsonify
|
| 2 |
+
import random
|
| 3 |
+
from sentence_transformers import SentenceTransformer, util
|
| 4 |
+
import torch
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
app = Flask(__name__)
|
| 8 |
+
|
| 9 |
+
# Load model
|
| 10 |
+
model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
|
| 11 |
+
|
| 12 |
+
# Load word pool
|
| 13 |
+
with open("words.txt", "r", encoding="utf-8") as f:
|
| 14 |
+
word_pool = [line.strip() for line in f if line.strip()]
|
| 15 |
+
|
| 16 |
+
# Game state
|
| 17 |
+
game_state = {
|
| 18 |
+
"secret_word": "",
|
| 19 |
+
"secret_vec": None,
|
| 20 |
+
"guesses": [],
|
| 21 |
+
"latest_guess": None
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
@app.route('/')
|
| 25 |
+
def home():
|
| 26 |
+
return render_template('index.html')
|
| 27 |
+
|
| 28 |
+
@app.route('/new-game', methods=['POST'])
|
| 29 |
+
def new_game():
|
| 30 |
+
global game_state
|
| 31 |
+
# Reset game state
|
| 32 |
+
game_state["secret_word"] = random.choice(word_pool)
|
| 33 |
+
game_state["secret_vec"] = model.encode(game_state["secret_word"], convert_to_tensor=True)
|
| 34 |
+
game_state["guesses"] = []
|
| 35 |
+
game_state["latest_guess"] = None
|
| 36 |
+
|
| 37 |
+
return jsonify({"status": "success", "message": "New game started"})
|
| 38 |
+
|
| 39 |
+
@app.route('/guess', methods=['POST'])
|
| 40 |
+
def guess():
|
| 41 |
+
data = request.json
|
| 42 |
+
guess_word = data.get('guess', '').strip()
|
| 43 |
+
|
| 44 |
+
if not guess_word:
|
| 45 |
+
return jsonify({"status": "error", "message": "No guess provided"})
|
| 46 |
+
|
| 47 |
+
# Encode the guess
|
| 48 |
+
guess_vec = model.encode(guess_word, convert_to_tensor=True)
|
| 49 |
+
|
| 50 |
+
# Calculate similarity
|
| 51 |
+
similarity = util.pytorch_cos_sim(game_state["secret_vec"], guess_vec)
|
| 52 |
+
percent = round(float(similarity[0][0]) * 100, 2)
|
| 53 |
+
|
| 54 |
+
# Check if correct
|
| 55 |
+
is_correct = (game_state["secret_word"] == guess_word)
|
| 56 |
+
|
| 57 |
+
# Update latest guess
|
| 58 |
+
game_state["latest_guess"] = guess_word
|
| 59 |
+
|
| 60 |
+
# Store guess
|
| 61 |
+
game_state["guesses"].append({
|
| 62 |
+
"word": guess_word,
|
| 63 |
+
"similarity": percent,
|
| 64 |
+
"is_correct": is_correct,
|
| 65 |
+
"is_latest": True
|
| 66 |
+
})
|
| 67 |
+
|
| 68 |
+
# Update previous guesses to not be latest
|
| 69 |
+
for i in range(len(game_state["guesses"]) - 1):
|
| 70 |
+
if "is_latest" in game_state["guesses"][i]:
|
| 71 |
+
game_state["guesses"][i]["is_latest"] = False
|
| 72 |
+
|
| 73 |
+
# Sort guesses by similarity (descending)
|
| 74 |
+
sorted_guesses = sorted(game_state["guesses"], key=lambda x: x["similarity"], reverse=True)
|
| 75 |
+
|
| 76 |
+
response = {
|
| 77 |
+
"status": "success",
|
| 78 |
+
"similarity": percent,
|
| 79 |
+
"is_correct": is_correct,
|
| 80 |
+
"guesses": game_state["guesses"],
|
| 81 |
+
"latest_guess": guess_word
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
if is_correct:
|
| 85 |
+
response["message"] = "猜对啦!"
|
| 86 |
+
|
| 87 |
+
return jsonify(response)
|
| 88 |
+
|
| 89 |
+
@app.route('/give-up', methods=['POST'])
|
| 90 |
+
def give_up():
|
| 91 |
+
return jsonify({
|
| 92 |
+
"status": "success",
|
| 93 |
+
"secret_word": game_state["secret_word"]
|
| 94 |
+
})
|
| 95 |
+
|
| 96 |
+
if __name__ == '__main__':
|
| 97 |
+
port = int(os.environ.get("PORT", 7860))
|
| 98 |
+
app.run(host='0.0.0.0', port=port)
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
flask
|
| 2 |
+
sentence-transformers
|
| 3 |
+
numpy
|
| 4 |
+
|
space.yaml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Semantic Hunter
|
| 3 |
+
emoji: 🔍
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
pinned: false
|
| 9 |
+
---
|
static/css/style.css
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Main Styles for Semantic Hunter */
|
| 2 |
+
:root {
|
| 3 |
+
--primary-color: #0f172a;
|
| 4 |
+
--secondary-color: #334155;
|
| 5 |
+
--highlight-color: #f59e0b;
|
| 6 |
+
--text-color: #f8fafc;
|
| 7 |
+
--error-color: #ef4444;
|
| 8 |
+
--success-color: #22c55e;
|
| 9 |
+
--input-bg: #1e293b;
|
| 10 |
+
--card-bg: #1e293b;
|
| 11 |
+
--separator-color: rgba(255, 255, 255, 0.1);
|
| 12 |
+
--yellow-bright: #ffee00;
|
| 13 |
+
--yellow-medium: #ffd700;
|
| 14 |
+
--orange-color: #ff9900;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
* {
|
| 18 |
+
margin: 0;
|
| 19 |
+
padding: 0;
|
| 20 |
+
box-sizing: border-box;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
body {
|
| 24 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 25 |
+
background-color: var(--primary-color);
|
| 26 |
+
color: var(--text-color);
|
| 27 |
+
line-height: 1.6;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.container {
|
| 31 |
+
max-width: 800px;
|
| 32 |
+
margin: 0 auto;
|
| 33 |
+
padding: 2rem 1rem;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
header {
|
| 37 |
+
display: flex;
|
| 38 |
+
justify-content: space-between;
|
| 39 |
+
align-items: center;
|
| 40 |
+
margin-bottom: 2rem;
|
| 41 |
+
flex-wrap: wrap;
|
| 42 |
+
gap: 1rem;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
h1 {
|
| 46 |
+
font-size: 2.5rem;
|
| 47 |
+
color: var(--highlight-color);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.subtitle {
|
| 51 |
+
font-size: 0.7em;
|
| 52 |
+
opacity: 0.8;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.controls {
|
| 56 |
+
display: flex;
|
| 57 |
+
gap: 0.5rem;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.btn {
|
| 61 |
+
padding: 0.6rem 1.2rem;
|
| 62 |
+
border: none;
|
| 63 |
+
border-radius: 0.5rem;
|
| 64 |
+
background-color: var(--secondary-color);
|
| 65 |
+
color: var(--text-color);
|
| 66 |
+
cursor: pointer;
|
| 67 |
+
transition: all 0.3s ease;
|
| 68 |
+
font-size: 1rem;
|
| 69 |
+
display: flex;
|
| 70 |
+
align-items: center;
|
| 71 |
+
gap: 0.5rem;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
.btn:hover {
|
| 75 |
+
opacity: 0.9;
|
| 76 |
+
transform: translateY(-2px);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.btn-primary {
|
| 80 |
+
background-color: var(--highlight-color);
|
| 81 |
+
color: var(--primary-color);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.btn-danger {
|
| 85 |
+
background-color: var(--error-color);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.game-area {
|
| 89 |
+
margin-bottom: 2rem;
|
| 90 |
+
background-color: var(--card-bg);
|
| 91 |
+
padding: 2rem;
|
| 92 |
+
border-radius: 1rem;
|
| 93 |
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
h2 {
|
| 97 |
+
margin-bottom: 1.5rem;
|
| 98 |
+
text-align: center;
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
.input-area {
|
| 102 |
+
display: flex;
|
| 103 |
+
gap: 0.5rem;
|
| 104 |
+
margin-bottom: 1.5rem;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
#guess-input {
|
| 108 |
+
flex: 1;
|
| 109 |
+
padding: 0.8rem 1rem;
|
| 110 |
+
border: none;
|
| 111 |
+
border-radius: 0.5rem;
|
| 112 |
+
background-color: var(--input-bg);
|
| 113 |
+
color: var(--text-color);
|
| 114 |
+
font-size: 1.1rem;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
#guess-input:focus {
|
| 118 |
+
outline: 2px solid var(--highlight-color);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
.message-area {
|
| 122 |
+
text-align: center;
|
| 123 |
+
min-height: 1.5rem;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.history-area {
|
| 127 |
+
background-color: var(--card-bg);
|
| 128 |
+
padding: 2rem;
|
| 129 |
+
border-radius: 1rem;
|
| 130 |
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
h3 {
|
| 134 |
+
margin-bottom: 1.5rem;
|
| 135 |
+
text-align: center;
|
| 136 |
+
color: var(--highlight-color);
|
| 137 |
+
display: none; /* Hide the Guess History heading */
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
.guesses-list {
|
| 141 |
+
display: flex;
|
| 142 |
+
flex-direction: column;
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/* Latest guess container styles */
|
| 146 |
+
.latest-guess-container {
|
| 147 |
+
margin-bottom: 0.5rem;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
/* Latest guess now has a background */
|
| 151 |
+
.latest-guess {
|
| 152 |
+
display: flex;
|
| 153 |
+
justify-content: space-between;
|
| 154 |
+
padding: 0.8rem 1rem;
|
| 155 |
+
border-radius: 0.5rem;
|
| 156 |
+
background-color: var(--secondary-color);
|
| 157 |
+
color: var(--text-color);
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
/* Separator line */
|
| 161 |
+
.guess-separator {
|
| 162 |
+
height: 1px;
|
| 163 |
+
background-color: var(--separator-color);
|
| 164 |
+
margin: 0.5rem 0 1rem 0;
|
| 165 |
+
width: 100%;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/* Regular history containers */
|
| 169 |
+
.history-guesses-container {
|
| 170 |
+
display: flex;
|
| 171 |
+
flex-direction: column;
|
| 172 |
+
gap: 0.8rem;
|
| 173 |
+
margin-bottom: 1.5rem;
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
/* History items now have no background */
|
| 177 |
+
.guess-item {
|
| 178 |
+
display: flex;
|
| 179 |
+
justify-content: space-between;
|
| 180 |
+
padding: 0rem 1rem;
|
| 181 |
+
border-radius: 0.5rem;
|
| 182 |
+
background-color: transparent;
|
| 183 |
+
color: #ddd;
|
| 184 |
+
transition: all 0.3s ease;
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
/* Special style for 100% match */
|
| 188 |
+
.perfect-match {
|
| 189 |
+
background-color: rgba(34, 197, 94, 0.2);
|
| 190 |
+
border-radius: 0.5rem;
|
| 191 |
+
border: 1px solid var(--success-color);
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
.guess-word {
|
| 195 |
+
font-weight: bold;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
/* Base similarity style */
|
| 199 |
+
.guess-similarity {
|
| 200 |
+
font-family: 'Courier New', Courier, monospace;
|
| 201 |
+
font-size: 1.25rem;
|
| 202 |
+
font-weight: bold;
|
| 203 |
+
letter-spacing: 0.05em;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
/* 1. 0-50%: Default style */
|
| 207 |
+
.similarity-low {
|
| 208 |
+
color: #aaa;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
/* 2. 50-70%: Orange */
|
| 212 |
+
.similarity-medium {
|
| 213 |
+
color: var(--orange-color);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
/* 3. 70-80%: Yellow */
|
| 217 |
+
.similarity-high {
|
| 218 |
+
color: var(--yellow-medium);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/* 4. 80-90%: Bright yellow with glow */
|
| 222 |
+
.similarity-very-high {
|
| 223 |
+
color: var(--yellow-bright);
|
| 224 |
+
text-shadow: 0 0 5px var(--yellow-bright);
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
/* 5. 90-99%: Orbitron font + bright yellow with glow */
|
| 228 |
+
.similarity-super-high {
|
| 229 |
+
font-family: 'Orbitron', 'Courier New', Courier, monospace;
|
| 230 |
+
color: var(--yellow-bright);
|
| 231 |
+
text-shadow: 0 0 8px var(--yellow-bright);
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
/* 6. 100%: Green with special container */
|
| 235 |
+
.similarity-perfect {
|
| 236 |
+
font-family: 'Orbitron', 'Courier New', Courier, monospace;
|
| 237 |
+
color: var(--success-color);
|
| 238 |
+
text-shadow: 0 0 10px var(--success-color);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
/* Pagination styles */
|
| 242 |
+
.pagination-container {
|
| 243 |
+
display: flex;
|
| 244 |
+
justify-content: center;
|
| 245 |
+
align-items: center;
|
| 246 |
+
padding: 1rem 0;
|
| 247 |
+
gap: 1.5rem;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
.pagination-btn {
|
| 251 |
+
display: flex;
|
| 252 |
+
justify-content: center;
|
| 253 |
+
align-items: center;
|
| 254 |
+
width: 2.5rem;
|
| 255 |
+
height: 2.5rem;
|
| 256 |
+
border-radius: 50%;
|
| 257 |
+
background-color: var(--secondary-color);
|
| 258 |
+
color: var(--text-color);
|
| 259 |
+
border: none;
|
| 260 |
+
font-size: 1rem;
|
| 261 |
+
cursor: pointer;
|
| 262 |
+
transition: all 0.2s ease;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.pagination-btn:hover:not(:disabled) {
|
| 266 |
+
background-color: var(--highlight-color);
|
| 267 |
+
color: var(--primary-color);
|
| 268 |
+
transform: translateY(-2px);
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
.pagination-btn:disabled {
|
| 272 |
+
opacity: 0.5;
|
| 273 |
+
cursor: not-allowed;
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
.page-indicator {
|
| 277 |
+
font-family: monospace;
|
| 278 |
+
font-size: 1.1rem;
|
| 279 |
+
color: var(--text-color);
|
| 280 |
+
opacity: 0.8;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
footer {
|
| 284 |
+
margin-top: 3rem;
|
| 285 |
+
text-align: center;
|
| 286 |
+
opacity: 0.7;
|
| 287 |
+
font-size: 0.9rem;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
/* Responsive */
|
| 291 |
+
@media (max-width: 600px) {
|
| 292 |
+
header {
|
| 293 |
+
flex-direction: column;
|
| 294 |
+
align-items: flex-start;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
.controls {
|
| 298 |
+
width: 100%;
|
| 299 |
+
justify-content: space-between;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
.game-area, .history-area {
|
| 303 |
+
padding: 1.5rem 1rem;
|
| 304 |
+
}
|
| 305 |
+
}
|
static/js/app.js
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Semantic Hunter Game Frontend
|
| 2 |
+
|
| 3 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 4 |
+
// DOM Elements
|
| 5 |
+
const guessInput = document.getElementById('guess-input');
|
| 6 |
+
const guessBtn = document.getElementById('guess-btn');
|
| 7 |
+
const messageArea = document.getElementById('message-area');
|
| 8 |
+
const guessesList = document.getElementById('guesses-list');
|
| 9 |
+
const newGameBtn = document.getElementById('new-game-btn');
|
| 10 |
+
const giveUpBtn = document.getElementById('give-up-btn');
|
| 11 |
+
|
| 12 |
+
// Game state
|
| 13 |
+
let gameActive = false;
|
| 14 |
+
let latestGuessId = null;
|
| 15 |
+
let allGuesses = [];
|
| 16 |
+
let currentPage = 1;
|
| 17 |
+
const itemsPerPage = 7;
|
| 18 |
+
|
| 19 |
+
// Initialize game
|
| 20 |
+
initGame();
|
| 21 |
+
|
| 22 |
+
// Event listeners
|
| 23 |
+
guessBtn.addEventListener('click', submitGuess);
|
| 24 |
+
guessInput.addEventListener('keypress', (e) => {
|
| 25 |
+
if (e.key === 'Enter') submitGuess();
|
| 26 |
+
});
|
| 27 |
+
newGameBtn.addEventListener('click', initGame);
|
| 28 |
+
giveUpBtn.addEventListener('click', giveUp);
|
| 29 |
+
|
| 30 |
+
// Functions
|
| 31 |
+
function initGame() {
|
| 32 |
+
fetch('/new-game', {
|
| 33 |
+
method: 'POST',
|
| 34 |
+
headers: {
|
| 35 |
+
'Content-Type': 'application/json'
|
| 36 |
+
}
|
| 37 |
+
})
|
| 38 |
+
.then(response => response.json())
|
| 39 |
+
.then(data => {
|
| 40 |
+
if (data.status === 'success') {
|
| 41 |
+
gameActive = true;
|
| 42 |
+
guessesList.innerHTML = '';
|
| 43 |
+
messageArea.innerHTML = '';
|
| 44 |
+
guessInput.value = '';
|
| 45 |
+
guessInput.focus();
|
| 46 |
+
allGuesses = [];
|
| 47 |
+
currentPage = 1;
|
| 48 |
+
latestGuessId = null;
|
| 49 |
+
messageArea.innerHTML = `<p class="message">游戏开始! 尝试猜测秘密词语!</p>`;
|
| 50 |
+
} else {
|
| 51 |
+
showError(data.message || '初始化游戏时出错');
|
| 52 |
+
}
|
| 53 |
+
})
|
| 54 |
+
.catch(err => {
|
| 55 |
+
showError('无法连接到服务器');
|
| 56 |
+
console.error(err);
|
| 57 |
+
});
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
function submitGuess() {
|
| 61 |
+
const guess = guessInput.value.trim();
|
| 62 |
+
if (!guess) return;
|
| 63 |
+
if (!gameActive) {
|
| 64 |
+
showError('游戏未开始,请点击新游戏');
|
| 65 |
+
return;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
// Check if this word has already been guessed, but just proceed silently
|
| 69 |
+
const alreadyGuessed = allGuesses.some(g => g.word === guess) ||
|
| 70 |
+
(latestGuessId && latestGuessId === guess);
|
| 71 |
+
|
| 72 |
+
// Always send the request to get similarity, but we'll handle duplicates when updating the UI
|
| 73 |
+
fetch('/guess', {
|
| 74 |
+
method: 'POST',
|
| 75 |
+
headers: {
|
| 76 |
+
'Content-Type': 'application/json'
|
| 77 |
+
},
|
| 78 |
+
body: JSON.stringify({ guess })
|
| 79 |
+
})
|
| 80 |
+
.then(response => response.json())
|
| 81 |
+
.then(data => {
|
| 82 |
+
if (data.status === 'success') {
|
| 83 |
+
// If the word was already guessed, just update the latest guess display
|
| 84 |
+
// without adding a duplicate to the history
|
| 85 |
+
if (alreadyGuessed) {
|
| 86 |
+
// Find this guess in our existing data to get its similarity
|
| 87 |
+
const existingGuess = allGuesses.find(g => g.word === guess);
|
| 88 |
+
if (existingGuess) {
|
| 89 |
+
// Show just the latest guess with this word
|
| 90 |
+
updateLatestGuess({
|
| 91 |
+
word: guess,
|
| 92 |
+
similarity: existingGuess.similarity,
|
| 93 |
+
is_correct: existingGuess.is_correct
|
| 94 |
+
});
|
| 95 |
+
|
| 96 |
+
// Show similarity message
|
| 97 |
+
showMessage(`相似度: ${existingGuess.similarity}%`);
|
| 98 |
+
}
|
| 99 |
+
} else {
|
| 100 |
+
// Normal flow for new guesses
|
| 101 |
+
updateGuessList(data.guesses, guess);
|
| 102 |
+
|
| 103 |
+
if (data.is_correct) {
|
| 104 |
+
gameActive = false;
|
| 105 |
+
showSuccess(`恭喜你猜对了! 秘密词语是: ${guess}`);
|
| 106 |
+
} else {
|
| 107 |
+
showMessage(`相似度: ${data.similarity}%`);
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
// Always update latest guess ID and clear input
|
| 112 |
+
latestGuessId = guess;
|
| 113 |
+
guessInput.value = '';
|
| 114 |
+
guessInput.focus();
|
| 115 |
+
} else {
|
| 116 |
+
showError(data.message || '提交猜测时出错');
|
| 117 |
+
}
|
| 118 |
+
})
|
| 119 |
+
.catch(err => {
|
| 120 |
+
showError('无法连接到服务器');
|
| 121 |
+
console.error(err);
|
| 122 |
+
});
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
function giveUp() {
|
| 126 |
+
if (!gameActive) {
|
| 127 |
+
showError('游戏未开始,请点击新游戏');
|
| 128 |
+
return;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
fetch('/give-up', {
|
| 132 |
+
method: 'POST',
|
| 133 |
+
headers: {
|
| 134 |
+
'Content-Type': 'application/json'
|
| 135 |
+
}
|
| 136 |
+
})
|
| 137 |
+
.then(response => response.json())
|
| 138 |
+
.then(data => {
|
| 139 |
+
if (data.status === 'success') {
|
| 140 |
+
gameActive = false;
|
| 141 |
+
showError(`你放弃了! 秘密词语是: ${data.secret_word}`);
|
| 142 |
+
} else {
|
| 143 |
+
showError(data.message || '放弃游戏时出错');
|
| 144 |
+
}
|
| 145 |
+
})
|
| 146 |
+
.catch(err => {
|
| 147 |
+
showError('无法连接到服务器');
|
| 148 |
+
console.error(err);
|
| 149 |
+
});
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
function updateGuessList(guesses, latestGuess) {
|
| 153 |
+
guessesList.innerHTML = '';
|
| 154 |
+
|
| 155 |
+
// Create a copy of the guesses array to avoid modifying the original
|
| 156 |
+
const sortedGuesses = [...guesses];
|
| 157 |
+
|
| 158 |
+
// Find the latest guess and remove it from the sorted array
|
| 159 |
+
const latestGuessIndex = sortedGuesses.findIndex(g => g.word === latestGuess);
|
| 160 |
+
let latestGuessItem = null;
|
| 161 |
+
if (latestGuessIndex !== -1) {
|
| 162 |
+
latestGuessItem = sortedGuesses.splice(latestGuessIndex, 1)[0];
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
// Sort the remaining guesses by similarity (descending)
|
| 166 |
+
sortedGuesses.sort((a, b) => b.similarity - a.similarity);
|
| 167 |
+
|
| 168 |
+
// Update our allGuesses array for pagination
|
| 169 |
+
allGuesses = sortedGuesses;
|
| 170 |
+
|
| 171 |
+
// If we have a latest guess, add it to the top of the list
|
| 172 |
+
if (latestGuessItem) {
|
| 173 |
+
// Create a container for the latest guess
|
| 174 |
+
const latestGuessContainer = document.createElement('div');
|
| 175 |
+
latestGuessContainer.className = 'latest-guess-container';
|
| 176 |
+
|
| 177 |
+
// Add the latest guess with a different style
|
| 178 |
+
const latestGuessElement = document.createElement('div');
|
| 179 |
+
latestGuessElement.className = 'latest-guess';
|
| 180 |
+
|
| 181 |
+
// Add perfect-match class if 100% similarity
|
| 182 |
+
if (latestGuessItem.similarity === 100) {
|
| 183 |
+
latestGuessElement.classList.add('perfect-match');
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
// Determine similarity color class
|
| 187 |
+
let similarityClass = getSimilarityClass(latestGuessItem.similarity);
|
| 188 |
+
|
| 189 |
+
latestGuessElement.innerHTML = `
|
| 190 |
+
<span class="guess-word">${latestGuessItem.word}</span>
|
| 191 |
+
<span class="guess-similarity ${similarityClass}">${latestGuessItem.similarity}%</span>
|
| 192 |
+
`;
|
| 193 |
+
|
| 194 |
+
latestGuessContainer.appendChild(latestGuessElement);
|
| 195 |
+
|
| 196 |
+
// Add separator line
|
| 197 |
+
const separator = document.createElement('div');
|
| 198 |
+
separator.className = 'guess-separator';
|
| 199 |
+
latestGuessContainer.appendChild(separator);
|
| 200 |
+
|
| 201 |
+
guessesList.appendChild(latestGuessContainer);
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
// Create a container for history guesses
|
| 205 |
+
const historyContainer = document.createElement('div');
|
| 206 |
+
historyContainer.className = 'history-guesses-container';
|
| 207 |
+
|
| 208 |
+
// Get current page of items
|
| 209 |
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
| 210 |
+
const endIndex = Math.min(startIndex + itemsPerPage, allGuesses.length);
|
| 211 |
+
const currentPageItems = allGuesses.slice(startIndex, endIndex);
|
| 212 |
+
|
| 213 |
+
// Add the current page of guesses
|
| 214 |
+
currentPageItems.forEach(guess => {
|
| 215 |
+
const guessItem = document.createElement('div');
|
| 216 |
+
guessItem.className = 'guess-item';
|
| 217 |
+
|
| 218 |
+
// Add perfect-match class if 100% similarity
|
| 219 |
+
if (guess.similarity === 100) {
|
| 220 |
+
guessItem.classList.add('perfect-match');
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
// Determine similarity color class
|
| 224 |
+
let similarityClass = getSimilarityClass(guess.similarity);
|
| 225 |
+
|
| 226 |
+
guessItem.innerHTML = `
|
| 227 |
+
<span class="guess-word">${guess.word}</span>
|
| 228 |
+
<span class="guess-similarity ${similarityClass}">${guess.similarity}%</span>
|
| 229 |
+
`;
|
| 230 |
+
|
| 231 |
+
historyContainer.appendChild(guessItem);
|
| 232 |
+
});
|
| 233 |
+
|
| 234 |
+
guessesList.appendChild(historyContainer);
|
| 235 |
+
|
| 236 |
+
// Add pagination if we have more than one page
|
| 237 |
+
if (allGuesses.length > itemsPerPage) {
|
| 238 |
+
addPagination();
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
function addPagination() {
|
| 243 |
+
const totalPages = Math.ceil(allGuesses.length / itemsPerPage);
|
| 244 |
+
|
| 245 |
+
// Create pagination container
|
| 246 |
+
const paginationContainer = document.createElement('div');
|
| 247 |
+
paginationContainer.className = 'pagination-container';
|
| 248 |
+
|
| 249 |
+
// Previous button
|
| 250 |
+
const prevBtn = document.createElement('button');
|
| 251 |
+
prevBtn.className = 'pagination-btn prev-btn';
|
| 252 |
+
prevBtn.innerHTML = `<i class="fas fa-chevron-left"></i>`;
|
| 253 |
+
prevBtn.disabled = currentPage === 1;
|
| 254 |
+
prevBtn.addEventListener('click', () => {
|
| 255 |
+
if (currentPage > 1) {
|
| 256 |
+
currentPage--;
|
| 257 |
+
updateGuessList(allGuesses.concat([]), ''); // Pass empty string for latestGuess to avoid highlighting
|
| 258 |
+
}
|
| 259 |
+
});
|
| 260 |
+
|
| 261 |
+
// Page indicator
|
| 262 |
+
const pageIndicator = document.createElement('div');
|
| 263 |
+
pageIndicator.className = 'page-indicator';
|
| 264 |
+
pageIndicator.textContent = `${currentPage} / ${totalPages}`;
|
| 265 |
+
|
| 266 |
+
// Next button
|
| 267 |
+
const nextBtn = document.createElement('button');
|
| 268 |
+
nextBtn.className = 'pagination-btn next-btn';
|
| 269 |
+
nextBtn.innerHTML = `<i class="fas fa-chevron-right"></i>`;
|
| 270 |
+
nextBtn.disabled = currentPage === totalPages;
|
| 271 |
+
nextBtn.addEventListener('click', () => {
|
| 272 |
+
if (currentPage < totalPages) {
|
| 273 |
+
currentPage++;
|
| 274 |
+
updateGuessList(allGuesses.concat([]), ''); // Pass empty string for latestGuess to avoid highlighting
|
| 275 |
+
}
|
| 276 |
+
});
|
| 277 |
+
|
| 278 |
+
// Append buttons to container
|
| 279 |
+
paginationContainer.appendChild(prevBtn);
|
| 280 |
+
paginationContainer.appendChild(pageIndicator);
|
| 281 |
+
paginationContainer.appendChild(nextBtn);
|
| 282 |
+
|
| 283 |
+
// Add to the DOM
|
| 284 |
+
guessesList.appendChild(paginationContainer);
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
function showMessage(msg) {
|
| 288 |
+
messageArea.innerHTML = `<p class="message">${msg}</p>`;
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
function showError(msg) {
|
| 292 |
+
messageArea.innerHTML = `<p class="message error">${msg}</p>`;
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
function showSuccess(msg) {
|
| 296 |
+
messageArea.innerHTML = `<p class="message success">${msg}</p>`;
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
// Function to determine similarity class based on percentage
|
| 300 |
+
function getSimilarityClass(similarity) {
|
| 301 |
+
if (similarity === 100) {
|
| 302 |
+
return 'similarity-perfect';
|
| 303 |
+
} else if (similarity >= 90) {
|
| 304 |
+
return 'similarity-super-high';
|
| 305 |
+
} else if (similarity >= 80) {
|
| 306 |
+
return 'similarity-very-high';
|
| 307 |
+
} else if (similarity >= 70) {
|
| 308 |
+
return 'similarity-high';
|
| 309 |
+
} else if (similarity >= 50) {
|
| 310 |
+
return 'similarity-medium';
|
| 311 |
+
} else {
|
| 312 |
+
return 'similarity-low';
|
| 313 |
+
}
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
// Function to just update the latest guess display without changing history
|
| 317 |
+
function updateLatestGuess(guessItem) {
|
| 318 |
+
// Remove existing latest guess container if it exists
|
| 319 |
+
const existingContainer = document.querySelector('.latest-guess-container');
|
| 320 |
+
if (existingContainer) {
|
| 321 |
+
existingContainer.remove();
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
// Create a new container for the latest guess
|
| 325 |
+
const latestGuessContainer = document.createElement('div');
|
| 326 |
+
latestGuessContainer.className = 'latest-guess-container';
|
| 327 |
+
|
| 328 |
+
// Add the latest guess with its style
|
| 329 |
+
const latestGuessElement = document.createElement('div');
|
| 330 |
+
latestGuessElement.className = 'latest-guess';
|
| 331 |
+
|
| 332 |
+
// Add perfect-match class if 100% similarity
|
| 333 |
+
if (guessItem.similarity === 100) {
|
| 334 |
+
latestGuessElement.classList.add('perfect-match');
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
// Determine similarity color class
|
| 338 |
+
let similarityClass = getSimilarityClass(guessItem.similarity);
|
| 339 |
+
|
| 340 |
+
latestGuessElement.innerHTML = `
|
| 341 |
+
<span class="guess-word">${guessItem.word}</span>
|
| 342 |
+
<span class="guess-similarity ${similarityClass}">${guessItem.similarity}%</span>
|
| 343 |
+
`;
|
| 344 |
+
|
| 345 |
+
latestGuessContainer.appendChild(latestGuessElement);
|
| 346 |
+
|
| 347 |
+
// Add separator line
|
| 348 |
+
const separator = document.createElement('div');
|
| 349 |
+
separator.className = 'guess-separator';
|
| 350 |
+
latestGuessContainer.appendChild(separator);
|
| 351 |
+
|
| 352 |
+
// Add to DOM before the history container
|
| 353 |
+
const historyContainer = document.querySelector('.history-guesses-container');
|
| 354 |
+
if (historyContainer) {
|
| 355 |
+
guessesList.insertBefore(latestGuessContainer, historyContainer);
|
| 356 |
+
} else {
|
| 357 |
+
guessesList.appendChild(latestGuessContainer);
|
| 358 |
+
}
|
| 359 |
+
}
|
| 360 |
+
});
|
templates/index.html
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="zh">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>词猎人 | Semantic Hunter</title>
|
| 7 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
| 8 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css">
|
| 9 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 11 |
+
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
|
| 12 |
+
</head>
|
| 13 |
+
<body>
|
| 14 |
+
<div class="container">
|
| 15 |
+
<header>
|
| 16 |
+
<h1>词猎人 <span class="subtitle">| Semantic Hunter</span></h1>
|
| 17 |
+
<div class="controls">
|
| 18 |
+
<button id="new-game-btn" class="btn"><i class="fas fa-sync-alt"></i> 新游戏</button>
|
| 19 |
+
<button id="give-up-btn" class="btn btn-danger"><i class="fas fa-flag"></i> 放弃</button>
|
| 20 |
+
</div>
|
| 21 |
+
</header>
|
| 22 |
+
|
| 23 |
+
<main>
|
| 24 |
+
<div class="game-area">
|
| 25 |
+
<h2>Can you guess the secret word?</h2>
|
| 26 |
+
<div class="input-area">
|
| 27 |
+
<input type="text" id="guess-input" placeholder="Can you guess the word?" autofocus>
|
| 28 |
+
<button id="guess-btn" class="btn btn-primary">
|
| 29 |
+
<i class="fas fa-arrow-right"></i>
|
| 30 |
+
</button>
|
| 31 |
+
</div>
|
| 32 |
+
<div id="message-area" class="message-area"></div>
|
| 33 |
+
</div>
|
| 34 |
+
|
| 35 |
+
<div class="history-area">
|
| 36 |
+
<div id="guesses-list" class="guesses-list">
|
| 37 |
+
<!-- Guesses will be added here dynamically -->
|
| 38 |
+
</div>
|
| 39 |
+
</div>
|
| 40 |
+
</main>
|
| 41 |
+
|
| 42 |
+
<footer>
|
| 43 |
+
<p>词猎人 | Semantic Hunter © 2023</p>
|
| 44 |
+
</footer>
|
| 45 |
+
</div>
|
| 46 |
+
|
| 47 |
+
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
|
| 48 |
+
</body>
|
| 49 |
+
</html>
|
words.txt
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
草莓
|
| 2 |
+
吊灯
|
| 3 |
+
番茄酱
|
| 4 |
+
牙膏
|
| 5 |
+
彩虹
|
| 6 |
+
双层床
|
| 7 |
+
桌游
|
| 8 |
+
蜂窝
|
| 9 |
+
柠檬
|
| 10 |
+
威化饼干
|
| 11 |
+
气泡
|
| 12 |
+
哨子
|
| 13 |
+
雪球
|
| 14 |
+
耳机
|
| 15 |
+
烟花
|
| 16 |
+
冰屋
|
| 17 |
+
香蕉皮
|
| 18 |
+
割草机
|
| 19 |
+
纸杯蛋糕
|
| 20 |
+
睡袋
|
| 21 |
+
脑
|
| 22 |
+
猫
|
| 23 |
+
操场
|
| 24 |
+
奇异果
|
| 25 |
+
南瓜
|
| 26 |
+
龙虾
|
| 27 |
+
机器人
|
| 28 |
+
棒棒糖
|
| 29 |
+
沙堡
|
| 30 |
+
城堡
|
| 31 |
+
磁铁
|
| 32 |
+
拖鞋
|
| 33 |
+
链锯
|
| 34 |
+
扩音器
|
| 35 |
+
马戏团
|
| 36 |
+
美人鱼
|
| 37 |
+
电脑
|
| 38 |
+
小型货车
|
| 39 |
+
婴儿床
|
| 40 |
+
蝌蚪
|
| 41 |
+
龙
|
| 42 |
+
音乐
|
| 43 |
+
帐篷
|
| 44 |
+
哑铃
|
| 45 |
+
望远镜
|
| 46 |
+
黄鳝
|
| 47 |
+
护士
|
| 48 |
+
火车
|
| 49 |
+
猫头鹰
|
| 50 |
+
三轮车
|
| 51 |
+
旗帜
|
| 52 |
+
芭蕾舞
|
| 53 |
+
钢琴
|
| 54 |
+
垃圾
|
| 55 |
+
公园
|
| 56 |
+
海盗
|
| 57 |
+
滑雪
|
| 58 |
+
棒球
|
| 59 |
+
女王
|
| 60 |
+
高尔夫球
|
| 61 |
+
热狗
|
| 62 |
+
盐
|
| 63 |
+
iPad
|
| 64 |
+
青蛙
|
| 65 |
+
床垫
|
| 66 |
+
蛋糕
|
| 67 |
+
电池
|
| 68 |
+
牛仔
|
| 69 |
+
老师
|
| 70 |
+
蝙蝠侠
|
| 71 |
+
海绵宝宝
|
| 72 |
+
超人
|
| 73 |
+
蜘蛛侠
|
| 74 |
+
吸血鬼
|
| 75 |
+
生日
|
| 76 |
+
安全带
|
| 77 |
+
烤鸭
|
| 78 |
+
考拉
|
| 79 |
+
面具
|
| 80 |
+
香水
|
| 81 |
+
照片
|
| 82 |
+
wifi
|
| 83 |
+
满月
|
| 84 |
+
僵尸
|
| 85 |
+
游戏
|
| 86 |
+
胡萝卜
|
| 87 |
+
洋葱
|
| 88 |
+
书
|
| 89 |
+
沙发
|
| 90 |
+
电话
|
| 91 |
+
鼻子
|
| 92 |
+
手
|
| 93 |
+
斑马
|
| 94 |
+
蚊子
|
| 95 |
+
蜜蜂
|
| 96 |
+
花
|
| 97 |
+
潜艇
|
| 98 |
+
棋
|
| 99 |
+
生日蛋糕
|
| 100 |
+
自行车
|
| 101 |
+
茶壶
|
| 102 |
+
膝盖
|
| 103 |
+
菠萝
|
| 104 |
+
钱
|
| 105 |
+
水池
|
| 106 |
+
坚果
|
| 107 |
+
苹果
|
| 108 |
+
香蕉
|
| 109 |
+
大象
|
| 110 |
+
门
|
| 111 |
+
天使
|
| 112 |
+
眼球
|
| 113 |
+
婴儿
|
| 114 |
+
胡须
|
| 115 |
+
飞碟
|
| 116 |
+
回收
|
| 117 |
+
圣经
|
| 118 |
+
长颈鹿
|
| 119 |
+
比基尼
|
| 120 |
+
眼镜
|
| 121 |
+
雪花
|
| 122 |
+
高跟鞋
|
| 123 |
+
楼梯
|
| 124 |
+
桶
|
| 125 |
+
蛋卷冰淇淋
|
| 126 |
+
海星
|
| 127 |
+
熊蜂
|
| 128 |
+
蝴蝶
|
| 129 |
+
瓢虫
|
| 130 |
+
太阳
|
| 131 |
+
相机
|
| 132 |
+
灯
|
| 133 |
+
胎
|
| 134 |
+
狮子
|
| 135 |
+
吐司
|
| 136 |
+
教会
|
| 137 |
+
邮箱
|
| 138 |
+
牙刷
|
| 139 |
+
蜡笔
|
| 140 |
+
夜晚
|
| 141 |
+
海豚
|
| 142 |
+
卡车
|
| 143 |
+
蛋
|
| 144 |
+
奥运会
|
| 145 |
+
排球
|
| 146 |
+
埃菲尔铁塔
|
| 147 |
+
花生
|
| 148 |
+
吻
|
| 149 |
+
口红
|
| 150 |
+
雨
|
| 151 |
+
公共汽车
|
| 152 |
+
车祸
|
| 153 |
+
棒糖
|
| 154 |
+
电锯
|
| 155 |
+
滚雪球
|
| 156 |
+
指甲
|
| 157 |
+
锤子
|
| 158 |
+
马戏团帐篷
|
| 159 |
+
自由女神像
|
| 160 |
+
大本钟
|
| 161 |
+
圆锥形帐篷
|
| 162 |
+
壶铃
|
| 163 |
+
北极
|
| 164 |
+
南极
|
| 165 |
+
鳗鱼
|
| 166 |
+
摩天轮
|
| 167 |
+
原子
|
| 168 |
+
奶嘴
|
| 169 |
+
短裙
|
| 170 |
+
垃圾邮件
|
| 171 |
+
吉他
|
| 172 |
+
后座
|
| 173 |
+
高脚椅
|
| 174 |
+
床
|
| 175 |
+
扇子
|
| 176 |
+
钟
|
| 177 |
+
椅子
|
| 178 |
+
书架
|
| 179 |
+
图片
|
| 180 |
+
壁橱
|
| 181 |
+
衣柜
|
| 182 |
+
枕头
|
| 183 |
+
毯子
|
| 184 |
+
垃圾桶
|
| 185 |
+
电视
|
| 186 |
+
洗手间
|
| 187 |
+
洗衣机
|
| 188 |
+
烘干机
|
| 189 |
+
淋浴
|
| 190 |
+
浴缸
|
| 191 |
+
卫生纸
|
| 192 |
+
剃刀
|
| 193 |
+
毛巾
|
| 194 |
+
钩子
|
| 195 |
+
洗发水
|
| 196 |
+
桌子
|
| 197 |
+
长椅
|
| 198 |
+
花瓶
|
| 199 |
+
火炉
|
| 200 |
+
冰箱
|
| 201 |
+
电饭锅
|
| 202 |
+
洗碗机
|
| 203 |
+
时间表
|
| 204 |
+
日历
|
| 205 |
+
梳子
|
| 206 |
+
衣服
|
| 207 |
+
杯子
|
| 208 |
+
窗帘
|
| 209 |
+
罐
|
| 210 |
+
屏幕
|
| 211 |
+
床单
|
| 212 |
+
手帕
|
| 213 |
+
手提包
|
| 214 |
+
夹子
|
| 215 |
+
剪刀
|
| 216 |
+
刀
|
| 217 |
+
叉
|
| 218 |
+
筷子
|
| 219 |
+
瓶子
|
| 220 |
+
老鼠
|
| 221 |
+
金属丝
|
| 222 |
+
笔记本电脑
|
| 223 |
+
钥匙
|
| 224 |
+
充电器
|
| 225 |
+
USB
|
| 226 |
+
处理
|
| 227 |
+
别针
|
| 228 |
+
抽屉
|
| 229 |
+
手电筒
|
| 230 |
+
红绿灯
|
| 231 |
+
药丸
|
| 232 |
+
药品
|
| 233 |
+
歌曲
|
| 234 |
+
弦
|
| 235 |
+
鼓
|
| 236 |
+
长笛
|
| 237 |
+
喇叭
|
| 238 |
+
大提琴
|
| 239 |
+
竖琴
|
| 240 |
+
戒指
|
| 241 |
+
钻石
|
| 242 |
+
树叶
|
| 243 |
+
地球
|
| 244 |
+
阴影
|
| 245 |
+
嘴唇
|
| 246 |
+
罗盘
|
| 247 |
+
屁
|
| 248 |
+
鱼
|
| 249 |
+
钓竿
|
| 250 |
+
医院
|
| 251 |
+
酒店
|
| 252 |
+
工厂
|
| 253 |
+
摩托车
|
| 254 |
+
车
|
| 255 |
+
监狱
|
| 256 |
+
囚犯
|
| 257 |
+
草
|
| 258 |
+
锄
|
| 259 |
+
镐
|
| 260 |
+
斧头
|
| 261 |
+
剑
|
| 262 |
+
衬衫
|
| 263 |
+
裤子
|
| 264 |
+
内裤
|
| 265 |
+
地平线
|
| 266 |
+
垂直的
|
| 267 |
+
心
|
| 268 |
+
钱包
|
| 269 |
+
西瓜
|
| 270 |
+
香肠
|
| 271 |
+
皮卡丘
|
| 272 |
+
土豆
|
| 273 |
+
小麦
|
| 274 |
+
可可
|
| 275 |
+
手套
|
| 276 |
+
飞机
|
| 277 |
+
盆地
|
| 278 |
+
羊
|
| 279 |
+
糖果
|
| 280 |
+
腿
|
| 281 |
+
肉
|
| 282 |
+
米
|
| 283 |
+
键盘
|
| 284 |
+
黑色的
|
| 285 |
+
馅饼
|
| 286 |
+
河
|
| 287 |
+
裙子
|
| 288 |
+
猪
|
| 289 |
+
啤酒
|
| 290 |
+
岩石
|
| 291 |
+
牙齿
|
| 292 |
+
嘴
|
| 293 |
+
眼睛
|
| 294 |
+
耳朵
|
| 295 |
+
骨头
|
| 296 |
+
线
|
| 297 |
+
头发
|
| 298 |
+
飞
|
| 299 |
+
游泳
|
| 300 |
+
射击
|
| 301 |
+
地图
|
| 302 |
+
拥抱
|
| 303 |
+
铅笔
|
| 304 |
+
水
|
| 305 |
+
可乐
|
| 306 |
+
烤箱
|
| 307 |
+
船
|
| 308 |
+
手指
|
| 309 |
+
卡片
|
| 310 |
+
纸
|
| 311 |
+
学校
|
| 312 |
+
沙拉
|
| 313 |
+
面包
|
| 314 |
+
花朵
|
| 315 |
+
窗户
|
| 316 |
+
树
|
| 317 |
+
铁
|
| 318 |
+
蛇
|
| 319 |
+
狗
|
| 320 |
+
鸡
|
| 321 |
+
咖啡
|
| 322 |
+
牛奶
|
| 323 |
+
垃圾箱
|
| 324 |
+
摇篮
|
| 325 |
+
消防队员
|
| 326 |
+
警察
|
| 327 |
+
医生
|
| 328 |
+
司机
|
| 329 |
+
银行
|
| 330 |
+
美国
|
| 331 |
+
中国
|
| 332 |
+
日本
|
| 333 |
+
德国
|
| 334 |
+
英国
|
| 335 |
+
汗
|
| 336 |
+
鞋
|
| 337 |
+
T恤
|
| 338 |
+
叶子
|
| 339 |
+
枪
|
| 340 |
+
步枪
|
| 341 |
+
霰弹枪
|
| 342 |
+
壳
|
| 343 |
+
铲
|
| 344 |
+
颅骨
|
| 345 |
+
蘑菇
|
| 346 |
+
橙子
|
| 347 |
+
氧
|
| 348 |
+
雷达
|
| 349 |
+
收音机
|
| 350 |
+
嗓音
|
| 351 |
+
黑洞
|
| 352 |
+
炒鸡蛋
|
| 353 |
+
熏肉
|
| 354 |
+
暴风雪
|
| 355 |
+
海啸
|
| 356 |
+
跳绳
|
| 357 |
+
袋鼠
|
| 358 |
+
队长
|
| 359 |
+
妖精
|
| 360 |
+
日食
|
| 361 |
+
光
|
| 362 |
+
空间
|
| 363 |
+
听诊器
|
| 364 |
+
游轮
|
| 365 |
+
战列舰
|
| 366 |
+
喷射
|
| 367 |
+
机械
|
| 368 |
+
牙医
|
| 369 |
+
鹳
|
| 370 |
+
舞蹈
|
| 371 |
+
妈妈
|
| 372 |
+
爸爸
|
| 373 |
+
Facebook
|
| 374 |
+
推特
|
| 375 |
+
连裤袜
|
| 376 |
+
游客
|
| 377 |
+
导游
|
| 378 |
+
平坦的
|
| 379 |
+
纸盘子
|
| 380 |
+
框架
|
| 381 |
+
无线上网
|
| 382 |
+
狼人
|
| 383 |
+
向导
|
| 384 |
+
巫婆
|
| 385 |
+
朝圣
|
| 386 |
+
美洲原住民
|
| 387 |
+
飞行员
|
| 388 |
+
鹦鹉
|
| 389 |
+
视频游戏
|
| 390 |
+
哑剧
|
| 391 |
+
冰雹
|
| 392 |
+
密码
|
| 393 |
+
激光
|
| 394 |
+
神话
|
| 395 |
+
素食主义者
|
| 396 |
+
丛林
|
| 397 |
+
农民
|
| 398 |
+
镜子
|
| 399 |
+
下载
|
| 400 |
+
滑雪镜
|
| 401 |
+
尖叫
|
| 402 |
+
说谎
|
| 403 |
+
橡皮
|
| 404 |
+
闪光
|
| 405 |
+
蚱蜢
|
| 406 |
+
理发师
|
| 407 |
+
维他命
|
| 408 |
+
服务员
|
| 409 |
+
伐木工人
|
| 410 |
+
救生员
|
| 411 |
+
矿工
|
| 412 |
+
图书管理员
|
| 413 |
+
法官
|
| 414 |
+
宇航员
|
| 415 |
+
渔夫
|
| 416 |
+
贝克
|
| 417 |
+
栅栏
|
| 418 |
+
狗窝
|
| 419 |
+
微波
|
| 420 |
+
动物园
|
| 421 |
+
公交车站
|
| 422 |
+
火车站
|
| 423 |
+
水塔
|
| 424 |
+
桥
|
| 425 |
+
腰带
|
| 426 |
+
领结
|
| 427 |
+
连帽衫
|
| 428 |
+
手表
|
| 429 |
+
制服
|
| 430 |
+
踝
|
| 431 |
+
前额
|
| 432 |
+
牙龈
|
| 433 |
+
食指
|
| 434 |
+
颚
|
| 435 |
+
指关节
|
| 436 |
+
鼻孔
|
| 437 |
+
耳垂
|
| 438 |
+
瞳孔
|
| 439 |
+
虹膜
|
| 440 |
+
订书机
|
| 441 |
+
薪水
|
| 442 |
+
计算器
|
| 443 |
+
蜘蛛
|
| 444 |
+
果冻
|
| 445 |
+
海蜇
|
| 446 |
+
冲浪板
|
| 447 |
+
松鼠
|
| 448 |
+
独角兽
|
| 449 |
+
吊床
|
| 450 |
+
金字塔
|
| 451 |
+
木乃伊
|
| 452 |
+
鸡皮疙瘩
|
| 453 |
+
鸭嘴兽
|
| 454 |
+
金鱼
|
| 455 |
+
蜘蛛网
|
| 456 |
+
海藻
|
| 457 |
+
齿
|
| 458 |
+
虫
|
| 459 |
+
家具
|
| 460 |
+
蜡烛
|
| 461 |
+
家
|
| 462 |
+
镘
|
| 463 |
+
发胶
|
| 464 |
+
围巾
|
| 465 |
+
时光机器
|
| 466 |
+
忍者
|
| 467 |
+
投手
|
| 468 |
+
鬼
|
| 469 |
+
呼啦圈
|
| 470 |
+
灭虫器
|
| 471 |
+
假发
|
| 472 |
+
饼形图
|
| 473 |
+
水枪
|
| 474 |
+
白天
|
| 475 |
+
夜间
|
| 476 |
+
长城
|
| 477 |
+
吸血蝙蝠
|
| 478 |
+
X射线
|
| 479 |
+
打哈欠
|
| 480 |
+
无形的
|
| 481 |
+
吸尘器
|
| 482 |
+
血
|
| 483 |
+
刺
|
| 484 |
+
天线
|
| 485 |
+
小行星
|
| 486 |
+
墙纸
|
| 487 |
+
压力
|
| 488 |
+
假期
|
| 489 |
+
欢呼
|
| 490 |
+
书呆子
|
| 491 |
+
人工智能
|
| 492 |
+
研究
|
| 493 |
+
免疫系统
|
| 494 |
+
仙女
|
| 495 |
+
马蜂窝
|
| 496 |
+
比萨斜塔
|
| 497 |
+
北极光
|
| 498 |
+
灰尘
|
| 499 |
+
打嗝
|
| 500 |
+
鼻涕泡
|
| 501 |
+
青春痘
|
| 502 |
+
水痘
|
| 503 |
+
兔子
|
| 504 |
+
蚂蚁
|
| 505 |
+
花瓣
|
| 506 |
+
海洋
|
| 507 |
+
碗
|
| 508 |
+
水果
|
| 509 |
+
马
|
| 510 |
+
直升机
|