Felix Zieger
commited on
Commit
·
aeb9637
1
Parent(s):
818d1f7
stuff
Browse files- README.md +0 -2
- src/components/GameContainer.tsx +69 -13
- src/components/HighScoreBoard.tsx +83 -236
- src/components/game/GuessDisplay.tsx +25 -7
- src/components/game/WelcomeScreen.tsx +27 -86
- src/components/game/guess-display/ActionButtons.tsx +49 -45
- src/components/game/leaderboard/LeaderboardHeader.tsx +25 -0
- src/components/game/leaderboard/LeaderboardPagination.tsx +58 -0
- src/components/game/leaderboard/ScoreSubmissionForm.tsx +46 -0
- src/components/game/leaderboard/ScoresTable.tsx +77 -0
- src/components/game/leaderboard/ThemeFilter.tsx +32 -0
- src/components/game/sentence-builder/InputForm.tsx +1 -2
- src/components/game/welcome/ContestSection.tsx +45 -0
- src/components/game/welcome/HowToPlayDialog.tsx +41 -0
- src/components/game/welcome/HuggingFaceLink.tsx +19 -0
- src/components/game/welcome/MainActions.tsx +39 -0
- src/i18n/translations/de.ts +103 -78
- src/i18n/translations/en.ts +32 -7
- src/i18n/translations/es.ts +35 -10
- src/i18n/translations/fr.ts +103 -78
- src/i18n/translations/it.ts +105 -80
- src/integrations/supabase/types.ts +13 -1
- src/lib/words-food.ts +230 -0
- src/lib/words-sports.ts +250 -0
- src/lib/words-standard.ts +1160 -0
- src/lib/words.ts +0 -337
- supabase/functions/submit-high-score/index.ts +61 -0
- supabase/functions/validate-sentence/index.ts +0 -56
README.md
CHANGED
@@ -17,8 +17,6 @@ However, you can only say one word at a time, taking turns with another AI.
|
|
17 |
|
18 |
You need Node.js and npm installed on your system.
|
19 |
|
20 |
-
Add a .env file with `VITE_MISTRAL_API_KEY=xxx` to query Mistral LLM from your local environment.
|
21 |
-
|
22 |
```
|
23 |
npm i
|
24 |
npm run dev
|
|
|
17 |
|
18 |
You need Node.js and npm installed on your system.
|
19 |
|
|
|
|
|
20 |
```
|
21 |
npm i
|
22 |
npm run dev
|
src/components/GameContainer.tsx
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
import { useState, KeyboardEvent, useEffect, useContext } from "react";
|
2 |
-
import { getRandomWord } from "@/lib/words";
|
|
|
|
|
3 |
import { motion } from "framer-motion";
|
4 |
import { generateAIResponse, guessWord } from "@/services/mistralService";
|
5 |
import { getThemedWord } from "@/services/themeService";
|
@@ -10,6 +12,7 @@ import { SentenceBuilder } from "./game/SentenceBuilder";
|
|
10 |
import { GuessDisplay } from "./game/GuessDisplay";
|
11 |
import { useTranslation } from "@/hooks/useTranslation";
|
12 |
import { LanguageContext } from "@/contexts/LanguageContext";
|
|
|
13 |
|
14 |
type GameState = "welcome" | "theme-selection" | "building-sentence" | "showing-guess";
|
15 |
|
@@ -25,6 +28,7 @@ export const GameContainer = () => {
|
|
25 |
const [totalWords, setTotalWords] = useState<number>(0);
|
26 |
const [usedWords, setUsedWords] = useState<string[]>([]);
|
27 |
const [sessionId, setSessionId] = useState<string>("");
|
|
|
28 |
const { toast } = useToast();
|
29 |
const t = useTranslation();
|
30 |
const { language } = useContext(LanguageContext);
|
@@ -37,13 +41,14 @@ export const GameContainer = () => {
|
|
37 |
|
38 |
useEffect(() => {
|
39 |
const handleKeyPress = (e: KeyboardEvent) => {
|
40 |
-
if (e.key === 'Enter') {
|
41 |
if (gameState === 'welcome') {
|
42 |
handleStart();
|
43 |
} else if (gameState === 'showing-guess') {
|
44 |
-
|
45 |
-
if (correct) {
|
46 |
handleNextRound();
|
|
|
|
|
47 |
}
|
48 |
}
|
49 |
}
|
@@ -51,7 +56,7 @@ export const GameContainer = () => {
|
|
51 |
|
52 |
window.addEventListener('keydown', handleKeyPress as any);
|
53 |
return () => window.removeEventListener('keydown', handleKeyPress as any);
|
54 |
-
}, [gameState, aiGuess, currentWord]);
|
55 |
|
56 |
const handleStart = () => {
|
57 |
setGameState("theme-selection");
|
@@ -72,9 +77,20 @@ export const GameContainer = () => {
|
|
72 |
const handleThemeSelect = async (theme: string) => {
|
73 |
setCurrentTheme(theme);
|
74 |
try {
|
75 |
-
|
76 |
-
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
setCurrentWord(word);
|
79 |
setGameState("building-sentence");
|
80 |
setSuccessfulRounds(0);
|
@@ -119,6 +135,28 @@ export const GameContainer = () => {
|
|
119 |
}
|
120 |
};
|
121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
const handleMakeGuess = async () => {
|
123 |
setIsAiThinking(true);
|
124 |
try {
|
@@ -135,6 +173,11 @@ export const GameContainer = () => {
|
|
135 |
const sentenceString = finalSentence.join(' ');
|
136 |
const guess = await guessWord(sentenceString, language);
|
137 |
setAiGuess(guess);
|
|
|
|
|
|
|
|
|
|
|
138 |
setGameState("showing-guess");
|
139 |
} catch (error) {
|
140 |
console.error('Error getting AI guess:', error);
|
@@ -152,9 +195,20 @@ export const GameContainer = () => {
|
|
152 |
if (handleGuessComplete()) {
|
153 |
const getNewWord = async () => {
|
154 |
try {
|
155 |
-
|
156 |
-
|
157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
setCurrentWord(word);
|
159 |
setGameState("building-sentence");
|
160 |
setSentence([]);
|
@@ -232,13 +286,15 @@ export const GameContainer = () => {
|
|
232 |
currentWord={currentWord}
|
233 |
onNextRound={handleNextRound}
|
234 |
onPlayAgain={handlePlayAgain}
|
|
|
235 |
currentScore={successfulRounds}
|
236 |
avgWordsPerRound={getAverageWordsPerRound()}
|
237 |
sessionId={sessionId}
|
238 |
-
|
|
|
239 |
/>
|
240 |
)}
|
241 |
</motion.div>
|
242 |
</div>
|
243 |
);
|
244 |
-
};
|
|
|
1 |
import { useState, KeyboardEvent, useEffect, useContext } from "react";
|
2 |
+
import { getRandomWord } from "@/lib/words-standard";
|
3 |
+
import { getRandomSportsWord } from "@/lib/words-sports";
|
4 |
+
import { getRandomFoodWord } from "@/lib/words-food";
|
5 |
import { motion } from "framer-motion";
|
6 |
import { generateAIResponse, guessWord } from "@/services/mistralService";
|
7 |
import { getThemedWord } from "@/services/themeService";
|
|
|
12 |
import { GuessDisplay } from "./game/GuessDisplay";
|
13 |
import { useTranslation } from "@/hooks/useTranslation";
|
14 |
import { LanguageContext } from "@/contexts/LanguageContext";
|
15 |
+
import { supabase } from "@/integrations/supabase/client";
|
16 |
|
17 |
type GameState = "welcome" | "theme-selection" | "building-sentence" | "showing-guess";
|
18 |
|
|
|
28 |
const [totalWords, setTotalWords] = useState<number>(0);
|
29 |
const [usedWords, setUsedWords] = useState<string[]>([]);
|
30 |
const [sessionId, setSessionId] = useState<string>("");
|
31 |
+
const [isHighScoreDialogOpen, setIsHighScoreDialogOpen] = useState(false);
|
32 |
const { toast } = useToast();
|
33 |
const t = useTranslation();
|
34 |
const { language } = useContext(LanguageContext);
|
|
|
41 |
|
42 |
useEffect(() => {
|
43 |
const handleKeyPress = (e: KeyboardEvent) => {
|
44 |
+
if (e.key === 'Enter' && !isHighScoreDialogOpen) {
|
45 |
if (gameState === 'welcome') {
|
46 |
handleStart();
|
47 |
} else if (gameState === 'showing-guess') {
|
48 |
+
if (isGuessCorrect()) {
|
|
|
49 |
handleNextRound();
|
50 |
+
} else {
|
51 |
+
handlePlayAgain();
|
52 |
}
|
53 |
}
|
54 |
}
|
|
|
56 |
|
57 |
window.addEventListener('keydown', handleKeyPress as any);
|
58 |
return () => window.removeEventListener('keydown', handleKeyPress as any);
|
59 |
+
}, [gameState, aiGuess, currentWord, isHighScoreDialogOpen]);
|
60 |
|
61 |
const handleStart = () => {
|
62 |
setGameState("theme-selection");
|
|
|
77 |
const handleThemeSelect = async (theme: string) => {
|
78 |
setCurrentTheme(theme);
|
79 |
try {
|
80 |
+
let word;
|
81 |
+
switch (theme) {
|
82 |
+
case "sports":
|
83 |
+
word = getRandomSportsWord(language);
|
84 |
+
break;
|
85 |
+
case "food":
|
86 |
+
word = getRandomFoodWord(language);
|
87 |
+
break;
|
88 |
+
case "standard":
|
89 |
+
word = getRandomWord(language);
|
90 |
+
break;
|
91 |
+
default:
|
92 |
+
word = await getThemedWord(theme, usedWords, language);
|
93 |
+
}
|
94 |
setCurrentWord(word);
|
95 |
setGameState("building-sentence");
|
96 |
setSuccessfulRounds(0);
|
|
|
135 |
}
|
136 |
};
|
137 |
|
138 |
+
const saveGameResult = async (sentence: string[], aiGuess: string, isCorrect: boolean) => {
|
139 |
+
try {
|
140 |
+
const { error } = await supabase
|
141 |
+
.from('game_results')
|
142 |
+
.insert({
|
143 |
+
target_word: currentWord,
|
144 |
+
description: sentence.join(' '),
|
145 |
+
ai_guess: aiGuess,
|
146 |
+
is_correct: isCorrect,
|
147 |
+
session_id: sessionId
|
148 |
+
});
|
149 |
+
|
150 |
+
if (error) {
|
151 |
+
console.error('Error saving game result:', error);
|
152 |
+
} else {
|
153 |
+
console.log('Game result saved successfully');
|
154 |
+
}
|
155 |
+
} catch (error) {
|
156 |
+
console.error('Error saving game result:', error);
|
157 |
+
}
|
158 |
+
};
|
159 |
+
|
160 |
const handleMakeGuess = async () => {
|
161 |
setIsAiThinking(true);
|
162 |
try {
|
|
|
173 |
const sentenceString = finalSentence.join(' ');
|
174 |
const guess = await guessWord(sentenceString, language);
|
175 |
setAiGuess(guess);
|
176 |
+
|
177 |
+
// Save game result in the background
|
178 |
+
saveGameResult(finalSentence, guess, guess.toLowerCase() === currentWord.toLowerCase())
|
179 |
+
.catch(error => console.error('Background save failed:', error));
|
180 |
+
|
181 |
setGameState("showing-guess");
|
182 |
} catch (error) {
|
183 |
console.error('Error getting AI guess:', error);
|
|
|
195 |
if (handleGuessComplete()) {
|
196 |
const getNewWord = async () => {
|
197 |
try {
|
198 |
+
let word;
|
199 |
+
switch (currentTheme) {
|
200 |
+
case "sports":
|
201 |
+
word = getRandomSportsWord(language);
|
202 |
+
break;
|
203 |
+
case "food":
|
204 |
+
word = getRandomFoodWord(language);
|
205 |
+
break;
|
206 |
+
case "standard":
|
207 |
+
word = getRandomWord(language);
|
208 |
+
break;
|
209 |
+
default:
|
210 |
+
word = await getThemedWord(currentTheme, usedWords, language);
|
211 |
+
}
|
212 |
setCurrentWord(word);
|
213 |
setGameState("building-sentence");
|
214 |
setSentence([]);
|
|
|
286 |
currentWord={currentWord}
|
287 |
onNextRound={handleNextRound}
|
288 |
onPlayAgain={handlePlayAgain}
|
289 |
+
onBack={handleBack}
|
290 |
currentScore={successfulRounds}
|
291 |
avgWordsPerRound={getAverageWordsPerRound()}
|
292 |
sessionId={sessionId}
|
293 |
+
currentTheme={currentTheme}
|
294 |
+
onHighScoreDialogChange={setIsHighScoreDialogOpen}
|
295 |
/>
|
296 |
)}
|
297 |
</motion.div>
|
298 |
</div>
|
299 |
);
|
300 |
+
};
|
src/components/HighScoreBoard.tsx
CHANGED
@@ -1,26 +1,13 @@
|
|
1 |
-
import {
|
2 |
-
import { Button } from "@/components/ui/button";
|
3 |
-
import { Input } from "@/components/ui/input";
|
4 |
import { supabase } from "@/integrations/supabase/client";
|
5 |
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
6 |
-
import {
|
7 |
-
Table,
|
8 |
-
TableBody,
|
9 |
-
TableCell,
|
10 |
-
TableHead,
|
11 |
-
TableHeader,
|
12 |
-
TableRow,
|
13 |
-
} from "@/components/ui/table";
|
14 |
import { useToast } from "@/components/ui/use-toast";
|
15 |
-
import {
|
16 |
-
Pagination,
|
17 |
-
PaginationContent,
|
18 |
-
PaginationItem,
|
19 |
-
PaginationLink,
|
20 |
-
PaginationNext,
|
21 |
-
PaginationPrevious,
|
22 |
-
} from "@/components/ui/pagination";
|
23 |
import { useTranslation } from "@/hooks/useTranslation";
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
interface HighScore {
|
26 |
id: string;
|
@@ -29,55 +16,52 @@ interface HighScore {
|
|
29 |
avg_words_per_round: number;
|
30 |
created_at: string;
|
31 |
session_id: string;
|
|
|
32 |
}
|
33 |
|
34 |
interface HighScoreBoardProps {
|
35 |
-
currentScore
|
36 |
-
avgWordsPerRound
|
37 |
-
onClose
|
38 |
-
onPlayAgain
|
39 |
-
sessionId
|
40 |
onScoreSubmitted?: () => void;
|
|
|
|
|
41 |
}
|
42 |
|
43 |
const ITEMS_PER_PAGE = 5;
|
44 |
-
const MAX_PAGES = 5;
|
45 |
-
|
46 |
-
const getRankMedal = (rank: number) => {
|
47 |
-
switch (rank) {
|
48 |
-
case 1:
|
49 |
-
return "🥇";
|
50 |
-
case 2:
|
51 |
-
return "🥈";
|
52 |
-
case 3:
|
53 |
-
return "🥉";
|
54 |
-
default:
|
55 |
-
return null;
|
56 |
-
}
|
57 |
-
};
|
58 |
|
59 |
export const HighScoreBoard = ({
|
60 |
-
currentScore,
|
61 |
-
avgWordsPerRound,
|
62 |
onClose,
|
63 |
-
sessionId,
|
64 |
onScoreSubmitted,
|
|
|
|
|
65 |
}: HighScoreBoardProps) => {
|
66 |
const [playerName, setPlayerName] = useState("");
|
67 |
const [isSubmitting, setIsSubmitting] = useState(false);
|
68 |
const [hasSubmitted, setHasSubmitted] = useState(false);
|
69 |
const [currentPage, setCurrentPage] = useState(1);
|
|
|
|
|
|
|
70 |
const { toast } = useToast();
|
71 |
const t = useTranslation();
|
72 |
const queryClient = useQueryClient();
|
73 |
|
74 |
-
const
|
75 |
-
|
|
|
|
|
76 |
queryFn: async () => {
|
77 |
-
console.log("Fetching high scores
|
78 |
const { data, error } = await supabase
|
79 |
.from("high_scores")
|
80 |
.select("*")
|
|
|
81 |
.order("score", { ascending: false })
|
82 |
.order("avg_words_per_round", { ascending: true });
|
83 |
|
@@ -120,78 +104,35 @@ export const HighScoreBoard = ({
|
|
120 |
|
121 |
setIsSubmitting(true);
|
122 |
try {
|
123 |
-
console.log("
|
124 |
-
const { data
|
125 |
-
|
126 |
-
|
127 |
-
|
|
|
|
|
|
|
|
|
|
|
128 |
|
129 |
-
if (
|
130 |
-
console.error("Error
|
131 |
-
throw
|
132 |
}
|
133 |
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
const { error: updateError } = await supabase
|
141 |
-
.from("high_scores")
|
142 |
-
.update({
|
143 |
-
score: currentScore,
|
144 |
-
avg_words_per_round: avgWordsPerRound,
|
145 |
-
session_id: sessionId
|
146 |
-
})
|
147 |
-
.eq("id", existingScore.id);
|
148 |
-
|
149 |
-
if (updateError) {
|
150 |
-
console.error("Error updating score:", updateError);
|
151 |
-
throw updateError;
|
152 |
-
}
|
153 |
-
|
154 |
-
toast({
|
155 |
-
title: t.leaderboard.error.newHighScore,
|
156 |
-
description: t.leaderboard.error.beatRecord.replace(
|
157 |
-
"{score}",
|
158 |
-
String(existingScore.score)
|
159 |
-
),
|
160 |
-
});
|
161 |
-
|
162 |
-
console.log("Score updated successfully");
|
163 |
-
await queryClient.invalidateQueries({ queryKey: ["highScores"] });
|
164 |
-
} else {
|
165 |
-
toast({
|
166 |
-
title: t.leaderboard.error.notHigher
|
167 |
-
.replace("{current}", String(currentScore))
|
168 |
-
.replace("{best}", String(existingScore.score)),
|
169 |
-
variant: "destructive",
|
170 |
-
});
|
171 |
-
setIsSubmitting(false);
|
172 |
-
return;
|
173 |
-
}
|
174 |
-
} else {
|
175 |
-
console.log("Inserting new score...");
|
176 |
-
const { error: insertError } = await supabase.from("high_scores").insert({
|
177 |
-
player_name: playerName.trim(),
|
178 |
-
score: currentScore,
|
179 |
-
avg_words_per_round: avgWordsPerRound,
|
180 |
-
session_id: sessionId
|
181 |
});
|
182 |
-
|
183 |
-
if (insertError) {
|
184 |
-
console.error("Error inserting score:", insertError);
|
185 |
-
throw insertError;
|
186 |
-
}
|
187 |
|
188 |
-
|
|
|
|
|
189 |
await queryClient.invalidateQueries({ queryKey: ["highScores"] });
|
190 |
}
|
191 |
-
|
192 |
-
setHasSubmitted(true);
|
193 |
-
onScoreSubmitted?.();
|
194 |
-
setPlayerName("");
|
195 |
} catch (error) {
|
196 |
console.error("Error submitting score:", error);
|
197 |
toast({
|
@@ -211,141 +152,47 @@ export const HighScoreBoard = ({
|
|
211 |
}
|
212 |
};
|
213 |
|
214 |
-
const totalPages = highScores ? Math.
|
215 |
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
216 |
const paginatedScores = highScores?.slice(startIndex, startIndex + ITEMS_PER_PAGE);
|
217 |
|
218 |
-
const handlePreviousPage = () => {
|
219 |
-
if (currentPage > 1) {
|
220 |
-
setCurrentPage(p => p - 1);
|
221 |
-
}
|
222 |
-
};
|
223 |
-
|
224 |
-
const handleNextPage = () => {
|
225 |
-
if (currentPage < totalPages) {
|
226 |
-
setCurrentPage(p => p + 1);
|
227 |
-
}
|
228 |
-
};
|
229 |
-
|
230 |
-
useEffect(() => {
|
231 |
-
const handleKeyDown = (e: KeyboardEvent) => {
|
232 |
-
if (e.key === 'ArrowLeft') {
|
233 |
-
handlePreviousPage();
|
234 |
-
} else if (e.key === 'ArrowRight') {
|
235 |
-
handleNextPage();
|
236 |
-
}
|
237 |
-
};
|
238 |
-
|
239 |
-
window.addEventListener('keydown', handleKeyDown);
|
240 |
-
return () => window.removeEventListener('keydown', handleKeyDown);
|
241 |
-
}, [currentPage, totalPages]);
|
242 |
-
|
243 |
return (
|
244 |
<div className="space-y-6">
|
245 |
-
<
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
|
|
|
|
|
|
|
|
253 |
|
254 |
{!hasSubmitted && currentScore > 0 && (
|
255 |
-
<
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
onKeyDown={handleKeyDown}
|
264 |
-
className="flex-1"
|
265 |
-
maxLength={20}
|
266 |
-
/>
|
267 |
-
<Button
|
268 |
-
onClick={handleSubmitScore}
|
269 |
-
disabled={isSubmitting || !playerName.trim() || hasSubmitted}
|
270 |
-
>
|
271 |
-
{isSubmitting ? t.leaderboard.submitting : t.leaderboard.submit}
|
272 |
-
</Button>
|
273 |
-
</div>
|
274 |
)}
|
275 |
|
276 |
-
<
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
{paginatedScores?.map((score, index) => {
|
288 |
-
const absoluteRank = startIndex + index + 1;
|
289 |
-
const medal = getRankMedal(absoluteRank);
|
290 |
-
return (
|
291 |
-
<TableRow key={score.id}>
|
292 |
-
<TableCell>
|
293 |
-
{absoluteRank}
|
294 |
-
{medal && <span className="ml-2">{medal}</span>}
|
295 |
-
</TableCell>
|
296 |
-
<TableCell>{score.player_name}</TableCell>
|
297 |
-
<TableCell>{score.score}</TableCell>
|
298 |
-
<TableCell>{score.avg_words_per_round.toFixed(1)}</TableCell>
|
299 |
-
</TableRow>
|
300 |
-
);
|
301 |
-
})}
|
302 |
-
{!paginatedScores?.length && (
|
303 |
-
<TableRow>
|
304 |
-
<TableCell colSpan={4} className="text-center">
|
305 |
-
{t.leaderboard.noScores}
|
306 |
-
</TableCell>
|
307 |
-
</TableRow>
|
308 |
-
)}
|
309 |
-
</TableBody>
|
310 |
-
</Table>
|
311 |
-
</div>
|
312 |
-
|
313 |
-
{totalPages > 1 && (
|
314 |
-
<Pagination>
|
315 |
-
<PaginationContent>
|
316 |
-
<PaginationItem>
|
317 |
-
<PaginationPrevious
|
318 |
-
onClick={handlePreviousPage}
|
319 |
-
className={currentPage === 1 ? "pointer-events-none opacity-50" : ""}
|
320 |
-
>
|
321 |
-
<span className="hidden sm:inline">{t.leaderboard.previous}</span>
|
322 |
-
<span className="text-xs text-muted-foreground ml-1">←</span>
|
323 |
-
</PaginationPrevious>
|
324 |
-
</PaginationItem>
|
325 |
-
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
|
326 |
-
<PaginationItem key={page}>
|
327 |
-
<PaginationLink
|
328 |
-
onClick={() => setCurrentPage(page)}
|
329 |
-
isActive={currentPage === page}
|
330 |
-
>
|
331 |
-
{page}
|
332 |
-
</PaginationLink>
|
333 |
-
</PaginationItem>
|
334 |
-
))}
|
335 |
-
<PaginationItem>
|
336 |
-
<PaginationNext
|
337 |
-
onClick={handleNextPage}
|
338 |
-
className={
|
339 |
-
currentPage === totalPages ? "pointer-events-none opacity-50" : ""
|
340 |
-
}
|
341 |
-
>
|
342 |
-
<span className="hidden sm:inline">{t.leaderboard.next}</span>
|
343 |
-
<span className="text-xs text-muted-foreground ml-1">→</span>
|
344 |
-
</PaginationNext>
|
345 |
-
</PaginationItem>
|
346 |
-
</PaginationContent>
|
347 |
-
</Pagination>
|
348 |
-
)}
|
349 |
</div>
|
350 |
);
|
351 |
-
};
|
|
|
1 |
+
import { useState } from "react";
|
|
|
|
|
2 |
import { supabase } from "@/integrations/supabase/client";
|
3 |
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import { useToast } from "@/components/ui/use-toast";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
import { useTranslation } from "@/hooks/useTranslation";
|
6 |
+
import { ThemeFilter } from "./game/leaderboard/ThemeFilter";
|
7 |
+
import { ScoreSubmissionForm } from "./game/leaderboard/ScoreSubmissionForm";
|
8 |
+
import { ScoresTable } from "./game/leaderboard/ScoresTable";
|
9 |
+
import { LeaderboardHeader } from "./game/leaderboard/LeaderboardHeader";
|
10 |
+
import { LeaderboardPagination } from "./game/leaderboard/LeaderboardPagination";
|
11 |
|
12 |
interface HighScore {
|
13 |
id: string;
|
|
|
16 |
avg_words_per_round: number;
|
17 |
created_at: string;
|
18 |
session_id: string;
|
19 |
+
theme: string;
|
20 |
}
|
21 |
|
22 |
interface HighScoreBoardProps {
|
23 |
+
currentScore?: number;
|
24 |
+
avgWordsPerRound?: number;
|
25 |
+
onClose?: () => void;
|
26 |
+
onPlayAgain?: () => void;
|
27 |
+
sessionId?: string;
|
28 |
onScoreSubmitted?: () => void;
|
29 |
+
showThemeFilter?: boolean;
|
30 |
+
initialTheme?: string;
|
31 |
}
|
32 |
|
33 |
const ITEMS_PER_PAGE = 5;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
export const HighScoreBoard = ({
|
36 |
+
currentScore = 0,
|
37 |
+
avgWordsPerRound = 0,
|
38 |
onClose,
|
39 |
+
sessionId = "",
|
40 |
onScoreSubmitted,
|
41 |
+
showThemeFilter = true,
|
42 |
+
initialTheme = "standard",
|
43 |
}: HighScoreBoardProps) => {
|
44 |
const [playerName, setPlayerName] = useState("");
|
45 |
const [isSubmitting, setIsSubmitting] = useState(false);
|
46 |
const [hasSubmitted, setHasSubmitted] = useState(false);
|
47 |
const [currentPage, setCurrentPage] = useState(1);
|
48 |
+
const [selectedTheme, setSelectedTheme] = useState<'standard' | 'sports' | 'food' | 'custom'>(
|
49 |
+
initialTheme as 'standard' | 'sports' | 'food' | 'custom'
|
50 |
+
);
|
51 |
const { toast } = useToast();
|
52 |
const t = useTranslation();
|
53 |
const queryClient = useQueryClient();
|
54 |
|
55 |
+
const showScoreInfo = sessionId !== "" && currentScore > 0;
|
56 |
+
|
57 |
+
const { data: highScores } = useQuery({
|
58 |
+
queryKey: ["highScores", selectedTheme],
|
59 |
queryFn: async () => {
|
60 |
+
console.log("Fetching high scores for theme:", selectedTheme);
|
61 |
const { data, error } = await supabase
|
62 |
.from("high_scores")
|
63 |
.select("*")
|
64 |
+
.eq('theme', selectedTheme)
|
65 |
.order("score", { ascending: false })
|
66 |
.order("avg_words_per_round", { ascending: true });
|
67 |
|
|
|
104 |
|
105 |
setIsSubmitting(true);
|
106 |
try {
|
107 |
+
console.log("Submitting score via Edge Function...");
|
108 |
+
const { data, error } = await supabase.functions.invoke('submit-high-score', {
|
109 |
+
body: {
|
110 |
+
playerName: playerName.trim(),
|
111 |
+
score: currentScore,
|
112 |
+
avgWordsPerRound,
|
113 |
+
sessionId,
|
114 |
+
theme: selectedTheme
|
115 |
+
}
|
116 |
+
});
|
117 |
|
118 |
+
if (error) {
|
119 |
+
console.error("Error submitting score:", error);
|
120 |
+
throw error;
|
121 |
}
|
122 |
|
123 |
+
console.log("Score submitted successfully:", data);
|
124 |
+
|
125 |
+
if (data.success) {
|
126 |
+
toast({
|
127 |
+
title: t.leaderboard.success,
|
128 |
+
description: t.leaderboard.success,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
});
|
|
|
|
|
|
|
|
|
|
|
130 |
|
131 |
+
setHasSubmitted(true);
|
132 |
+
onScoreSubmitted?.();
|
133 |
+
setPlayerName("");
|
134 |
await queryClient.invalidateQueries({ queryKey: ["highScores"] });
|
135 |
}
|
|
|
|
|
|
|
|
|
136 |
} catch (error) {
|
137 |
console.error("Error submitting score:", error);
|
138 |
toast({
|
|
|
152 |
}
|
153 |
};
|
154 |
|
155 |
+
const totalPages = highScores ? Math.ceil(highScores.length / ITEMS_PER_PAGE) : 0;
|
156 |
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
157 |
const paginatedScores = highScores?.slice(startIndex, startIndex + ITEMS_PER_PAGE);
|
158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
return (
|
160 |
<div className="space-y-6">
|
161 |
+
<LeaderboardHeader
|
162 |
+
currentScore={currentScore}
|
163 |
+
avgWordsPerRound={avgWordsPerRound}
|
164 |
+
showScoreInfo={showScoreInfo}
|
165 |
+
/>
|
166 |
+
|
167 |
+
{showThemeFilter && (
|
168 |
+
<ThemeFilter
|
169 |
+
selectedTheme={selectedTheme}
|
170 |
+
onThemeChange={setSelectedTheme}
|
171 |
+
/>
|
172 |
+
)}
|
173 |
|
174 |
{!hasSubmitted && currentScore > 0 && (
|
175 |
+
<ScoreSubmissionForm
|
176 |
+
playerName={playerName}
|
177 |
+
setPlayerName={setPlayerName}
|
178 |
+
isSubmitting={isSubmitting}
|
179 |
+
hasSubmitted={hasSubmitted}
|
180 |
+
onSubmit={handleSubmitScore}
|
181 |
+
onKeyDown={handleKeyDown}
|
182 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
)}
|
184 |
|
185 |
+
<ScoresTable
|
186 |
+
scores={paginatedScores || []}
|
187 |
+
startIndex={startIndex}
|
188 |
+
/>
|
189 |
+
|
190 |
+
<LeaderboardPagination
|
191 |
+
currentPage={currentPage}
|
192 |
+
totalPages={totalPages}
|
193 |
+
onPreviousPage={() => setCurrentPage(p => Math.max(1, p - 1))}
|
194 |
+
onNextPage={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
|
195 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
</div>
|
197 |
);
|
198 |
+
};
|
src/components/game/GuessDisplay.tsx
CHANGED
@@ -24,6 +24,8 @@ interface GuessDisplayProps {
|
|
24 |
currentScore: number;
|
25 |
avgWordsPerRound: number;
|
26 |
sessionId: string;
|
|
|
|
|
27 |
}
|
28 |
|
29 |
export const GuessDisplay = ({
|
@@ -36,26 +38,25 @@ export const GuessDisplay = ({
|
|
36 |
currentScore,
|
37 |
avgWordsPerRound,
|
38 |
sessionId,
|
|
|
|
|
39 |
}: GuessDisplayProps) => {
|
40 |
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
41 |
const [hasSubmittedScore, setHasSubmittedScore] = useState(false);
|
|
|
42 |
const t = useTranslation();
|
43 |
|
44 |
-
|
|
|
|
|
45 |
|
46 |
const handleSetShowConfirmDialog = (show: boolean) => {
|
47 |
-
console.log("GuessDisplay - Setting showConfirmDialog to:", show);
|
48 |
setShowConfirmDialog(show);
|
49 |
};
|
50 |
|
51 |
-
useEffect(() => {
|
52 |
-
console.log("GuessDisplay - showConfirmDialog changed to:", showConfirmDialog);
|
53 |
-
}, [showConfirmDialog]);
|
54 |
-
|
55 |
const isGuessCorrect = () => aiGuess.toLowerCase() === currentWord.toLowerCase();
|
56 |
|
57 |
const handleScoreSubmitted = () => {
|
58 |
-
console.log('Score submitted, updating state');
|
59 |
setHasSubmittedScore(true);
|
60 |
};
|
61 |
|
@@ -85,8 +86,25 @@ export const GuessDisplay = ({
|
|
85 |
currentScore={currentScore}
|
86 |
avgWordsPerRound={avgWordsPerRound}
|
87 |
sessionId={sessionId}
|
|
|
88 |
onScoreSubmitted={handleScoreSubmitted}
|
|
|
89 |
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
</motion.div>
|
91 |
);
|
92 |
};
|
|
|
24 |
currentScore: number;
|
25 |
avgWordsPerRound: number;
|
26 |
sessionId: string;
|
27 |
+
currentTheme: string;
|
28 |
+
onHighScoreDialogChange?: (isOpen: boolean) => void;
|
29 |
}
|
30 |
|
31 |
export const GuessDisplay = ({
|
|
|
38 |
currentScore,
|
39 |
avgWordsPerRound,
|
40 |
sessionId,
|
41 |
+
currentTheme,
|
42 |
+
onHighScoreDialogChange,
|
43 |
}: GuessDisplayProps) => {
|
44 |
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
45 |
const [hasSubmittedScore, setHasSubmittedScore] = useState(false);
|
46 |
+
const [showHighScores, setShowHighScores] = useState(false);
|
47 |
const t = useTranslation();
|
48 |
|
49 |
+
useEffect(() => {
|
50 |
+
onHighScoreDialogChange?.(showHighScores);
|
51 |
+
}, [showHighScores, onHighScoreDialogChange]);
|
52 |
|
53 |
const handleSetShowConfirmDialog = (show: boolean) => {
|
|
|
54 |
setShowConfirmDialog(show);
|
55 |
};
|
56 |
|
|
|
|
|
|
|
|
|
57 |
const isGuessCorrect = () => aiGuess.toLowerCase() === currentWord.toLowerCase();
|
58 |
|
59 |
const handleScoreSubmitted = () => {
|
|
|
60 |
setHasSubmittedScore(true);
|
61 |
};
|
62 |
|
|
|
86 |
currentScore={currentScore}
|
87 |
avgWordsPerRound={avgWordsPerRound}
|
88 |
sessionId={sessionId}
|
89 |
+
currentTheme={currentTheme}
|
90 |
onScoreSubmitted={handleScoreSubmitted}
|
91 |
+
onHighScoreDialogChange={setShowHighScores}
|
92 |
/>
|
93 |
+
|
94 |
+
<Dialog open={showHighScores} onOpenChange={setShowHighScores}>
|
95 |
+
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-[600px]">
|
96 |
+
<HighScoreBoard
|
97 |
+
currentScore={currentScore}
|
98 |
+
avgWordsPerRound={avgWordsPerRound}
|
99 |
+
onClose={() => setShowHighScores(false)}
|
100 |
+
onPlayAgain={onPlayAgain}
|
101 |
+
sessionId={sessionId}
|
102 |
+
showThemeFilter={false}
|
103 |
+
initialTheme={currentTheme}
|
104 |
+
onScoreSubmitted={handleScoreSubmitted}
|
105 |
+
/>
|
106 |
+
</DialogContent>
|
107 |
+
</Dialog>
|
108 |
</motion.div>
|
109 |
);
|
110 |
};
|
src/components/game/WelcomeScreen.tsx
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
-
import { Button } from "@/components/ui/button";
|
2 |
import { motion } from "framer-motion";
|
3 |
import { useState } from "react";
|
4 |
import { HighScoreBoard } from "../HighScoreBoard";
|
5 |
-
import { Dialog, DialogContent
|
6 |
import { LanguageSelector } from "./LanguageSelector";
|
7 |
import { useTranslation } from "@/hooks/useTranslation";
|
8 |
-
import {
|
9 |
-
import {
|
10 |
-
import {
|
|
|
11 |
|
12 |
interface WelcomeScreenProps {
|
13 |
onStart: () => void;
|
@@ -17,7 +17,6 @@ export const WelcomeScreen = ({ onStart }: WelcomeScreenProps) => {
|
|
17 |
const [showHighScores, setShowHighScores] = useState(false);
|
18 |
const [showHowToPlay, setShowHowToPlay] = useState(false);
|
19 |
const t = useTranslation();
|
20 |
-
const { language } = useContext(LanguageContext);
|
21 |
|
22 |
return (
|
23 |
<>
|
@@ -35,103 +34,45 @@ export const WelcomeScreen = ({ onStart }: WelcomeScreenProps) => {
|
|
35 |
</p>
|
36 |
</div>
|
37 |
|
38 |
-
<
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
</Button>
|
53 |
-
<Button
|
54 |
-
onClick={() => setShowHighScores(true)}
|
55 |
-
variant="outline"
|
56 |
-
className="text-lg"
|
57 |
-
>
|
58 |
-
{t.welcome.leaderboard} 🏆
|
59 |
-
</Button>
|
60 |
-
</div>
|
61 |
-
</div>
|
62 |
</motion.div>
|
63 |
|
64 |
<motion.div
|
65 |
initial={{ opacity: 0 }}
|
66 |
animate={{ opacity: 1 }}
|
67 |
transition={{ delay: 0.2 }}
|
68 |
-
className="max-w-2xl mx-auto text-center mt-
|
69 |
>
|
70 |
-
<
|
71 |
-
{t.welcome.credits}{" "}
|
72 |
-
<a
|
73 |
-
href="https://blog.felixzieger.de/gaming-hackathon-paris/"
|
74 |
-
target="_blank"
|
75 |
-
rel="noopener noreferrer"
|
76 |
-
className="text-primary hover:underline"
|
77 |
-
>
|
78 |
-
AI Gaming Hackathon
|
79 |
-
</a>.
|
80 |
-
</p>
|
81 |
-
<div className="flex flex-col items-center gap-2">
|
82 |
-
<p className="text-sm text-gray-600">{t.welcome.helpWin}</p>
|
83 |
-
<a
|
84 |
-
href="https://huggingface.co/spaces/Mistral-AI-Game-Jam/description-improv/tree/main"
|
85 |
-
target="_blank"
|
86 |
-
rel="noopener noreferrer"
|
87 |
-
className="inline-flex flex-col items-center gap-2 px-4 py-2 text-sm font-bold text-primary hover:text-primary/90 transition-colors border border-primary/20 rounded-md hover:border-primary/40"
|
88 |
-
>
|
89 |
-
<span className="inline-flex items-center gap-2">
|
90 |
-
<Heart className="w-4 h-4" /> {t.welcome.onHuggingface}
|
91 |
-
</span>
|
92 |
-
</a>
|
93 |
-
</div>
|
94 |
</motion.div>
|
95 |
|
96 |
<Dialog open={showHighScores} onOpenChange={setShowHighScores}>
|
97 |
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-[600px]">
|
98 |
<HighScoreBoard
|
99 |
-
|
100 |
-
avgWordsPerRound={0}
|
101 |
onClose={() => setShowHighScores(false)}
|
102 |
onPlayAgain={onStart}
|
103 |
-
sessionId=""
|
104 |
/>
|
105 |
</DialogContent>
|
106 |
</Dialog>
|
107 |
|
108 |
-
<
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
</DialogHeader>
|
113 |
-
<div className="space-y-6">
|
114 |
-
<div className="grid gap-4 text-gray-600">
|
115 |
-
<div>
|
116 |
-
<h3 className="font-medium text-gray-800">{t.howToPlay.setup.title}</h3>
|
117 |
-
<p>{t.howToPlay.setup.description}</p>
|
118 |
-
</div>
|
119 |
-
<div>
|
120 |
-
<h3 className="font-medium text-gray-800">{t.howToPlay.goal.title}</h3>
|
121 |
-
<p>{t.howToPlay.goal.description}</p>
|
122 |
-
</div>
|
123 |
-
<div>
|
124 |
-
<h3 className="font-medium text-gray-800">{t.howToPlay.rules.title}</h3>
|
125 |
-
<ul className="list-disc list-inside space-y-1">
|
126 |
-
{t.howToPlay.rules.items.map((rule, index) => (
|
127 |
-
<li key={index}>{rule}</li>
|
128 |
-
))}
|
129 |
-
</ul>
|
130 |
-
</div>
|
131 |
-
</div>
|
132 |
-
</div>
|
133 |
-
</DialogContent>
|
134 |
-
</Dialog>
|
135 |
</>
|
136 |
);
|
137 |
-
};
|
|
|
|
|
1 |
import { motion } from "framer-motion";
|
2 |
import { useState } from "react";
|
3 |
import { HighScoreBoard } from "../HighScoreBoard";
|
4 |
+
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
5 |
import { LanguageSelector } from "./LanguageSelector";
|
6 |
import { useTranslation } from "@/hooks/useTranslation";
|
7 |
+
import { ContestSection } from "./welcome/ContestSection";
|
8 |
+
import { HuggingFaceLink } from "./welcome/HuggingFaceLink";
|
9 |
+
import { MainActions } from "./welcome/MainActions";
|
10 |
+
import { HowToPlayDialog } from "./welcome/HowToPlayDialog";
|
11 |
|
12 |
interface WelcomeScreenProps {
|
13 |
onStart: () => void;
|
|
|
17 |
const [showHighScores, setShowHighScores] = useState(false);
|
18 |
const [showHowToPlay, setShowHowToPlay] = useState(false);
|
19 |
const t = useTranslation();
|
|
|
20 |
|
21 |
return (
|
22 |
<>
|
|
|
34 |
</p>
|
35 |
</div>
|
36 |
|
37 |
+
<MainActions
|
38 |
+
onStart={onStart}
|
39 |
+
onShowHowToPlay={() => setShowHowToPlay(true)}
|
40 |
+
onShowHighScores={() => setShowHighScores(true)}
|
41 |
+
/>
|
42 |
+
</motion.div>
|
43 |
+
|
44 |
+
<motion.div
|
45 |
+
initial={{ opacity: 0 }}
|
46 |
+
animate={{ opacity: 1 }}
|
47 |
+
transition={{ delay: 0.1 }}
|
48 |
+
className="max-w-2xl mx-auto text-center mt-8"
|
49 |
+
>
|
50 |
+
<ContestSection />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
</motion.div>
|
52 |
|
53 |
<motion.div
|
54 |
initial={{ opacity: 0 }}
|
55 |
animate={{ opacity: 1 }}
|
56 |
transition={{ delay: 0.2 }}
|
57 |
+
className="max-w-2xl mx-auto text-center mt-8"
|
58 |
>
|
59 |
+
<HuggingFaceLink />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
</motion.div>
|
61 |
|
62 |
<Dialog open={showHighScores} onOpenChange={setShowHighScores}>
|
63 |
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-[600px]">
|
64 |
<HighScoreBoard
|
65 |
+
showThemeFilter={true}
|
|
|
66 |
onClose={() => setShowHighScores(false)}
|
67 |
onPlayAgain={onStart}
|
|
|
68 |
/>
|
69 |
</DialogContent>
|
70 |
</Dialog>
|
71 |
|
72 |
+
<HowToPlayDialog
|
73 |
+
open={showHowToPlay}
|
74 |
+
onOpenChange={setShowHowToPlay}
|
75 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
</>
|
77 |
);
|
78 |
+
};
|
src/components/game/guess-display/ActionButtons.tsx
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
import { Button } from "@/components/ui/button";
|
2 |
-
import { Dialog, DialogContent
|
3 |
-
import { HighScoreBoard } from "@/components/HighScoreBoard";
|
4 |
import { useState } from "react";
|
5 |
import { useTranslation } from "@/hooks/useTranslation";
|
|
|
6 |
|
7 |
interface ActionButtonsProps {
|
8 |
isCorrect: boolean;
|
@@ -11,7 +11,9 @@ interface ActionButtonsProps {
|
|
11 |
currentScore: number;
|
12 |
avgWordsPerRound: number;
|
13 |
sessionId: string;
|
14 |
-
|
|
|
|
|
15 |
}
|
16 |
|
17 |
export const ActionButtons = ({
|
@@ -21,52 +23,54 @@ export const ActionButtons = ({
|
|
21 |
currentScore,
|
22 |
avgWordsPerRound,
|
23 |
sessionId,
|
|
|
24 |
onScoreSubmitted,
|
|
|
25 |
}: ActionButtonsProps) => {
|
26 |
-
const [
|
27 |
const t = useTranslation();
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
return (
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
onClick={onNextRound}
|
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 |
-
</Dialog>
|
62 |
-
<Button
|
63 |
-
onClick={onPlayAgain}
|
64 |
-
className="w-full bg-primary text-lg hover:bg-primary/90"
|
65 |
-
>
|
66 |
-
{t.guess.playAgain} ⏎
|
67 |
-
</Button>
|
68 |
-
</>
|
69 |
-
)}
|
70 |
-
</div>
|
71 |
);
|
72 |
};
|
|
|
1 |
import { Button } from "@/components/ui/button";
|
2 |
+
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
|
|
3 |
import { useState } from "react";
|
4 |
import { useTranslation } from "@/hooks/useTranslation";
|
5 |
+
import { HighScoreBoard } from "../../HighScoreBoard";
|
6 |
|
7 |
interface ActionButtonsProps {
|
8 |
isCorrect: boolean;
|
|
|
11 |
currentScore: number;
|
12 |
avgWordsPerRound: number;
|
13 |
sessionId: string;
|
14 |
+
currentTheme: string;
|
15 |
+
onScoreSubmitted?: () => void;
|
16 |
+
onHighScoreDialogChange?: (isOpen: boolean) => void;
|
17 |
}
|
18 |
|
19 |
export const ActionButtons = ({
|
|
|
23 |
currentScore,
|
24 |
avgWordsPerRound,
|
25 |
sessionId,
|
26 |
+
currentTheme,
|
27 |
onScoreSubmitted,
|
28 |
+
onHighScoreDialogChange,
|
29 |
}: ActionButtonsProps) => {
|
30 |
+
const [showHighScores, setShowHighScores] = useState(false);
|
31 |
const t = useTranslation();
|
32 |
|
33 |
+
const handleShowHighScores = () => {
|
34 |
+
setShowHighScores(true);
|
35 |
+
onHighScoreDialogChange?.(true);
|
36 |
+
};
|
37 |
+
|
38 |
+
const handleCloseHighScores = () => {
|
39 |
+
setShowHighScores(false);
|
40 |
+
onHighScoreDialogChange?.(false);
|
41 |
+
};
|
42 |
+
|
43 |
return (
|
44 |
+
<>
|
45 |
+
<div className="flex justify-center gap-4">
|
46 |
+
{isCorrect ? (
|
47 |
+
<Button onClick={onNextRound} className="text-white">{t.game.nextRound} ⏎</Button>
|
48 |
+
) : (
|
49 |
+
<>
|
50 |
+
<Button onClick={onPlayAgain} className="text-white">
|
51 |
+
{t.game.playAgain} ⏎
|
52 |
+
</Button>
|
53 |
+
<Button onClick={handleShowHighScores} variant="secondary" className="text-white">
|
54 |
+
{t.game.saveScore}
|
55 |
+
</Button>
|
56 |
+
</>
|
57 |
+
)}
|
58 |
+
</div>
|
59 |
+
|
60 |
+
<Dialog open={showHighScores} onOpenChange={handleCloseHighScores}>
|
61 |
+
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-[600px]">
|
62 |
+
<HighScoreBoard
|
63 |
+
currentScore={currentScore}
|
64 |
+
avgWordsPerRound={avgWordsPerRound}
|
65 |
+
onClose={handleCloseHighScores}
|
66 |
+
onPlayAgain={onPlayAgain}
|
67 |
+
sessionId={sessionId}
|
68 |
+
showThemeFilter={false}
|
69 |
+
initialTheme={currentTheme}
|
70 |
+
onScoreSubmitted={onScoreSubmitted}
|
71 |
+
/>
|
72 |
+
</DialogContent>
|
73 |
+
</Dialog>
|
74 |
+
</>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
);
|
76 |
};
|
src/components/game/leaderboard/LeaderboardHeader.tsx
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
2 |
+
|
3 |
+
interface LeaderboardHeaderProps {
|
4 |
+
currentScore?: number;
|
5 |
+
avgWordsPerRound?: number;
|
6 |
+
showScoreInfo: boolean;
|
7 |
+
}
|
8 |
+
|
9 |
+
export const LeaderboardHeader = ({ currentScore, avgWordsPerRound, showScoreInfo }: LeaderboardHeaderProps) => {
|
10 |
+
const t = useTranslation();
|
11 |
+
|
12 |
+
return (
|
13 |
+
<div className="text-center">
|
14 |
+
<h2 className="text-2xl font-bold mb-2">{t.leaderboard.title}</h2>
|
15 |
+
{showScoreInfo && currentScore !== undefined && (
|
16 |
+
<p className="text-gray-600">
|
17 |
+
{t.leaderboard.yourScore}: {currentScore} {t.leaderboard.roundCount}
|
18 |
+
{currentScore > 0 &&
|
19 |
+
avgWordsPerRound !== undefined &&
|
20 |
+
` (${avgWordsPerRound.toFixed(1)} ${t.leaderboard.wordsPerRound})`}
|
21 |
+
</p>
|
22 |
+
)}
|
23 |
+
</div>
|
24 |
+
);
|
25 |
+
};
|
src/components/game/leaderboard/LeaderboardPagination.tsx
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Pagination,
|
3 |
+
PaginationContent,
|
4 |
+
PaginationItem,
|
5 |
+
PaginationLink,
|
6 |
+
PaginationNext,
|
7 |
+
PaginationPrevious,
|
8 |
+
} from "@/components/ui/pagination";
|
9 |
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
10 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
11 |
+
|
12 |
+
interface LeaderboardPaginationProps {
|
13 |
+
currentPage: number;
|
14 |
+
totalPages: number;
|
15 |
+
onPreviousPage: () => void;
|
16 |
+
onNextPage: () => void;
|
17 |
+
}
|
18 |
+
|
19 |
+
export const LeaderboardPagination = ({
|
20 |
+
currentPage,
|
21 |
+
totalPages,
|
22 |
+
onPreviousPage,
|
23 |
+
onNextPage,
|
24 |
+
}: LeaderboardPaginationProps) => {
|
25 |
+
const t = useTranslation();
|
26 |
+
|
27 |
+
if (totalPages <= 1) return null;
|
28 |
+
|
29 |
+
return (
|
30 |
+
<Pagination>
|
31 |
+
<PaginationContent>
|
32 |
+
<PaginationItem>
|
33 |
+
<PaginationPrevious
|
34 |
+
onClick={onPreviousPage}
|
35 |
+
className={currentPage === 1 ? "pointer-events-none opacity-50" : ""}
|
36 |
+
>
|
37 |
+
<ChevronLeft className="h-4 w-4" />
|
38 |
+
<span className="sr-only">{t.leaderboard.previous}</span>
|
39 |
+
</PaginationPrevious>
|
40 |
+
</PaginationItem>
|
41 |
+
<PaginationItem>
|
42 |
+
<PaginationLink isActive={true}>
|
43 |
+
{currentPage}
|
44 |
+
</PaginationLink>
|
45 |
+
</PaginationItem>
|
46 |
+
<PaginationItem>
|
47 |
+
<PaginationNext
|
48 |
+
onClick={onNextPage}
|
49 |
+
className={currentPage === totalPages ? "pointer-events-none opacity-50" : ""}
|
50 |
+
>
|
51 |
+
<span className="sr-only">{t.leaderboard.next}</span>
|
52 |
+
<ChevronRight className="h-4 w-4" />
|
53 |
+
</PaginationNext>
|
54 |
+
</PaginationItem>
|
55 |
+
</PaginationContent>
|
56 |
+
</Pagination>
|
57 |
+
);
|
58 |
+
};
|
src/components/game/leaderboard/ScoreSubmissionForm.tsx
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Button } from "@/components/ui/button";
|
2 |
+
import { Input } from "@/components/ui/input";
|
3 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
4 |
+
|
5 |
+
interface ScoreSubmissionFormProps {
|
6 |
+
playerName: string;
|
7 |
+
setPlayerName: (name: string) => void;
|
8 |
+
isSubmitting: boolean;
|
9 |
+
hasSubmitted: boolean;
|
10 |
+
onSubmit: () => Promise<void>;
|
11 |
+
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
12 |
+
}
|
13 |
+
|
14 |
+
export const ScoreSubmissionForm = ({
|
15 |
+
playerName,
|
16 |
+
setPlayerName,
|
17 |
+
isSubmitting,
|
18 |
+
hasSubmitted,
|
19 |
+
onSubmit,
|
20 |
+
onKeyDown,
|
21 |
+
}: ScoreSubmissionFormProps) => {
|
22 |
+
const t = useTranslation();
|
23 |
+
|
24 |
+
return (
|
25 |
+
<div className="flex gap-4 mb-6">
|
26 |
+
<Input
|
27 |
+
placeholder={t.leaderboard.enterName}
|
28 |
+
value={playerName}
|
29 |
+
onChange={(e) => {
|
30 |
+
const value = e.target.value.replace(/[^a-zA-ZÀ-ÿ0-9]/g, "");
|
31 |
+
setPlayerName(value);
|
32 |
+
}}
|
33 |
+
onKeyDown={onKeyDown}
|
34 |
+
className="flex-1"
|
35 |
+
maxLength={20}
|
36 |
+
autoComplete="words"
|
37 |
+
/>
|
38 |
+
<Button
|
39 |
+
onClick={onSubmit}
|
40 |
+
disabled={isSubmitting || !playerName.trim() || hasSubmitted}
|
41 |
+
>
|
42 |
+
{isSubmitting ? t.leaderboard.submitting : t.leaderboard.submit}
|
43 |
+
</Button>
|
44 |
+
</div>
|
45 |
+
);
|
46 |
+
};
|
src/components/game/leaderboard/ScoresTable.tsx
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import {
|
2 |
+
Table,
|
3 |
+
TableBody,
|
4 |
+
TableCell,
|
5 |
+
TableHead,
|
6 |
+
TableHeader,
|
7 |
+
TableRow,
|
8 |
+
} from "@/components/ui/table";
|
9 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
10 |
+
|
11 |
+
interface HighScore {
|
12 |
+
id: string;
|
13 |
+
player_name: string;
|
14 |
+
score: number;
|
15 |
+
avg_words_per_round: number;
|
16 |
+
created_at: string;
|
17 |
+
session_id: string;
|
18 |
+
theme: string;
|
19 |
+
}
|
20 |
+
|
21 |
+
interface ScoresTableProps {
|
22 |
+
scores: HighScore[];
|
23 |
+
startIndex: number;
|
24 |
+
}
|
25 |
+
|
26 |
+
const getRankMedal = (rank: number) => {
|
27 |
+
switch (rank) {
|
28 |
+
case 1:
|
29 |
+
return "🥇";
|
30 |
+
case 2:
|
31 |
+
return "🥈";
|
32 |
+
case 3:
|
33 |
+
return "🥉";
|
34 |
+
default:
|
35 |
+
return rank;
|
36 |
+
}
|
37 |
+
};
|
38 |
+
|
39 |
+
export const ScoresTable = ({ scores, startIndex }: ScoresTableProps) => {
|
40 |
+
const t = useTranslation();
|
41 |
+
|
42 |
+
return (
|
43 |
+
<div className="rounded-md border">
|
44 |
+
<Table>
|
45 |
+
<TableHeader>
|
46 |
+
<TableRow>
|
47 |
+
<TableHead>{t.leaderboard.rank}</TableHead>
|
48 |
+
<TableHead>{t.leaderboard.player}</TableHead>
|
49 |
+
<TableHead>{t.leaderboard.roundsColumn}</TableHead>
|
50 |
+
<TableHead>{t.leaderboard.avgWords}</TableHead>
|
51 |
+
</TableRow>
|
52 |
+
</TableHeader>
|
53 |
+
<TableBody>
|
54 |
+
{scores?.map((score, index) => {
|
55 |
+
const absoluteRank = startIndex + index + 1;
|
56 |
+
const medal = getRankMedal(absoluteRank);
|
57 |
+
return (
|
58 |
+
<TableRow key={score.id}>
|
59 |
+
<TableCell>{medal}</TableCell>
|
60 |
+
<TableCell>{score.player_name}</TableCell>
|
61 |
+
<TableCell>{score.score}</TableCell>
|
62 |
+
<TableCell>{score.avg_words_per_round.toFixed(1)}</TableCell>
|
63 |
+
</TableRow>
|
64 |
+
);
|
65 |
+
})}
|
66 |
+
{!scores?.length && (
|
67 |
+
<TableRow>
|
68 |
+
<TableCell colSpan={4} className="text-center">
|
69 |
+
{t.leaderboard.noScores}
|
70 |
+
</TableCell>
|
71 |
+
</TableRow>
|
72 |
+
)}
|
73 |
+
</TableBody>
|
74 |
+
</Table>
|
75 |
+
</div>
|
76 |
+
);
|
77 |
+
};
|
src/components/game/leaderboard/ThemeFilter.tsx
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Button } from "@/components/ui/button";
|
2 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
3 |
+
import { Filter } from "lucide-react";
|
4 |
+
|
5 |
+
type Theme = 'standard' | 'sports' | 'food' | 'custom';
|
6 |
+
|
7 |
+
interface ThemeFilterProps {
|
8 |
+
selectedTheme: Theme;
|
9 |
+
onThemeChange: (theme: Theme) => void;
|
10 |
+
}
|
11 |
+
|
12 |
+
export const ThemeFilter = ({ selectedTheme, onThemeChange }: ThemeFilterProps) => {
|
13 |
+
const t = useTranslation();
|
14 |
+
|
15 |
+
const themes: Theme[] = ['standard', 'sports', 'food', 'custom'];
|
16 |
+
|
17 |
+
return (
|
18 |
+
<div className="flex flex-wrap gap-2 mb-4 items-center">
|
19 |
+
<Filter className="h-4 w-4 text-gray-500" />
|
20 |
+
{themes.map((theme) => (
|
21 |
+
<Button
|
22 |
+
key={theme}
|
23 |
+
variant={selectedTheme === theme ? "default" : "outline"}
|
24 |
+
size="sm"
|
25 |
+
onClick={() => onThemeChange(theme)}
|
26 |
+
>
|
27 |
+
{t.themes[theme]}
|
28 |
+
</Button>
|
29 |
+
))}
|
30 |
+
</div>
|
31 |
+
);
|
32 |
+
};
|
src/components/game/sentence-builder/InputForm.tsx
CHANGED
@@ -29,7 +29,6 @@ export const InputForm = ({
|
|
29 |
const inputRef = useRef<HTMLInputElement>(null);
|
30 |
const t = useTranslation();
|
31 |
|
32 |
-
// Focus input on mount and after AI response
|
33 |
useEffect(() => {
|
34 |
if (!isAiThinking) {
|
35 |
setTimeout(() => {
|
@@ -56,7 +55,6 @@ export const InputForm = ({
|
|
56 |
|
57 |
const error = getInputError();
|
58 |
|
59 |
-
// Check if there's either something in the sentence or in the input box
|
60 |
const canMakeGuess = (sentence.length > 0 || playerInput.trim().length > 0) &&
|
61 |
!hasMultipleWords && !containsTargetWord && isValidInput && !isAiThinking;
|
62 |
|
@@ -72,6 +70,7 @@ export const InputForm = ({
|
|
72 |
placeholder={t.game.inputPlaceholder}
|
73 |
className={`w-full ${error ? 'border-red-500 focus-visible:ring-red-500' : ''}`}
|
74 |
disabled={isAiThinking}
|
|
|
75 |
/>
|
76 |
{error && (
|
77 |
<p className="text-sm text-red-500 mt-1">
|
|
|
29 |
const inputRef = useRef<HTMLInputElement>(null);
|
30 |
const t = useTranslation();
|
31 |
|
|
|
32 |
useEffect(() => {
|
33 |
if (!isAiThinking) {
|
34 |
setTimeout(() => {
|
|
|
55 |
|
56 |
const error = getInputError();
|
57 |
|
|
|
58 |
const canMakeGuess = (sentence.length > 0 || playerInput.trim().length > 0) &&
|
59 |
!hasMultipleWords && !containsTargetWord && isValidInput && !isAiThinking;
|
60 |
|
|
|
70 |
placeholder={t.game.inputPlaceholder}
|
71 |
className={`w-full ${error ? 'border-red-500 focus-visible:ring-red-500' : ''}`}
|
72 |
disabled={isAiThinking}
|
73 |
+
autoComplete="words"
|
74 |
/>
|
75 |
{error && (
|
76 |
<p className="text-sm text-red-500 mt-1">
|
src/components/game/welcome/ContestSection.tsx
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
|
2 |
+
import { Info } from "lucide-react";
|
3 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
4 |
+
|
5 |
+
export const ContestSection = () => {
|
6 |
+
const t = useTranslation();
|
7 |
+
|
8 |
+
return (
|
9 |
+
<div className="flex flex-col items-center gap-2">
|
10 |
+
<p className="text-lg font-semibold text-primary">🕹️ {t.welcome.contest.prize} 🤑</p>
|
11 |
+
<Dialog>
|
12 |
+
<DialogTrigger asChild>
|
13 |
+
<button className="inline-flex items-center text-sm text-primary/80 hover:text-primary">
|
14 |
+
{t.welcome.contest.terms} <Info className="h-4 w-4 ml-1" />
|
15 |
+
</button>
|
16 |
+
</DialogTrigger>
|
17 |
+
<DialogContent className="sm:max-w-[425px]">
|
18 |
+
<DialogHeader>
|
19 |
+
<DialogTitle>{t.welcome.contest.terms}</DialogTitle>
|
20 |
+
</DialogHeader>
|
21 |
+
<div className="space-y-4">
|
22 |
+
<p className="text-sm text-gray-600">{t.welcome.contest.howTo}</p>
|
23 |
+
<ul className="text-sm text-gray-600 list-disc list-inside">
|
24 |
+
{t.welcome.contest.conditions.map((condition, index) => (
|
25 |
+
<li key={index}>{condition}</li>
|
26 |
+
))}
|
27 |
+
</ul>
|
28 |
+
<div className="space-y-2">
|
29 |
+
<p className="text-sm text-gray-600">{t.welcome.contest.deadline}</p>
|
30 |
+
<div className="space-y-1">
|
31 |
+
<p className="text-sm font-medium text-gray-800">{t.welcome.contest.prizes.title}</p>
|
32 |
+
<ul className="text-sm text-gray-600 list-none space-y-1">
|
33 |
+
{t.welcome.contest.prizes.list.map((prize, index) => (
|
34 |
+
<li key={index}>{prize}</li>
|
35 |
+
))}
|
36 |
+
</ul>
|
37 |
+
</div>
|
38 |
+
<p className="text-sm font-medium text-red-600">{t.welcome.contest.fairPlay}</p>
|
39 |
+
</div>
|
40 |
+
</div>
|
41 |
+
</DialogContent>
|
42 |
+
</Dialog>
|
43 |
+
</div>
|
44 |
+
);
|
45 |
+
};
|
src/components/game/welcome/HowToPlayDialog.tsx
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
2 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
3 |
+
|
4 |
+
interface HowToPlayDialogProps {
|
5 |
+
open: boolean;
|
6 |
+
onOpenChange: (open: boolean) => void;
|
7 |
+
}
|
8 |
+
|
9 |
+
export const HowToPlayDialog = ({ open, onOpenChange }: HowToPlayDialogProps) => {
|
10 |
+
const t = useTranslation();
|
11 |
+
|
12 |
+
return (
|
13 |
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
14 |
+
<DialogContent className="sm:max-w-[600px]">
|
15 |
+
<DialogHeader>
|
16 |
+
<DialogTitle className="text-xl font-semibold text-primary">{t.welcome.howToPlay}</DialogTitle>
|
17 |
+
</DialogHeader>
|
18 |
+
<div className="space-y-6">
|
19 |
+
<div className="grid gap-4 text-gray-600">
|
20 |
+
<div>
|
21 |
+
<h3 className="font-medium text-gray-800">{t.howToPlay.setup.title}</h3>
|
22 |
+
<p>{t.howToPlay.setup.description}</p>
|
23 |
+
</div>
|
24 |
+
<div>
|
25 |
+
<h3 className="font-medium text-gray-800">{t.howToPlay.goal.title}</h3>
|
26 |
+
<p>{t.howToPlay.goal.description}</p>
|
27 |
+
</div>
|
28 |
+
<div>
|
29 |
+
<h3 className="font-medium text-gray-800">{t.howToPlay.rules.title}</h3>
|
30 |
+
<ul className="list-disc list-inside space-y-1">
|
31 |
+
{t.howToPlay.rules.items.map((rule, index) => (
|
32 |
+
<li key={index}>{rule}</li>
|
33 |
+
))}
|
34 |
+
</ul>
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
</DialogContent>
|
39 |
+
</Dialog>
|
40 |
+
);
|
41 |
+
};
|
src/components/game/welcome/HuggingFaceLink.tsx
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Heart } from "lucide-react";
|
2 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
3 |
+
|
4 |
+
export const HuggingFaceLink = () => {
|
5 |
+
const t = useTranslation();
|
6 |
+
|
7 |
+
return (
|
8 |
+
<div className="flex flex-col items-center gap-2">
|
9 |
+
<a
|
10 |
+
href="https://huggingface.co/spaces/Mistral-AI-Game-Jam/description-improv/tree/main"
|
11 |
+
target="_blank"
|
12 |
+
rel="noopener noreferrer"
|
13 |
+
className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary hover:text-primary/90 transition-colors border border-primary/20 rounded-md hover:border-primary/40"
|
14 |
+
>
|
15 |
+
<Heart className="w-4 h-4" /> {t.welcome.likeOnHuggingface}
|
16 |
+
</a>
|
17 |
+
</div>
|
18 |
+
);
|
19 |
+
};
|
src/components/game/welcome/MainActions.tsx
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Button } from "@/components/ui/button";
|
2 |
+
import { useTranslation } from "@/hooks/useTranslation";
|
3 |
+
|
4 |
+
interface MainActionsProps {
|
5 |
+
onStart: () => void;
|
6 |
+
onShowHowToPlay: () => void;
|
7 |
+
onShowHighScores: () => void;
|
8 |
+
}
|
9 |
+
|
10 |
+
export const MainActions = ({ onStart, onShowHowToPlay, onShowHighScores }: MainActionsProps) => {
|
11 |
+
const t = useTranslation();
|
12 |
+
|
13 |
+
return (
|
14 |
+
<div className="space-y-4">
|
15 |
+
<Button
|
16 |
+
onClick={onStart}
|
17 |
+
className="w-full bg-primary text-lg hover:bg-primary/90"
|
18 |
+
>
|
19 |
+
{t.welcome.startButton} ⏎
|
20 |
+
</Button>
|
21 |
+
<div className="grid grid-cols-2 gap-4">
|
22 |
+
<Button
|
23 |
+
onClick={onShowHowToPlay}
|
24 |
+
variant="outline"
|
25 |
+
className="text-lg"
|
26 |
+
>
|
27 |
+
{t.welcome.howToPlay} 📖
|
28 |
+
</Button>
|
29 |
+
<Button
|
30 |
+
onClick={onShowHighScores}
|
31 |
+
variant="outline"
|
32 |
+
className="text-lg"
|
33 |
+
>
|
34 |
+
{t.welcome.leaderboard} 🏆
|
35 |
+
</Button>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
);
|
39 |
+
};
|
src/i18n/translations/de.ts
CHANGED
@@ -18,85 +18,110 @@ export const de = {
|
|
18 |
leaveGameDescription: "Dein aktueller Fortschritt geht verloren. Bist du sicher, dass du das Spiel verlassen möchtest?",
|
19 |
cancel: "Abbrechen",
|
20 |
confirm: "Bestätigen",
|
21 |
-
describeWord: "Dein Ziel ist es folgendes Wort zu beschreiben"
|
|
|
|
|
|
|
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 |
-
themes: {
|
61 |
-
title: "Wähle ein Thema",
|
62 |
-
subtitle: "Wähle ein Thema für das Wort, das die KI erraten soll",
|
63 |
-
standard: "Standard",
|
64 |
-
technology: "Technologie",
|
65 |
-
sports: "Sport",
|
66 |
-
food: "Essen",
|
67 |
-
custom: "Benutzerdefiniertes Thema",
|
68 |
-
customPlaceholder: "Gib dein eigenes Thema ein...",
|
69 |
-
continue: "Weiter",
|
70 |
-
generating: "Wird generiert...",
|
71 |
-
pressKey: "Drücke"
|
72 |
-
},
|
73 |
-
welcome: {
|
74 |
-
title: "Think in Sync",
|
75 |
-
subtitle: "Arbeite mit einer KI zusammen, um einen Hinweis zu erstellen, und lass eine andere KI dein geheimes Wort erraten!",
|
76 |
-
startButton: "Spiel starten",
|
77 |
-
howToPlay: "Spielanleitung",
|
78 |
-
leaderboard: "Bestenliste",
|
79 |
-
credits: "Erstellt während des",
|
80 |
-
helpWin: "Hilf uns zu gewinnen",
|
81 |
-
onHuggingface: "mit einem Like auf Huggingface"
|
82 |
-
},
|
83 |
-
howToPlay: {
|
84 |
-
setup: {
|
85 |
-
title: "Vorbereitung",
|
86 |
-
description: "Wähle ein Thema und erhalte ein geheimes Wort, das die KI erraten soll."
|
87 |
},
|
88 |
-
|
89 |
-
title: "
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
91 |
},
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
"
|
96 |
-
|
97 |
-
|
98 |
-
"
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
}
|
101 |
-
|
102 |
-
};
|
|
|
18 |
leaveGameDescription: "Dein aktueller Fortschritt geht verloren. Bist du sicher, dass du das Spiel verlassen möchtest?",
|
19 |
cancel: "Abbrechen",
|
20 |
confirm: "Bestätigen",
|
21 |
+
describeWord: "Dein Ziel ist es folgendes Wort zu beschreiben",
|
22 |
+
nextRound: "Nächste Runde",
|
23 |
+
playAgain: "Erneut spielen",
|
24 |
+
saveScore: "Punktzahl speichern"
|
25 |
},
|
26 |
+
leaderboard: {
|
27 |
+
title: "Bestenliste",
|
28 |
+
yourScore: "Deine Punktzahl",
|
29 |
+
roundCount: "Runden",
|
30 |
+
wordsPerRound: "Wörter pro Runde",
|
31 |
+
enterName: "Gib deinen Namen ein",
|
32 |
+
submitting: "Wird übermittelt...",
|
33 |
+
submit: "Punktzahl einreichen",
|
34 |
+
rank: "Rang",
|
35 |
+
player: "Spieler",
|
36 |
+
roundsColumn: "Runden",
|
37 |
+
avgWords: "Durchschn. Wörter",
|
38 |
+
noScores: "Noch keine Punktzahlen",
|
39 |
+
previous: "Vorherige",
|
40 |
+
next: "Nächste",
|
41 |
+
success: "Punktzahl erfolgreich übermittelt!",
|
42 |
+
error: {
|
43 |
+
invalidName: "Bitte gib einen gültigen Namen ein",
|
44 |
+
noRounds: "Du musst mindestens eine Runde abschließen",
|
45 |
+
alreadySubmitted: "Punktzahl bereits eingereicht",
|
46 |
+
newHighScore: "Neuer Highscore!",
|
47 |
+
beatRecord: "Du hast deinen bisherigen Rekord von {score} geschlagen!",
|
48 |
+
notHigher: "Punktzahl von {current} nicht höher als dein Bester von {best}",
|
49 |
+
submitError: "Fehler beim Einreichen der Punktzahl"
|
50 |
+
}
|
51 |
+
},
|
52 |
+
guess: {
|
53 |
+
title: "KI-Vermutung",
|
54 |
+
goalDescription: "Dein Ziel war es folgendes Wort zu beschreiben",
|
55 |
+
providedDescription: "Du hast folgende Beschreibung gegeben",
|
56 |
+
aiGuessedDescription: "Basierend auf deiner Beschreibung hat die KI geraten",
|
57 |
+
correct: "Das ist richtig!",
|
58 |
+
incorrect: "Das ist falsch.",
|
59 |
+
nextRound: "Nächste Runde",
|
60 |
+
playAgain: "Erneut spielen",
|
61 |
+
viewLeaderboard: "In Bestenliste eintragen",
|
62 |
+
cheatingDetected: "Betrugsversuch erkannt!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
},
|
64 |
+
themes: {
|
65 |
+
title: "Wähle ein Thema",
|
66 |
+
subtitle: "Wähle ein Thema für das Wort, das die KI erraten soll",
|
67 |
+
standard: "Standard",
|
68 |
+
technology: "Technologie",
|
69 |
+
sports: "Sport",
|
70 |
+
food: "Essen",
|
71 |
+
custom: "Eigenes Thema",
|
72 |
+
customPlaceholder: "Gib dein eigenes Thema ein...",
|
73 |
+
continue: "Weiter",
|
74 |
+
generating: "Wird generiert...",
|
75 |
+
pressKey: "Drücke",
|
76 |
+
playing: "Thema"
|
77 |
+
},
|
78 |
+
welcome: {
|
79 |
+
title: "Think in Sync",
|
80 |
+
subtitle: "Arbeite mit KI zusammen, um einen Hinweis zu erstellen und lass eine andere KI dein geheimes Wort erraten!",
|
81 |
+
startButton: "Spiel starten",
|
82 |
+
howToPlay: "Spielanleitung",
|
83 |
+
leaderboard: "Bestenliste",
|
84 |
+
credits: "Erstellt während des",
|
85 |
+
contest: {
|
86 |
+
prize: "Spiele und gewinne bis zu 50€!",
|
87 |
+
terms: "Bedingungen ansehen",
|
88 |
+
howTo: "So nimmst du teil:",
|
89 |
+
conditions: [
|
90 |
+
"Spiele Think in Sync mit der Standard-Wortliste",
|
91 |
+
"Setze deinen Bestenlisten-Namen gleich deinem Hugging Face Benutzernamen",
|
92 |
+
"Like unser Projekt auf Hugging Face"
|
93 |
+
],
|
94 |
+
deadline: "Ende: 5. Februar, 10:00 Uhr",
|
95 |
+
prizes: {
|
96 |
+
title: "Kämpfe um die Top 5 Plätze und gewinne:",
|
97 |
+
list: [
|
98 |
+
"🥇 1. Platz: 50€",
|
99 |
+
"🥈 2. Platz: 20€",
|
100 |
+
"🥉 3. Platz: 10€",
|
101 |
+
"🎖️ 4. & 5. Platz: je 10€"
|
102 |
+
]
|
103 |
+
},
|
104 |
+
fairPlay: "🚨 Faires Spielen wird überwacht. Betrug führt zur Disqualifikation!"
|
105 |
+
},
|
106 |
+
likeOnHuggingface: "Auf Hugging Face liken"
|
107 |
},
|
108 |
+
howToPlay: {
|
109 |
+
setup: {
|
110 |
+
title: "Vorbereitung",
|
111 |
+
description: "Wähle ein Thema und erhalte ein geheimes Wort, das die KI erraten soll."
|
112 |
+
},
|
113 |
+
goal: {
|
114 |
+
title: "Ziel",
|
115 |
+
description: "Baue gemeinsam mit der KI Sätze, die dein Wort beschreiben, ohne es direkt zu verwenden."
|
116 |
+
},
|
117 |
+
rules: {
|
118 |
+
title: "Regeln",
|
119 |
+
items: [
|
120 |
+
"Füge abwechselnd Wörter hinzu, um beschreibende Sätze zu bilden",
|
121 |
+
"Verwende nicht das geheime Wort oder seine Variationen",
|
122 |
+
"Sei kreativ und beschreibend",
|
123 |
+
"Die KI wird nach jedem Satz versuchen, dein Wort zu erraten"
|
124 |
+
]
|
125 |
+
}
|
126 |
}
|
127 |
+
};
|
|
src/i18n/translations/en.ts
CHANGED
@@ -18,7 +18,10 @@ export const en = {
|
|
18 |
leaveGameDescription: "Your current progress will be lost. Are you sure you want to leave?",
|
19 |
cancel: "Cancel",
|
20 |
confirm: "Confirm",
|
21 |
-
describeWord: "Your goal is to describe the word"
|
|
|
|
|
|
|
22 |
},
|
23 |
leaderboard: {
|
24 |
title: "High Scores",
|
@@ -31,10 +34,11 @@ export const en = {
|
|
31 |
rank: "Rank",
|
32 |
player: "Player",
|
33 |
roundsColumn: "Rounds",
|
34 |
-
avgWords: "
|
35 |
noScores: "No scores yet",
|
36 |
previous: "Previous",
|
37 |
next: "Next",
|
|
|
38 |
error: {
|
39 |
invalidName: "Please enter a valid name",
|
40 |
noRounds: "You need to complete at least one round",
|
@@ -59,7 +63,7 @@ export const en = {
|
|
59 |
},
|
60 |
themes: {
|
61 |
title: "Choose a Theme",
|
62 |
-
subtitle: "Select a theme for the word the AI will try to guess",
|
63 |
standard: "Standard",
|
64 |
technology: "Technology",
|
65 |
sports: "Sports",
|
@@ -68,7 +72,8 @@ export const en = {
|
|
68 |
customPlaceholder: "Enter your custom theme...",
|
69 |
continue: "Continue",
|
70 |
generating: "Generating...",
|
71 |
-
pressKey: "Press"
|
|
|
72 |
},
|
73 |
welcome: {
|
74 |
title: "Think in Sync",
|
@@ -77,8 +82,28 @@ export const en = {
|
|
77 |
howToPlay: "How to Play",
|
78 |
leaderboard: "Leaderboard",
|
79 |
credits: "Created during the",
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
},
|
83 |
howToPlay: {
|
84 |
setup: {
|
@@ -99,4 +124,4 @@ export const en = {
|
|
99 |
]
|
100 |
}
|
101 |
}
|
102 |
-
};
|
|
|
18 |
leaveGameDescription: "Your current progress will be lost. Are you sure you want to leave?",
|
19 |
cancel: "Cancel",
|
20 |
confirm: "Confirm",
|
21 |
+
describeWord: "Your goal is to describe the word",
|
22 |
+
nextRound: "Next Round",
|
23 |
+
playAgain: "Play Again",
|
24 |
+
saveScore: "Save Score"
|
25 |
},
|
26 |
leaderboard: {
|
27 |
title: "High Scores",
|
|
|
34 |
rank: "Rank",
|
35 |
player: "Player",
|
36 |
roundsColumn: "Rounds",
|
37 |
+
avgWords: "Avg. Words",
|
38 |
noScores: "No scores yet",
|
39 |
previous: "Previous",
|
40 |
next: "Next",
|
41 |
+
success: "Score submitted successfully!",
|
42 |
error: {
|
43 |
invalidName: "Please enter a valid name",
|
44 |
noRounds: "You need to complete at least one round",
|
|
|
63 |
},
|
64 |
themes: {
|
65 |
title: "Choose a Theme",
|
66 |
+
subtitle: "Select a theme for the word that the AI will try to guess",
|
67 |
standard: "Standard",
|
68 |
technology: "Technology",
|
69 |
sports: "Sports",
|
|
|
72 |
customPlaceholder: "Enter your custom theme...",
|
73 |
continue: "Continue",
|
74 |
generating: "Generating...",
|
75 |
+
pressKey: "Press",
|
76 |
+
playing: "Theme"
|
77 |
},
|
78 |
welcome: {
|
79 |
title: "Think in Sync",
|
|
|
82 |
howToPlay: "How to Play",
|
83 |
leaderboard: "Leaderboard",
|
84 |
credits: "Created during the",
|
85 |
+
contest: {
|
86 |
+
prize: "Play to win up to 50€!",
|
87 |
+
terms: "See Terms",
|
88 |
+
howTo: "How to participate:",
|
89 |
+
conditions: [
|
90 |
+
"Play Think in Sync using the Standard wordlist",
|
91 |
+
"Set your leaderboard name to match your Hugging Face username",
|
92 |
+
"Like our project on Hugging Face"
|
93 |
+
],
|
94 |
+
deadline: "Ends: February 5, 10:00 AM",
|
95 |
+
prizes: {
|
96 |
+
title: "Compete for the top 5 spots and win:",
|
97 |
+
list: [
|
98 |
+
"🥇 1st: 50€",
|
99 |
+
"🥈 2nd: 20€",
|
100 |
+
"🥉 3rd: 10€",
|
101 |
+
"🎖️ 4th & 5th: 10€ each"
|
102 |
+
]
|
103 |
+
},
|
104 |
+
fairPlay: "🚨 Fair play is monitored. Any cheating will result in disqualification!"
|
105 |
+
},
|
106 |
+
likeOnHuggingface: "Like on Hugging Face"
|
107 |
},
|
108 |
howToPlay: {
|
109 |
setup: {
|
|
|
124 |
]
|
125 |
}
|
126 |
}
|
127 |
+
};
|
src/i18n/translations/es.ts
CHANGED
@@ -18,7 +18,10 @@ export const es = {
|
|
18 |
leaveGameDescription: "Tu progreso actual se perderá. ¿Estás seguro de que quieres salir?",
|
19 |
cancel: "Cancelar",
|
20 |
confirm: "Confirmar",
|
21 |
-
describeWord: "Tu objetivo es describir la palabra"
|
|
|
|
|
|
|
22 |
},
|
23 |
leaderboard: {
|
24 |
title: "Puntuaciones Más Altas",
|
@@ -31,10 +34,11 @@ export const es = {
|
|
31 |
rank: "Posición",
|
32 |
player: "Jugador",
|
33 |
roundsColumn: "Rondas",
|
34 |
-
avgWords: "
|
35 |
noScores: "Aún no hay puntuaciones",
|
36 |
previous: "Anterior",
|
37 |
next: "Siguiente",
|
|
|
38 |
error: {
|
39 |
invalidName: "Por favor, ingresa un nombre válido",
|
40 |
noRounds: "Debes completar al menos una ronda",
|
@@ -68,17 +72,38 @@ export const es = {
|
|
68 |
customPlaceholder: "Ingresa tu tema personalizado...",
|
69 |
continue: "Continuar",
|
70 |
generating: "Generando...",
|
71 |
-
pressKey: "Presiona"
|
|
|
72 |
},
|
73 |
welcome: {
|
74 |
title: "Think in Sync",
|
75 |
-
subtitle: "¡
|
76 |
-
startButton: "Comenzar
|
77 |
-
howToPlay: "Cómo
|
78 |
-
leaderboard: "
|
79 |
credits: "Creado durante el",
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
},
|
83 |
howToPlay: {
|
84 |
setup: {
|
@@ -99,4 +124,4 @@ export const es = {
|
|
99 |
]
|
100 |
}
|
101 |
}
|
102 |
-
};
|
|
|
18 |
leaveGameDescription: "Tu progreso actual se perderá. ¿Estás seguro de que quieres salir?",
|
19 |
cancel: "Cancelar",
|
20 |
confirm: "Confirmar",
|
21 |
+
describeWord: "Tu objetivo es describir la palabra",
|
22 |
+
nextRound: "Siguiente Ronda",
|
23 |
+
playAgain: "Jugar de Nuevo",
|
24 |
+
saveScore: "Guardar Puntuación"
|
25 |
},
|
26 |
leaderboard: {
|
27 |
title: "Puntuaciones Más Altas",
|
|
|
34 |
rank: "Posición",
|
35 |
player: "Jugador",
|
36 |
roundsColumn: "Rondas",
|
37 |
+
avgWords: "Prom. Palabras",
|
38 |
noScores: "Aún no hay puntuaciones",
|
39 |
previous: "Anterior",
|
40 |
next: "Siguiente",
|
41 |
+
success: "¡Puntuación enviada con éxito!",
|
42 |
error: {
|
43 |
invalidName: "Por favor, ingresa un nombre válido",
|
44 |
noRounds: "Debes completar al menos una ronda",
|
|
|
72 |
customPlaceholder: "Ingresa tu tema personalizado...",
|
73 |
continue: "Continuar",
|
74 |
generating: "Generando...",
|
75 |
+
pressKey: "Presiona",
|
76 |
+
playing: "Tema"
|
77 |
},
|
78 |
welcome: {
|
79 |
title: "Think in Sync",
|
80 |
+
subtitle: "¡Forma equipo con la IA para crear una pista y deja que otra IA adivine tu palabra secreta!",
|
81 |
+
startButton: "Comenzar juego",
|
82 |
+
howToPlay: "Cómo jugar",
|
83 |
+
leaderboard: "Tabla de clasificación",
|
84 |
credits: "Creado durante el",
|
85 |
+
contest: {
|
86 |
+
prize: "¡Juega para ganar hasta 50€!",
|
87 |
+
terms: "Ver términos",
|
88 |
+
howTo: "Cómo participar:",
|
89 |
+
conditions: [
|
90 |
+
"Juega Think in Sync usando la lista de palabras estándar",
|
91 |
+
"Establece tu nombre en la tabla de clasificación igual a tu nombre de usuario de Hugging Face",
|
92 |
+
"Dale me gusta a nuestro proyecto en Hugging Face"
|
93 |
+
],
|
94 |
+
deadline: "Finaliza: 5 de febrero, 10:00 AM",
|
95 |
+
prizes: {
|
96 |
+
title: "Compite por los 5 primeros puestos y gana:",
|
97 |
+
list: [
|
98 |
+
"🥇 1º: 50€",
|
99 |
+
"🥈 2º: 20€",
|
100 |
+
"🥉 3º: 10€",
|
101 |
+
"🎖️ 4º y 5º: 10€ cada uno"
|
102 |
+
]
|
103 |
+
},
|
104 |
+
fairPlay: "🚨 El juego limpio está monitoreado. ¡Cualquier trampa resultará en descalificación!"
|
105 |
+
},
|
106 |
+
likeOnHuggingface: "Me gusta en Hugging Face"
|
107 |
},
|
108 |
howToPlay: {
|
109 |
setup: {
|
|
|
124 |
]
|
125 |
}
|
126 |
}
|
127 |
+
};
|
src/i18n/translations/fr.ts
CHANGED
@@ -17,85 +17,110 @@ export const fr = {
|
|
17 |
leaveGameDescription: "Votre progression actuelle sera perdue. Êtes-vous sûr de vouloir quitter ?",
|
18 |
cancel: "Annuler",
|
19 |
confirm: "Confirmer",
|
20 |
-
describeWord: "Votre objectif est de décrire le mot"
|
|
|
|
|
|
|
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 |
-
themes: {
|
60 |
-
title: "Choisissez un Thème",
|
61 |
-
subtitle: "Sélectionnez un thème pour le mot que l'IA essaiera de deviner",
|
62 |
-
standard: "Standard",
|
63 |
-
technology: "Technologie",
|
64 |
-
sports: "Sports",
|
65 |
-
food: "Nourriture",
|
66 |
-
custom: "Thème Personnalisé",
|
67 |
-
customPlaceholder: "Entrez votre thème personnalisé...",
|
68 |
-
continue: "Continuer",
|
69 |
-
generating: "Génération...",
|
70 |
-
pressKey: "Appuyez sur"
|
71 |
-
},
|
72 |
-
welcome: {
|
73 |
-
title: "Think in Sync",
|
74 |
-
subtitle: "Faites équipe avec une IA pour créer un indice et laissez une autre IA deviner votre mot secret",
|
75 |
-
startButton: "Commencer",
|
76 |
-
howToPlay: "Comment Jouer",
|
77 |
-
leaderboard: "Classement",
|
78 |
-
credits: "Créé pendant le",
|
79 |
-
helpWin: "Aidez-nous à gagner en",
|
80 |
-
onHuggingface: "Nous aimant sur Huggingface"
|
81 |
-
},
|
82 |
-
howToPlay: {
|
83 |
-
setup: {
|
84 |
-
title: "Mise en place",
|
85 |
-
description: "Choisissez un thème et obtenez un mot secret que l'IA essaiera de deviner."
|
86 |
},
|
87 |
-
|
88 |
-
title: "
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
},
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
"
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
}
|
100 |
-
|
101 |
-
};
|
|
|
17 |
leaveGameDescription: "Votre progression actuelle sera perdue. Êtes-vous sûr de vouloir quitter ?",
|
18 |
cancel: "Annuler",
|
19 |
confirm: "Confirmer",
|
20 |
+
describeWord: "Votre objectif est de décrire le mot",
|
21 |
+
nextRound: "Tour Suivant",
|
22 |
+
playAgain: "Rejouer",
|
23 |
+
saveScore: "Sauvegarder le Score"
|
24 |
},
|
25 |
+
leaderboard: {
|
26 |
+
title: "Meilleurs Scores",
|
27 |
+
yourScore: "Votre Score",
|
28 |
+
roundCount: "tours",
|
29 |
+
wordsPerRound: "mots par tour",
|
30 |
+
enterName: "Entrez votre nom",
|
31 |
+
submitting: "Envoi en cours...",
|
32 |
+
submit: "Soumettre le Score",
|
33 |
+
rank: "Rang",
|
34 |
+
player: "Joueur",
|
35 |
+
roundsColumn: "Tours",
|
36 |
+
avgWords: "Moy. Mots",
|
37 |
+
noScores: "Pas encore de scores",
|
38 |
+
previous: "Précédent",
|
39 |
+
next: "Suivant",
|
40 |
+
success: "Score soumis avec succès !",
|
41 |
+
error: {
|
42 |
+
invalidName: "Veuillez entrer un nom valide",
|
43 |
+
noRounds: "Vous devez compléter au moins un tour",
|
44 |
+
alreadySubmitted: "Score déjà soumis",
|
45 |
+
newHighScore: "Nouveau Record !",
|
46 |
+
beatRecord: "Vous avez battu votre record précédent de {score} !",
|
47 |
+
notHigher: "Score de {current} pas plus élevé que votre meilleur de {best}",
|
48 |
+
submitError: "Erreur lors de la soumission du score"
|
49 |
+
}
|
50 |
+
},
|
51 |
+
guess: {
|
52 |
+
title: "Devinette de l'IA",
|
53 |
+
goalDescription: "Votre objectif était de décrire le mot",
|
54 |
+
providedDescription: "Vous avez fourni la description",
|
55 |
+
aiGuessedDescription: "Basé sur votre description, l'IA a deviné",
|
56 |
+
correct: "C'est correct !",
|
57 |
+
incorrect: "C'est incorrect.",
|
58 |
+
nextRound: "Tour Suivant",
|
59 |
+
playAgain: "Rejouer",
|
60 |
+
viewLeaderboard: "Voir les Scores",
|
61 |
+
cheatingDetected: "Tentative de triche détectée !"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
},
|
63 |
+
themes: {
|
64 |
+
title: "Choisissez un Thème",
|
65 |
+
subtitle: "Sélectionnez un thème pour le mot que l'IA essaiera de deviner",
|
66 |
+
standard: "Standard",
|
67 |
+
technology: "Technologie",
|
68 |
+
sports: "Sports",
|
69 |
+
food: "Nourriture",
|
70 |
+
custom: "Thème Personnalisé",
|
71 |
+
customPlaceholder: "Entrez votre thème personnalisé...",
|
72 |
+
continue: "Continuer",
|
73 |
+
generating: "Génération...",
|
74 |
+
pressKey: "Appuyez sur",
|
75 |
+
playing: "Thème"
|
76 |
+
},
|
77 |
+
welcome: {
|
78 |
+
title: "Think in Sync",
|
79 |
+
subtitle: "Faites équipe avec une IA pour créer un indice et laissez une autre IA deviner votre mot secret !",
|
80 |
+
startButton: "Commencer",
|
81 |
+
howToPlay: "Comment Jouer",
|
82 |
+
leaderboard: "Classement",
|
83 |
+
credits: "Créé pendant le",
|
84 |
+
contest: {
|
85 |
+
prize: "Jouez pour gagner jusqu'à 50€ !",
|
86 |
+
terms: "Voir conditions",
|
87 |
+
howTo: "Comment participer :",
|
88 |
+
conditions: [
|
89 |
+
"Jouez à Think in Sync avec la liste de mots standard",
|
90 |
+
"Utilisez votre nom d'utilisateur Hugging Face dans le classement",
|
91 |
+
"Aimez notre projet sur Hugging Face"
|
92 |
+
],
|
93 |
+
deadline: "Fin : 5 février, 10h00",
|
94 |
+
prizes: {
|
95 |
+
title: "Participez pour les 5 premières places et gagnez :",
|
96 |
+
list: [
|
97 |
+
"🥇 1er : 50€",
|
98 |
+
"🥈 2ème : 20€",
|
99 |
+
"🥉 3ème : 10€",
|
100 |
+
"🎖️ 4ème & 5ème : 10€ chacun"
|
101 |
+
]
|
102 |
+
},
|
103 |
+
fairPlay: "🚨 Le fair-play est surveillé. Toute triche entraînera une disqualification !"
|
104 |
+
},
|
105 |
+
likeOnHuggingface: "Aimer sur Hugging Face"
|
106 |
},
|
107 |
+
howToPlay: {
|
108 |
+
setup: {
|
109 |
+
title: "Mise en place",
|
110 |
+
description: "Choisissez un thème et obtenez un mot secret que l'IA essaiera de deviner."
|
111 |
+
},
|
112 |
+
goal: {
|
113 |
+
title: "Objectif",
|
114 |
+
description: "Construisez des phrases avec l'IA qui décrivent votre mot sans l'utiliser directement."
|
115 |
+
},
|
116 |
+
rules: {
|
117 |
+
title: "Règles",
|
118 |
+
items: [
|
119 |
+
"Ajoutez des mots à tour de rôle pour construire des phrases descriptives",
|
120 |
+
"N'utilisez pas le mot secret ou ses variations",
|
121 |
+
"Soyez créatif et descriptif",
|
122 |
+
"L'IA essaiera de deviner votre mot après chaque phrase"
|
123 |
+
]
|
124 |
+
}
|
125 |
}
|
126 |
+
};
|
|
src/i18n/translations/it.ts
CHANGED
@@ -18,87 +18,112 @@ export const it = {
|
|
18 |
leaveGameDescription: "I tuoi progressi attuali andranno persi. Sei sicuro di voler uscire?",
|
19 |
cancel: "Annulla",
|
20 |
confirm: "Conferma",
|
21 |
-
describeWord: "Il tuo obiettivo è descrivere la parola"
|
|
|
|
|
|
|
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 |
-
themes: {
|
63 |
-
title: "Scegli un Tema",
|
64 |
-
subtitle: "Seleziona un tema per la parola che l'IA cercherà di indovinare",
|
65 |
-
standard: "Standard",
|
66 |
-
technology: "Tecnologia",
|
67 |
-
sports: "Sport",
|
68 |
-
food: "Cibo",
|
69 |
-
custom: "Tema Personalizzato",
|
70 |
-
customPlaceholder: "Inserisci il tuo tema personalizzato...",
|
71 |
-
continue: "Continua",
|
72 |
-
generating: "Generazione...",
|
73 |
-
pressKey: "Premi"
|
74 |
-
},
|
75 |
-
welcome: {
|
76 |
-
title: "Think in Sync",
|
77 |
-
subtitle: "Collabora con un'IA per creare un indizio e lascia che un'altra IA indovini la tua parola segreta!",
|
78 |
-
startButton: "Inizia Gioco",
|
79 |
-
howToPlay: "Come Giocare",
|
80 |
-
leaderboard: "Classifica",
|
81 |
-
credits: "Creato durante il",
|
82 |
-
helpWin: "Aiutaci a vincere",
|
83 |
-
onHuggingface: "Mettendo mi piace su Huggingface"
|
84 |
-
},
|
85 |
-
howToPlay: {
|
86 |
-
setup: {
|
87 |
-
title: "Preparazione",
|
88 |
-
description: "Scegli un tema e ottieni una parola segreta che l'IA cercherà di indovinare."
|
89 |
},
|
90 |
-
|
91 |
-
title: "
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
},
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
"
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
-
|
104 |
-
};
|
|
|
18 |
leaveGameDescription: "I tuoi progressi attuali andranno persi. Sei sicuro di voler uscire?",
|
19 |
cancel: "Annulla",
|
20 |
confirm: "Conferma",
|
21 |
+
describeWord: "Il tuo obiettivo è descrivere la parola",
|
22 |
+
nextRound: "Prossimo Turno",
|
23 |
+
playAgain: "Gioca Ancora",
|
24 |
+
saveScore: "Salva Punteggio"
|
25 |
},
|
26 |
+
leaderboard: {
|
27 |
+
title: "Punteggi Migliori",
|
28 |
+
yourScore: "Il Tuo Punteggio",
|
29 |
+
roundCount: "turni",
|
30 |
+
wordsPerRound: "parole per turno",
|
31 |
+
enterName: "Inserisci il tuo nome",
|
32 |
+
submitting: "Invio in corso...",
|
33 |
+
submit: "Invia Punteggio",
|
34 |
+
rank: "Posizione",
|
35 |
+
player: "Giocatore",
|
36 |
+
roundsColumn: "Turni",
|
37 |
+
avgWords: "Media Parole",
|
38 |
+
noScores: "Ancora nessun punteggio",
|
39 |
+
previous: "Precedente",
|
40 |
+
next: "Successivo",
|
41 |
+
success: "Punteggio inviato con successo!",
|
42 |
+
error: {
|
43 |
+
invalidName: "Inserisci un nome valido",
|
44 |
+
noRounds: "Devi completare almeno un turno",
|
45 |
+
alreadySubmitted: "Punteggio già inviato",
|
46 |
+
newHighScore: "Nuovo Record!",
|
47 |
+
beatRecord: "Hai battuto il tuo record precedente di {score}!",
|
48 |
+
notHigher: "Punteggio di {current} non superiore al tuo migliore di {best}",
|
49 |
+
submitError: "Errore nell'invio del punteggio"
|
50 |
+
}
|
51 |
+
},
|
52 |
+
guess: {
|
53 |
+
title: "Ipotesi dell'IA",
|
54 |
+
sentence: "La tua frase",
|
55 |
+
aiGuessed: "L'IA ha indovinato",
|
56 |
+
goalDescription: "Il tuo obiettivo era descrivere la parola",
|
57 |
+
providedDescription: "Hai fornito la descrizione",
|
58 |
+
aiGuessedDescription: "Basandosi sulla tua descrizione, l'IA ha indovinato",
|
59 |
+
correct: "Corretto! L'IA ha indovinato la parola!",
|
60 |
+
incorrect: "Sbagliato. Riprova!",
|
61 |
+
nextRound: "Prossimo Turno",
|
62 |
+
playAgain: "Gioca Ancora",
|
63 |
+
viewLeaderboard: "Vedi Classifica",
|
64 |
+
cheatingDetected: "Tentativo di imbroglio rilevato!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
},
|
66 |
+
themes: {
|
67 |
+
title: "Scegli un Tema",
|
68 |
+
subtitle: "Seleziona un tema per la parola che l'IA cercherà di indovinare",
|
69 |
+
standard: "Standard",
|
70 |
+
technology: "Tecnologia",
|
71 |
+
sports: "Sport",
|
72 |
+
food: "Cibo",
|
73 |
+
custom: "Tema Personalizzato",
|
74 |
+
customPlaceholder: "Inserisci il tuo tema personalizzato...",
|
75 |
+
continue: "Continua",
|
76 |
+
generating: "Generazione...",
|
77 |
+
pressKey: "Premi",
|
78 |
+
playing: "Tema"
|
79 |
+
},
|
80 |
+
welcome: {
|
81 |
+
title: "Think in Sync",
|
82 |
+
subtitle: "Fai squadra con l'IA per creare un indizio e lascia che un'altra IA indovini la tua parola segreta!",
|
83 |
+
startButton: "Inizia gioco",
|
84 |
+
howToPlay: "Come giocare",
|
85 |
+
leaderboard: "Classifica",
|
86 |
+
credits: "Creato durante il",
|
87 |
+
contest: {
|
88 |
+
prize: "Gioca per vincere fino a 50€!",
|
89 |
+
terms: "Vedi termini",
|
90 |
+
howTo: "Come partecipare:",
|
91 |
+
conditions: [
|
92 |
+
"Gioca a Think in Sync usando la lista di parole standard",
|
93 |
+
"Imposta il tuo nome in classifica uguale al tuo nome utente Hugging Face",
|
94 |
+
"Metti mi piace al nostro progetto su Hugging Face"
|
95 |
+
],
|
96 |
+
deadline: "Termina: 5 febbraio, 10:00",
|
97 |
+
prizes: {
|
98 |
+
title: "Competi per i primi 5 posti e vinci:",
|
99 |
+
list: [
|
100 |
+
"🥇 1°: 50€",
|
101 |
+
"🥈 2°: 20€",
|
102 |
+
"🥉 3°: 10€",
|
103 |
+
"🎖️ 4° e 5°: 10€ ciascuno"
|
104 |
+
]
|
105 |
+
},
|
106 |
+
fairPlay: "🚨 Il fair play è monitorato. Qualsiasi imbroglio porterà alla squalifica!"
|
107 |
+
},
|
108 |
+
likeOnHuggingface: "Mi piace su Hugging Face"
|
109 |
},
|
110 |
+
howToPlay: {
|
111 |
+
setup: {
|
112 |
+
title: "Preparazione",
|
113 |
+
description: "Scegli un tema e ottieni una parola segreta che l'IA cercherà di indovinare."
|
114 |
+
},
|
115 |
+
goal: {
|
116 |
+
title: "Obiettivo",
|
117 |
+
description: "Costruisci frasi insieme all'IA che descrivono la tua parola senza usarla direttamente."
|
118 |
+
},
|
119 |
+
rules: {
|
120 |
+
title: "Regole",
|
121 |
+
items: [
|
122 |
+
"Aggiungi parole a turno per costruire frasi descrittive",
|
123 |
+
"Non usare la parola segreta o le sue variazioni",
|
124 |
+
"Sii creativo e descrittivo",
|
125 |
+
"L'IA cercherà di indovinare la tua parola dopo ogni frase"
|
126 |
+
]
|
127 |
+
}
|
128 |
}
|
129 |
+
};
|
|
src/integrations/supabase/types.ts
CHANGED
@@ -47,6 +47,7 @@ export type Database = {
|
|
47 |
player_name: string
|
48 |
score: number
|
49 |
session_id: string
|
|
|
50 |
}
|
51 |
Insert: {
|
52 |
avg_words_per_round: number
|
@@ -55,6 +56,7 @@ export type Database = {
|
|
55 |
player_name: string
|
56 |
score: number
|
57 |
session_id: string
|
|
|
58 |
}
|
59 |
Update: {
|
60 |
avg_words_per_round?: number
|
@@ -63,6 +65,7 @@ export type Database = {
|
|
63 |
player_name?: string
|
64 |
score?: number
|
65 |
session_id?: string
|
|
|
66 |
}
|
67 |
Relationships: []
|
68 |
}
|
@@ -71,7 +74,16 @@ export type Database = {
|
|
71 |
[_ in never]: never
|
72 |
}
|
73 |
Functions: {
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
}
|
76 |
Enums: {
|
77 |
[_ in never]: never
|
|
|
47 |
player_name: string
|
48 |
score: number
|
49 |
session_id: string
|
50 |
+
theme: string
|
51 |
}
|
52 |
Insert: {
|
53 |
avg_words_per_round: number
|
|
|
56 |
player_name: string
|
57 |
score: number
|
58 |
session_id: string
|
59 |
+
theme?: string
|
60 |
}
|
61 |
Update: {
|
62 |
avg_words_per_round?: number
|
|
|
65 |
player_name?: string
|
66 |
score?: number
|
67 |
session_id?: string
|
68 |
+
theme?: string
|
69 |
}
|
70 |
Relationships: []
|
71 |
}
|
|
|
74 |
[_ in never]: never
|
75 |
}
|
76 |
Functions: {
|
77 |
+
check_and_update_high_score: {
|
78 |
+
Args: {
|
79 |
+
p_player_name: string
|
80 |
+
p_score: number
|
81 |
+
p_avg_words_per_round: number
|
82 |
+
p_session_id: string
|
83 |
+
p_theme?: string
|
84 |
+
}
|
85 |
+
Returns: boolean
|
86 |
+
}
|
87 |
}
|
88 |
Enums: {
|
89 |
[_ in never]: never
|
src/lib/words-food.ts
ADDED
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const englishFoodWords = [
|
2 |
+
"PIZZA",
|
3 |
+
"PASTA",
|
4 |
+
"BREAD",
|
5 |
+
"CHEESE",
|
6 |
+
"APPLE",
|
7 |
+
"BANANA",
|
8 |
+
"ORANGE",
|
9 |
+
"CARROT",
|
10 |
+
"POTATO",
|
11 |
+
"TOMATO",
|
12 |
+
"CHICKEN",
|
13 |
+
"BEEF",
|
14 |
+
"FISH",
|
15 |
+
"RICE",
|
16 |
+
"SOUP",
|
17 |
+
"SALAD",
|
18 |
+
"CAKE",
|
19 |
+
"COOKIE",
|
20 |
+
"CHOCOLATE",
|
21 |
+
"HONEY",
|
22 |
+
"GRAPES",
|
23 |
+
"LEMON",
|
24 |
+
"PEPPER",
|
25 |
+
"ONION",
|
26 |
+
"GARLIC",
|
27 |
+
"CABBAGE",
|
28 |
+
"BROCCOLI",
|
29 |
+
"SPINACH",
|
30 |
+
"MUSHROOM",
|
31 |
+
"PUMPKIN",
|
32 |
+
"ZUCCHINI",
|
33 |
+
"BELL PEPPER",
|
34 |
+
"CORN",
|
35 |
+
"AVOCADO",
|
36 |
+
"YOGURT",
|
37 |
+
"NUTS",
|
38 |
+
"CEREAL",
|
39 |
+
"PUDDING",
|
40 |
+
"JAM"
|
41 |
+
];
|
42 |
+
|
43 |
+
export const germanFoodWords = [
|
44 |
+
"PIZZA",
|
45 |
+
"NUDELN",
|
46 |
+
"BROT",
|
47 |
+
"KÄSE",
|
48 |
+
"APFEL",
|
49 |
+
"BANANE",
|
50 |
+
"ORANGE",
|
51 |
+
"KAROTTE",
|
52 |
+
"KARTOFFEL",
|
53 |
+
"TOMATE",
|
54 |
+
"HUHN",
|
55 |
+
"RINDFLEISCH",
|
56 |
+
"FISCH",
|
57 |
+
"REIS",
|
58 |
+
"SUPPE",
|
59 |
+
"SALAT",
|
60 |
+
"KUCHEN",
|
61 |
+
"KEKS",
|
62 |
+
"SCHOKOLADE",
|
63 |
+
"HONIG",
|
64 |
+
"TRAUBE",
|
65 |
+
"ZITRONE",
|
66 |
+
"PFEFFER",
|
67 |
+
"ZWIEBEL",
|
68 |
+
"KNOBLAUCH",
|
69 |
+
"KOHLSALAT",
|
70 |
+
"BROKKOLI",
|
71 |
+
"SPINAT",
|
72 |
+
"PILZ",
|
73 |
+
"KÜRBIS",
|
74 |
+
"ZUCCHINI",
|
75 |
+
"BELL-PAPRIKA",
|
76 |
+
"MAIS",
|
77 |
+
"AVOCADO",
|
78 |
+
"JOGHURT",
|
79 |
+
"NÜSSE",
|
80 |
+
"MÜSLI",
|
81 |
+
"PUDDING",
|
82 |
+
"MARMELADE"
|
83 |
+
];
|
84 |
+
|
85 |
+
export const frenchFoodWords = [
|
86 |
+
"PIZZA",
|
87 |
+
"PÂTES",
|
88 |
+
"PAIN",
|
89 |
+
"FROMAGE",
|
90 |
+
"POMME",
|
91 |
+
"BANANE",
|
92 |
+
"ORANGE",
|
93 |
+
"CAROTTE",
|
94 |
+
"POMMETERRE",
|
95 |
+
"TOMATE",
|
96 |
+
"POULET",
|
97 |
+
"BOEUF",
|
98 |
+
"POISSON",
|
99 |
+
"RIZ",
|
100 |
+
"SOUPE",
|
101 |
+
"SALADE",
|
102 |
+
"GÂTEAU",
|
103 |
+
"BISCUIT",
|
104 |
+
"CHOCOLAT",
|
105 |
+
"MIEL",
|
106 |
+
"RAISIN",
|
107 |
+
"CITRON",
|
108 |
+
"POIVRON",
|
109 |
+
"OIGNON",
|
110 |
+
"AIL",
|
111 |
+
"CHOU",
|
112 |
+
"BROCOLI",
|
113 |
+
"ÉPINARD",
|
114 |
+
"CHAMPIGNON",
|
115 |
+
"COURGE",
|
116 |
+
"COURGETTE",
|
117 |
+
"POIVRON DOUX",
|
118 |
+
"MAÏS",
|
119 |
+
"AVOCAT",
|
120 |
+
"YAOURT",
|
121 |
+
"NOIX",
|
122 |
+
"CÉRÉALES",
|
123 |
+
"CRÈME",
|
124 |
+
"CONFITURE"
|
125 |
+
];
|
126 |
+
|
127 |
+
export const italianFoodWords = [
|
128 |
+
"PIZZA",
|
129 |
+
"PASTA",
|
130 |
+
"PANE",
|
131 |
+
"FORMAGGIO",
|
132 |
+
"MELA",
|
133 |
+
"BANANA",
|
134 |
+
"ARANCIA",
|
135 |
+
"CAROTA",
|
136 |
+
"PATATA",
|
137 |
+
"POMODORO",
|
138 |
+
"POLLO",
|
139 |
+
"MANZO",
|
140 |
+
"PESCE",
|
141 |
+
"RISO",
|
142 |
+
"ZUPPA",
|
143 |
+
"INSALATA",
|
144 |
+
"TORTA",
|
145 |
+
"BISCOTTO",
|
146 |
+
"CIOCCOLATO",
|
147 |
+
"MIELE",
|
148 |
+
"UVA",
|
149 |
+
"LIMONE",
|
150 |
+
"PEPERONE",
|
151 |
+
"CIPOLLA",
|
152 |
+
"AGLIO",
|
153 |
+
"CAVOLO",
|
154 |
+
"BROCCOLI",
|
155 |
+
"SPINACI",
|
156 |
+
"FUNGHI",
|
157 |
+
"ZUCCA",
|
158 |
+
"ZUCCHINI",
|
159 |
+
"PEPERONE DOLCE",
|
160 |
+
"MAIS",
|
161 |
+
"AVOCADO",
|
162 |
+
"YOGURT",
|
163 |
+
"NOCI",
|
164 |
+
"CEREALI",
|
165 |
+
"CREMA",
|
166 |
+
"MARMELLATA"
|
167 |
+
];
|
168 |
+
|
169 |
+
export const spanishFoodWords = [
|
170 |
+
"PIZZA",
|
171 |
+
"PASTA",
|
172 |
+
"PAN",
|
173 |
+
"QUESO",
|
174 |
+
"MANZANA",
|
175 |
+
"PLÁTANO",
|
176 |
+
"NARANJA",
|
177 |
+
"ZANAHORIA",
|
178 |
+
"PATATA",
|
179 |
+
"TOMATE",
|
180 |
+
"POLLO",
|
181 |
+
"TERNERA",
|
182 |
+
"PESCADO",
|
183 |
+
"ARROZ",
|
184 |
+
"SOPA",
|
185 |
+
"ENSALADA",
|
186 |
+
"PASTEL",
|
187 |
+
"GALLETA",
|
188 |
+
"CHOCOLATE",
|
189 |
+
"MIEL",
|
190 |
+
"UVAS",
|
191 |
+
"LIMÓN",
|
192 |
+
"PIMIENTA",
|
193 |
+
"CEBOLLA",
|
194 |
+
"AJO",
|
195 |
+
"COL",
|
196 |
+
"BRÓCOLI",
|
197 |
+
"ESPINACA",
|
198 |
+
"CHAMPIÑÓN",
|
199 |
+
"CALABAZA",
|
200 |
+
"CALABACÍN",
|
201 |
+
"PIMIENTO",
|
202 |
+
"MAÍZ",
|
203 |
+
"AGUACATE",
|
204 |
+
"YOGUR",
|
205 |
+
"NUECES",
|
206 |
+
"CEREALES",
|
207 |
+
"CREMA",
|
208 |
+
"MERMELADA"
|
209 |
+
];
|
210 |
+
|
211 |
+
export const getRandomFoodWord = (language: string = 'en') => {
|
212 |
+
let wordList;
|
213 |
+
switch (language) {
|
214 |
+
case 'de':
|
215 |
+
wordList = germanFoodWords;
|
216 |
+
break;
|
217 |
+
case 'fr':
|
218 |
+
wordList = frenchFoodWords;
|
219 |
+
break;
|
220 |
+
case 'it':
|
221 |
+
wordList = italianFoodWords;
|
222 |
+
break;
|
223 |
+
case 'es':
|
224 |
+
wordList = spanishFoodWords;
|
225 |
+
break;
|
226 |
+
default:
|
227 |
+
wordList = englishFoodWords;
|
228 |
+
}
|
229 |
+
return wordList[Math.floor(Math.random() * wordList.length)];
|
230 |
+
};
|
src/lib/words-sports.ts
ADDED
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const englishSportsWords = [
|
2 |
+
"SOCCER",
|
3 |
+
"TENNIS",
|
4 |
+
"GOLF",
|
5 |
+
"BASEBALL",
|
6 |
+
"BASKETBALL",
|
7 |
+
"VOLLEYBALL",
|
8 |
+
"HOCKEY",
|
9 |
+
"RUGBY",
|
10 |
+
"CRICKET",
|
11 |
+
"SWIMMING",
|
12 |
+
"RUNNING",
|
13 |
+
"CYCLING",
|
14 |
+
"SKIING",
|
15 |
+
"BOXING",
|
16 |
+
"SKATING",
|
17 |
+
"SURFING",
|
18 |
+
"CLIMBING",
|
19 |
+
"DIVING",
|
20 |
+
"SAILING",
|
21 |
+
"WRESTLING",
|
22 |
+
"LACROSSE",
|
23 |
+
"BADMINTON",
|
24 |
+
"FENCING",
|
25 |
+
"ARCHERY",
|
26 |
+
"MMA",
|
27 |
+
"KARATE",
|
28 |
+
"JUDO",
|
29 |
+
"PADDLEBOARDING",
|
30 |
+
"SNOWBOARDING",
|
31 |
+
"BILLIARDS",
|
32 |
+
"DARTS",
|
33 |
+
"GYMNASTICS",
|
34 |
+
"PILATES",
|
35 |
+
"YOGA",
|
36 |
+
"CROSSFIT",
|
37 |
+
"TRIATHLON",
|
38 |
+
"MARATHON",
|
39 |
+
"ULTRAMARATHON",
|
40 |
+
"POLO",
|
41 |
+
"SQUASH",
|
42 |
+
"BOWLING",
|
43 |
+
"CURLING",
|
44 |
+
"SKATEBOARDING",
|
45 |
+
"FLOORBALL",
|
46 |
+
"KITESURFING",
|
47 |
+
"BMX",
|
48 |
+
"TRAMPOLINE",
|
49 |
+
"SOFTBALL",
|
50 |
+
"RINGETTE",
|
51 |
+
"BANDY",
|
52 |
+
"SNOWSHOEING",
|
53 |
+
"PARAGLIDING",
|
54 |
+
"CANYONING",
|
55 |
+
"CAVING"
|
56 |
+
];
|
57 |
+
|
58 |
+
export const germanSportsWords = [
|
59 |
+
"FUSSBALL",
|
60 |
+
"TENNIS",
|
61 |
+
"GOLF",
|
62 |
+
"BASEBALL",
|
63 |
+
"BASKETBALL",
|
64 |
+
"VOLLEYBALL",
|
65 |
+
"HOCKEY",
|
66 |
+
"RUGBY",
|
67 |
+
"CRICKET",
|
68 |
+
"SCHWIMMEN",
|
69 |
+
"LAUFEN",
|
70 |
+
"RADFAHREN",
|
71 |
+
"SKIFAHREN",
|
72 |
+
"BOXEN",
|
73 |
+
"EISLAUFEN",
|
74 |
+
"SURFEN",
|
75 |
+
"KLETTERN",
|
76 |
+
"TAUCHEN",
|
77 |
+
"SEGELN",
|
78 |
+
"RINGEN",
|
79 |
+
"TURNEN",
|
80 |
+
"BOGENSCHIESSEN",
|
81 |
+
"FECHTEN",
|
82 |
+
"MOTORSPORT",
|
83 |
+
"SCHIESSEN",
|
84 |
+
"REITEN",
|
85 |
+
"HANDBALL",
|
86 |
+
"SNOWBOARDEN",
|
87 |
+
"WASSERBALL",
|
88 |
+
"TRIATHLON",
|
89 |
+
"GEWICHTHEBEN",
|
90 |
+
"JUDO",
|
91 |
+
"TAEKWONDO",
|
92 |
+
"SKATEBOARDEN",
|
93 |
+
"WINDSURFEN",
|
94 |
+
"BADMINTON",
|
95 |
+
"TISCHTENNIS",
|
96 |
+
"SYNCHRONSCHWIMMEN",
|
97 |
+
"RUDERN",
|
98 |
+
"LACROSSE"
|
99 |
+
];
|
100 |
+
|
101 |
+
export const frenchSportsWords = [
|
102 |
+
"FOOTBALL",
|
103 |
+
"TENNIS",
|
104 |
+
"GOLF",
|
105 |
+
"BASEBALL",
|
106 |
+
"BASKETBALL",
|
107 |
+
"VOLLEYBALL",
|
108 |
+
"HOCKEY",
|
109 |
+
"RUGBY",
|
110 |
+
"CRICKET",
|
111 |
+
"NATATION",
|
112 |
+
"COURSE",
|
113 |
+
"CYCLISME",
|
114 |
+
"SKI",
|
115 |
+
"BOXE",
|
116 |
+
"PATINAGE",
|
117 |
+
"SURF",
|
118 |
+
"ESCALADE",
|
119 |
+
"PLONGÉE",
|
120 |
+
"VOILE",
|
121 |
+
"LUTTE",
|
122 |
+
"GYMNASTIQUE",
|
123 |
+
"TIR À L'ARC",
|
124 |
+
"ESCRIME",
|
125 |
+
"MOTOCYCLISME",
|
126 |
+
"TIR",
|
127 |
+
"ÉQUITATION",
|
128 |
+
"HANDBALL",
|
129 |
+
"SNOWBOARD",
|
130 |
+
"WATER-POLO",
|
131 |
+
"TRIATHLON",
|
132 |
+
"HALTÉROPHILIE",
|
133 |
+
"JUDO",
|
134 |
+
"TAEKWONDO",
|
135 |
+
"SKATEBOARD",
|
136 |
+
"WINDSURF",
|
137 |
+
"BADMINTON",
|
138 |
+
"TENNIS DE TABLE",
|
139 |
+
"NATATION SYNCHRONISÉE",
|
140 |
+
"AVIRON",
|
141 |
+
"LACROSSE"
|
142 |
+
];
|
143 |
+
|
144 |
+
export const italianSportsWords = [
|
145 |
+
"CALCIO",
|
146 |
+
"TENNIS",
|
147 |
+
"GOLF",
|
148 |
+
"BASEBALL",
|
149 |
+
"PALLACANESTRO",
|
150 |
+
"PALLAVOLO",
|
151 |
+
"HOCKEY",
|
152 |
+
"RUGBY",
|
153 |
+
"CRICKET",
|
154 |
+
"NUOTO",
|
155 |
+
"CORSA",
|
156 |
+
"CICLISMO",
|
157 |
+
"SCI",
|
158 |
+
"PUGILATO",
|
159 |
+
"PATTINAGGIO",
|
160 |
+
"SURF",
|
161 |
+
"ARRAMPICATA",
|
162 |
+
"IMMERSIONE",
|
163 |
+
"VELA",
|
164 |
+
"LOTTA",
|
165 |
+
"GINNASTICA",
|
166 |
+
"TIRO CON L'ARCO",
|
167 |
+
"SCHERMA",
|
168 |
+
"MOTOCICLISMO",
|
169 |
+
"TIRO A SEGNO",
|
170 |
+
"EQUITAZIONE",
|
171 |
+
"PALLAMANO",
|
172 |
+
"SNOWBOARD",
|
173 |
+
"PALLANUOTO",
|
174 |
+
"TRIATHLON",
|
175 |
+
"SOLLEVAMENTO PESI",
|
176 |
+
"JUDO",
|
177 |
+
"TAEKWONDO",
|
178 |
+
"SKATEBOARD",
|
179 |
+
"WINDSURF",
|
180 |
+
"BADMINTON",
|
181 |
+
"PING-PONG",
|
182 |
+
"NUOTO SINCRONIZZATO",
|
183 |
+
"CANOTTAGGIO",
|
184 |
+
"LACROSSE"
|
185 |
+
];
|
186 |
+
|
187 |
+
export const spanishSportsWords = [
|
188 |
+
"FÚTBOL",
|
189 |
+
"TENIS",
|
190 |
+
"GOLF",
|
191 |
+
"BÉISBOL",
|
192 |
+
"BALONCESTO",
|
193 |
+
"VOLEIBOL",
|
194 |
+
"HOCKEY",
|
195 |
+
"RUGBY",
|
196 |
+
"CRÍQUET",
|
197 |
+
"NATACIÓN",
|
198 |
+
"CARRERA",
|
199 |
+
"CICLISMO",
|
200 |
+
"ESQUÍ",
|
201 |
+
"BOXEO",
|
202 |
+
"PATINAJE",
|
203 |
+
"SURF",
|
204 |
+
"ESCALADA",
|
205 |
+
"BUCEO",
|
206 |
+
"VELA",
|
207 |
+
"LUCHA",
|
208 |
+
"GIMNASIA",
|
209 |
+
"TIRO CON ARCO",
|
210 |
+
"ESGRIMA",
|
211 |
+
"MOTOCICLISMO",
|
212 |
+
"TIRO",
|
213 |
+
"EQUITACIÓN",
|
214 |
+
"BALONMANO",
|
215 |
+
"SNOWBOARD",
|
216 |
+
"WATERPOLO",
|
217 |
+
"TRIATLÓN",
|
218 |
+
"HALTEROFILIA",
|
219 |
+
"JUDO",
|
220 |
+
"TAEKWONDO",
|
221 |
+
"SKATEBOARD",
|
222 |
+
"WINDSURF",
|
223 |
+
"BÁDMINTON",
|
224 |
+
"TENIS DE MESA",
|
225 |
+
"NATACIÓN SINCRONIZADA",
|
226 |
+
"REMO",
|
227 |
+
"LACROSSE"
|
228 |
+
];
|
229 |
+
|
230 |
+
|
231 |
+
export const getRandomSportsWord = (language: string = 'en') => {
|
232 |
+
let wordList;
|
233 |
+
switch (language) {
|
234 |
+
case 'de':
|
235 |
+
wordList = germanSportsWords;
|
236 |
+
break;
|
237 |
+
case 'fr':
|
238 |
+
wordList = frenchSportsWords;
|
239 |
+
break;
|
240 |
+
case 'it':
|
241 |
+
wordList = italianSportsWords;
|
242 |
+
break;
|
243 |
+
case 'es':
|
244 |
+
wordList = spanishSportsWords;
|
245 |
+
break;
|
246 |
+
default:
|
247 |
+
wordList = englishSportsWords;
|
248 |
+
}
|
249 |
+
return wordList[Math.floor(Math.random() * wordList.length)];
|
250 |
+
};
|
src/lib/words-standard.ts
ADDED
@@ -0,0 +1,1160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const englishWords = [
|
2 |
+
"DOG",
|
3 |
+
"CAT",
|
4 |
+
"SUN",
|
5 |
+
"RAIN",
|
6 |
+
"TREE",
|
7 |
+
"STAR",
|
8 |
+
"MOON",
|
9 |
+
"FISH",
|
10 |
+
"BIRD",
|
11 |
+
"CLOUD",
|
12 |
+
"SKY",
|
13 |
+
"WIND",
|
14 |
+
"SNOW",
|
15 |
+
"FLOWER",
|
16 |
+
"BUTTERFLY",
|
17 |
+
"WATER",
|
18 |
+
"OCEAN",
|
19 |
+
"RIVER",
|
20 |
+
"MOUNTAIN",
|
21 |
+
"FOREST",
|
22 |
+
"HOUSE",
|
23 |
+
"CANDLE",
|
24 |
+
"GARDEN",
|
25 |
+
"BRIDGE",
|
26 |
+
"ISLAND",
|
27 |
+
"BREEZE",
|
28 |
+
"LIGHT",
|
29 |
+
"THUNDER",
|
30 |
+
"RAINBOW",
|
31 |
+
"SMILE",
|
32 |
+
"FRIEND",
|
33 |
+
"FAMILY",
|
34 |
+
"APPLE",
|
35 |
+
"BANANA",
|
36 |
+
"CAR",
|
37 |
+
"BOAT",
|
38 |
+
"BALL",
|
39 |
+
"CAKE",
|
40 |
+
"FROG",
|
41 |
+
"HORSE",
|
42 |
+
"LION",
|
43 |
+
"MONKEY",
|
44 |
+
"PANDA",
|
45 |
+
"PLANE",
|
46 |
+
"TRAIN",
|
47 |
+
"CANDY",
|
48 |
+
"KITE",
|
49 |
+
"BALLOON",
|
50 |
+
"PARK",
|
51 |
+
"BEACH",
|
52 |
+
"TOY",
|
53 |
+
"BOOK",
|
54 |
+
"BUBBLE",
|
55 |
+
"SHELL",
|
56 |
+
"PEN",
|
57 |
+
"ICE",
|
58 |
+
"HAT",
|
59 |
+
"SHOE",
|
60 |
+
"CLOCK",
|
61 |
+
"BED",
|
62 |
+
"CUP",
|
63 |
+
"KEY",
|
64 |
+
"DOOR",
|
65 |
+
"CHICKEN",
|
66 |
+
"DUCK",
|
67 |
+
"SHEEP",
|
68 |
+
"COW",
|
69 |
+
"PIG",
|
70 |
+
"GOAT",
|
71 |
+
"FOX",
|
72 |
+
"BEAR",
|
73 |
+
"DEER",
|
74 |
+
"OWL",
|
75 |
+
"EGG",
|
76 |
+
"NEST",
|
77 |
+
"ROCK",
|
78 |
+
"LEAF",
|
79 |
+
"BRUSH",
|
80 |
+
"TOOTH",
|
81 |
+
"HAND",
|
82 |
+
"FEET",
|
83 |
+
"EYE",
|
84 |
+
"NOSE",
|
85 |
+
"EAR",
|
86 |
+
"MOUTH",
|
87 |
+
"CHILD",
|
88 |
+
"RAINCOAT",
|
89 |
+
"LADDER",
|
90 |
+
"WINDOW",
|
91 |
+
"DOCTOR",
|
92 |
+
"NURSE",
|
93 |
+
"TEACHER",
|
94 |
+
"STUDENT",
|
95 |
+
"PENCIL",
|
96 |
+
"TABLE",
|
97 |
+
"CHAIR",
|
98 |
+
"LAMP",
|
99 |
+
"MIRROR",
|
100 |
+
"BOWL",
|
101 |
+
"PLATE",
|
102 |
+
"SPOON",
|
103 |
+
"FORK",
|
104 |
+
"KNIFE",
|
105 |
+
"GLASS",
|
106 |
+
"STRAW",
|
107 |
+
"RULER",
|
108 |
+
"PAPER",
|
109 |
+
"BASKET",
|
110 |
+
"CARPET",
|
111 |
+
"SOFA",
|
112 |
+
"TELEVISION",
|
113 |
+
"RADIO",
|
114 |
+
"BATTERY",
|
115 |
+
"CANDLE",
|
116 |
+
"FENCE",
|
117 |
+
"MAILBOX",
|
118 |
+
"BRICK",
|
119 |
+
"LANTERN",
|
120 |
+
"WHEEL",
|
121 |
+
"BELL",
|
122 |
+
"UMBRELLA",
|
123 |
+
"TRUCK",
|
124 |
+
"MOTORCYCLE",
|
125 |
+
"BICYCLE",
|
126 |
+
"STOVE",
|
127 |
+
"REFRIGERATOR",
|
128 |
+
"MICROWAVE",
|
129 |
+
"WASHER",
|
130 |
+
"DRYER",
|
131 |
+
"FURNACE",
|
132 |
+
"FAN",
|
133 |
+
"PAINTBRUSH",
|
134 |
+
"BUCKET",
|
135 |
+
"SPONGE",
|
136 |
+
"SOAP",
|
137 |
+
"TOWEL",
|
138 |
+
"CLOTH",
|
139 |
+
"SCISSORS",
|
140 |
+
"TAPE",
|
141 |
+
"RIBBON",
|
142 |
+
"THREAD",
|
143 |
+
"NEEDLE",
|
144 |
+
"BUTTON",
|
145 |
+
"ZIPPER",
|
146 |
+
"SLIPPER",
|
147 |
+
"COAT",
|
148 |
+
"MITTEN",
|
149 |
+
"SCARF",
|
150 |
+
"GLOVE",
|
151 |
+
"PANTS",
|
152 |
+
"SHIRT",
|
153 |
+
"JACKET",
|
154 |
+
"DRESS",
|
155 |
+
"SKIRT",
|
156 |
+
"SOCK",
|
157 |
+
"BOOT",
|
158 |
+
"SANDAL",
|
159 |
+
"HAT",
|
160 |
+
"CAP",
|
161 |
+
"MASK",
|
162 |
+
"SUNGALASSES",
|
163 |
+
"WATCH",
|
164 |
+
"NECKLACE",
|
165 |
+
"BRACELET",
|
166 |
+
"RING",
|
167 |
+
"EARRING",
|
168 |
+
"BACKPACK",
|
169 |
+
"SUITCASE",
|
170 |
+
"TICKET",
|
171 |
+
"PASSPORT",
|
172 |
+
"MAP",
|
173 |
+
"COMPASS",
|
174 |
+
"TORCH",
|
175 |
+
"FLASHLIGHT",
|
176 |
+
"CAMPFIRE",
|
177 |
+
"TENT",
|
178 |
+
"SLEEPINGBAG",
|
179 |
+
"PICNIC",
|
180 |
+
"BENCH",
|
181 |
+
"FENCE",
|
182 |
+
"GATE",
|
183 |
+
"SIGN",
|
184 |
+
"CROSSWALK",
|
185 |
+
"TRAFFICLIGHT",
|
186 |
+
"SIDEWALK",
|
187 |
+
"LANTERN",
|
188 |
+
"BALLOON",
|
189 |
+
"POSTCARD",
|
190 |
+
"STAMP",
|
191 |
+
"LETTER",
|
192 |
+
"ENVELOPE",
|
193 |
+
"PARKING",
|
194 |
+
"STREET",
|
195 |
+
"HIGHWAY",
|
196 |
+
"BRIDGE",
|
197 |
+
"TUNNEL",
|
198 |
+
"STATUE",
|
199 |
+
"FOUNTAIN",
|
200 |
+
"TOWER",
|
201 |
+
"CASTLE",
|
202 |
+
"PYRAMID",
|
203 |
+
"PLANET",
|
204 |
+
"GALAXY",
|
205 |
+
"SATELLITE",
|
206 |
+
"ASTRONAUT",
|
207 |
+
"TELESCOPE",
|
208 |
+
"MICROSCOPE",
|
209 |
+
"MAGNET",
|
210 |
+
"BATTERY",
|
211 |
+
"BULB",
|
212 |
+
"SOCKET",
|
213 |
+
"PLUG",
|
214 |
+
"WIRE",
|
215 |
+
"SWITCH",
|
216 |
+
"CIRCUIT",
|
217 |
+
"ROBOT",
|
218 |
+
"COMPUTER",
|
219 |
+
"MOUSE",
|
220 |
+
"KEYBOARD",
|
221 |
+
"SCREEN",
|
222 |
+
"PRINTER",
|
223 |
+
"SPEAKER",
|
224 |
+
"HEADPHONE",
|
225 |
+
"PHONE",
|
226 |
+
"CAMERA"
|
227 |
+
];
|
228 |
+
|
229 |
+
export const germanWords = [
|
230 |
+
"HUND",
|
231 |
+
"KATZE",
|
232 |
+
"SONNE",
|
233 |
+
"REGEN",
|
234 |
+
"BAUM",
|
235 |
+
"STERN",
|
236 |
+
"MOND",
|
237 |
+
"FISCH",
|
238 |
+
"VOGEL",
|
239 |
+
"WOLKE",
|
240 |
+
"HIMMEL",
|
241 |
+
"WIND",
|
242 |
+
"SCHNEE",
|
243 |
+
"BLUME",
|
244 |
+
"SCHMETTERLING",
|
245 |
+
"WASSER",
|
246 |
+
"OZEAN",
|
247 |
+
"FLUSS",
|
248 |
+
"BERG",
|
249 |
+
"WALD",
|
250 |
+
"HAUS",
|
251 |
+
"KERZE",
|
252 |
+
"GARTEN",
|
253 |
+
"BRÜCKE",
|
254 |
+
"INSEL",
|
255 |
+
"BRISE",
|
256 |
+
"LICHT",
|
257 |
+
"DONNER",
|
258 |
+
"REGENBOGEN",
|
259 |
+
"LÄCHELN",
|
260 |
+
"FREUND",
|
261 |
+
"FAMILIE",
|
262 |
+
"APFEL",
|
263 |
+
"BANANE",
|
264 |
+
"AUTO",
|
265 |
+
"BOOT",
|
266 |
+
"BALL",
|
267 |
+
"KUCHEN",
|
268 |
+
"FROSCH",
|
269 |
+
"PFERD",
|
270 |
+
"LÖWE",
|
271 |
+
"AFFE",
|
272 |
+
"PANDA",
|
273 |
+
"FLUGZEUG",
|
274 |
+
"ZUG",
|
275 |
+
"SÜSSIGKEIT",
|
276 |
+
"DRACHEN",
|
277 |
+
"BALLON",
|
278 |
+
"PARK",
|
279 |
+
"STRAND",
|
280 |
+
"SPIELZEUG",
|
281 |
+
"BUCH",
|
282 |
+
"BLASE",
|
283 |
+
"MUSCHEL",
|
284 |
+
"STIFT",
|
285 |
+
"EIS",
|
286 |
+
"HUT",
|
287 |
+
"SCHUH",
|
288 |
+
"UHR",
|
289 |
+
"BETT",
|
290 |
+
"TASSE",
|
291 |
+
"SCHLÜSSEL",
|
292 |
+
"TÜR",
|
293 |
+
"HÜHNCHEN",
|
294 |
+
"ENTE",
|
295 |
+
"SCHAF",
|
296 |
+
"KUH",
|
297 |
+
"SCHWEIN",
|
298 |
+
"ZIEGE",
|
299 |
+
"FUCHS",
|
300 |
+
"BÄR",
|
301 |
+
"REH",
|
302 |
+
"EULE",
|
303 |
+
"EI",
|
304 |
+
"NEST",
|
305 |
+
"STEIN",
|
306 |
+
"BLATT",
|
307 |
+
"PINSEL",
|
308 |
+
"ZAHN",
|
309 |
+
"HAND",
|
310 |
+
"FÜSSE",
|
311 |
+
"AUGE",
|
312 |
+
"NASE",
|
313 |
+
"OHR",
|
314 |
+
"MUND",
|
315 |
+
"KIND",
|
316 |
+
"REGENMANTEL",
|
317 |
+
"LEITER",
|
318 |
+
"FENSTER",
|
319 |
+
"ARZT",
|
320 |
+
"KRANKENSCHWESTER",
|
321 |
+
"LEHRER",
|
322 |
+
"STUDENT",
|
323 |
+
"BLEISTIFT",
|
324 |
+
"TISCH",
|
325 |
+
"STUHL",
|
326 |
+
"LAMPE",
|
327 |
+
"SPIEGEL",
|
328 |
+
"SCHÜSSEL",
|
329 |
+
"TELLER",
|
330 |
+
"LÖFFEL",
|
331 |
+
"GABEL",
|
332 |
+
"MESSER",
|
333 |
+
"GLAS",
|
334 |
+
"STROHHALM",
|
335 |
+
"LINEAL",
|
336 |
+
"PAPIER",
|
337 |
+
"KORB",
|
338 |
+
"TEPPICH",
|
339 |
+
"SOFA",
|
340 |
+
"FERNSEHER",
|
341 |
+
"RADIO",
|
342 |
+
"BATTERIE",
|
343 |
+
"KERZE", // duplicate
|
344 |
+
"ZAUN",
|
345 |
+
"BRIEFKASTEN",
|
346 |
+
"BACKSTEIN",
|
347 |
+
"LATERNE",
|
348 |
+
"RAD",
|
349 |
+
"GLOCKE",
|
350 |
+
"REGENSCHIRM",
|
351 |
+
"LASTWAGEN",
|
352 |
+
"MOTORRAD",
|
353 |
+
"FAHRRAD",
|
354 |
+
"HERD",
|
355 |
+
"KÜHLSCHRANK",
|
356 |
+
"MIKROWELLE",
|
357 |
+
"WASCHMASCHINE",
|
358 |
+
"TROCKNER",
|
359 |
+
"OFEN",
|
360 |
+
"VENTILATOR",
|
361 |
+
"PINSEL", // paintbrush (same as index 78 but used for “brush” too)
|
362 |
+
"EIMER",
|
363 |
+
"SCHWAMM",
|
364 |
+
"SEIFE",
|
365 |
+
"HANDTUCH",
|
366 |
+
"STOFF",
|
367 |
+
"SCHERE",
|
368 |
+
"KLEBEBAND",
|
369 |
+
"BAND",
|
370 |
+
"FADEN",
|
371 |
+
"NADEL",
|
372 |
+
"KNOPF",
|
373 |
+
"REISSVERSCHLUSS",
|
374 |
+
"HAUSSCHUH",
|
375 |
+
"MANTEL",
|
376 |
+
"FAUSTHANDSCHUH",
|
377 |
+
"SCHAL",
|
378 |
+
"HANDSCHUH",
|
379 |
+
"HOSE",
|
380 |
+
"HEMD",
|
381 |
+
"JACKE",
|
382 |
+
"KLEID",
|
383 |
+
"ROCK",
|
384 |
+
"SOCKE",
|
385 |
+
"STIEFEL",
|
386 |
+
"SANDALE",
|
387 |
+
"HUT", // duplicate
|
388 |
+
"MÜTZE",
|
389 |
+
"MASKE",
|
390 |
+
"SONNENBRILLE",
|
391 |
+
"UHR",
|
392 |
+
"HALSKETTE",
|
393 |
+
"ARMBAND",
|
394 |
+
"RING",
|
395 |
+
"OHRRING",
|
396 |
+
"RUCKSACK",
|
397 |
+
"KOFFER",
|
398 |
+
"TICKET",
|
399 |
+
"REISEPASS",
|
400 |
+
"KARTE",
|
401 |
+
"KOMPASS",
|
402 |
+
"FACKEL",
|
403 |
+
"TASCHENLAMPE",
|
404 |
+
"LAGERFEUER",
|
405 |
+
"ZELT",
|
406 |
+
"SCHLAFSACK",
|
407 |
+
"PICKNICK",
|
408 |
+
"BANK",
|
409 |
+
"ZAUN", // duplicate
|
410 |
+
"TOR",
|
411 |
+
"SCHILD",
|
412 |
+
"ZEBRASTREIFEN",
|
413 |
+
"VERKEHRSAMPEL",
|
414 |
+
"BÜRGERSTEIG",
|
415 |
+
"LATERNE", // duplicate
|
416 |
+
"BALLON", // duplicate
|
417 |
+
"POSTKARTE",
|
418 |
+
"BRIEFMARKE",
|
419 |
+
"BRIEF",
|
420 |
+
"UMSCHLAG",
|
421 |
+
"PARKPLATZ",
|
422 |
+
"STRAßE",
|
423 |
+
"AUTOBAHN",
|
424 |
+
"BRÜCKE", // duplicate
|
425 |
+
"TUNNEL",
|
426 |
+
"STATUE",
|
427 |
+
"BRUNNEN",
|
428 |
+
"TURM",
|
429 |
+
"SCHLOSS",
|
430 |
+
"PYRAMIDE",
|
431 |
+
"PLANET",
|
432 |
+
"GALAXIE",
|
433 |
+
"SATELLIT",
|
434 |
+
"ASTRONAUT",
|
435 |
+
"TELESKOP",
|
436 |
+
"MIKROSKOP",
|
437 |
+
"MAGNET",
|
438 |
+
"BATTERIE", // duplicate
|
439 |
+
"GLÜHBIRNE",
|
440 |
+
"STECKDOSE",
|
441 |
+
"STECKER",
|
442 |
+
"DRAHT",
|
443 |
+
"SCHALTER",
|
444 |
+
"SCHALTUNG",
|
445 |
+
"ROBOTER",
|
446 |
+
"COMPUTER",
|
447 |
+
"MAUS",
|
448 |
+
"TASTATUR",
|
449 |
+
"BILDSCHIRM",
|
450 |
+
"DRUCKER",
|
451 |
+
"LAUTSPRECHER",
|
452 |
+
"KOPFHÖRER",
|
453 |
+
"TELEFON",
|
454 |
+
"KAMERA"
|
455 |
+
];
|
456 |
+
|
457 |
+
export const frenchWords = [
|
458 |
+
"CHIEN",
|
459 |
+
"CHAT",
|
460 |
+
"SOLEIL",
|
461 |
+
"PLUIE",
|
462 |
+
"ARBRE",
|
463 |
+
"ÉTOILE",
|
464 |
+
"LUNE",
|
465 |
+
"POISSON",
|
466 |
+
"OISEAU",
|
467 |
+
"NUAGE",
|
468 |
+
"CIEL",
|
469 |
+
"VENT",
|
470 |
+
"NEIGE",
|
471 |
+
"FLEUR",
|
472 |
+
"PAPILLON",
|
473 |
+
"EAU",
|
474 |
+
"OCÉAN",
|
475 |
+
"FLEUVE",
|
476 |
+
"MONTAGNE",
|
477 |
+
"FORÊT",
|
478 |
+
"MAISON",
|
479 |
+
"BOUGIE",
|
480 |
+
"JARDIN",
|
481 |
+
"PONT",
|
482 |
+
"ÎLE",
|
483 |
+
"BRISE",
|
484 |
+
"LUMIÈRE",
|
485 |
+
"TONNERRE",
|
486 |
+
"ARC-EN-CIEL",
|
487 |
+
"SOURIRE",
|
488 |
+
"AMI",
|
489 |
+
"FAMILLE",
|
490 |
+
"POMME",
|
491 |
+
"BANANE",
|
492 |
+
"VOITURE",
|
493 |
+
"BATEAU",
|
494 |
+
"BALLE",
|
495 |
+
"GÂTEAU",
|
496 |
+
"GRENOUILLE",
|
497 |
+
"CHEVAL",
|
498 |
+
"LION",
|
499 |
+
"SINGE",
|
500 |
+
"PANDA",
|
501 |
+
"AVION",
|
502 |
+
"TRAIN",
|
503 |
+
"BONBON",
|
504 |
+
"CERF-VOLANT",
|
505 |
+
"BALLON",
|
506 |
+
"PARC",
|
507 |
+
"PLAGE",
|
508 |
+
"JOUET",
|
509 |
+
"LIVRE",
|
510 |
+
"BULLE",
|
511 |
+
"COQUILLAGE",
|
512 |
+
"STYLO",
|
513 |
+
"GLACE",
|
514 |
+
"CHAPEAU",
|
515 |
+
"CHAUSSURE",
|
516 |
+
"HORLOGE",
|
517 |
+
"LIT",
|
518 |
+
"TASSE",
|
519 |
+
"CLÉ",
|
520 |
+
"PORTE",
|
521 |
+
"POULET",
|
522 |
+
"CANARD",
|
523 |
+
"MOUTON",
|
524 |
+
"VACHE",
|
525 |
+
"COCHON",
|
526 |
+
"CHÈVRE",
|
527 |
+
"RENARD",
|
528 |
+
"OURS",
|
529 |
+
"CERF",
|
530 |
+
"HIBOU",
|
531 |
+
"ŒUF",
|
532 |
+
"NID",
|
533 |
+
"ROCHE",
|
534 |
+
"FEUILLE",
|
535 |
+
"PINCEAU",
|
536 |
+
"DENT",
|
537 |
+
"MAIN",
|
538 |
+
"PIEDS",
|
539 |
+
"ŒIL",
|
540 |
+
"NEZ",
|
541 |
+
"OREILLE",
|
542 |
+
"BOUCHE",
|
543 |
+
"ENFANT",
|
544 |
+
"IMPERMÉABLE",
|
545 |
+
"ÉCHELLE",
|
546 |
+
"FENÊTRE",
|
547 |
+
"MÉDECIN",
|
548 |
+
"INFIRMIÈRE",
|
549 |
+
"ENSEIGNANT",
|
550 |
+
"ÉTUDIANT",
|
551 |
+
"CRAYON",
|
552 |
+
"TABLE",
|
553 |
+
"CHAISE",
|
554 |
+
"LAMPE",
|
555 |
+
"MIROIR",
|
556 |
+
"BOL",
|
557 |
+
"ASSIETTE",
|
558 |
+
"CUILLÈRE",
|
559 |
+
"FOURCHETTE",
|
560 |
+
"COUTEAU",
|
561 |
+
"VERRE",
|
562 |
+
"PAILLE",
|
563 |
+
"RÈGLE",
|
564 |
+
"PAPIER",
|
565 |
+
"PANIER",
|
566 |
+
"TAPIS",
|
567 |
+
"CANAPÉ",
|
568 |
+
"TÉLÉVISION",
|
569 |
+
"RADIO",
|
570 |
+
"PILE",
|
571 |
+
"BOUGIE", // duplicate
|
572 |
+
"CLÔTURE",
|
573 |
+
"BOÎTE AUX LETTRES",
|
574 |
+
"BRIQUE",
|
575 |
+
"LANTERNE",
|
576 |
+
"ROUE",
|
577 |
+
"CLOCHE",
|
578 |
+
"PARAPLUIE",
|
579 |
+
"CAMION",
|
580 |
+
"MOTO",
|
581 |
+
"VÉLO",
|
582 |
+
"CUISINIÈRE",
|
583 |
+
"RÉFRIGÉRATEUR",
|
584 |
+
"MICRO-ONDES",
|
585 |
+
"LAVE-LINGE",
|
586 |
+
"SÈCHE-LINGE",
|
587 |
+
"FOURNAISE",
|
588 |
+
"VENTILATEUR",
|
589 |
+
"PINCEAU", // paintbrush
|
590 |
+
"SEAU",
|
591 |
+
"ÉPONGE",
|
592 |
+
"SAVON",
|
593 |
+
"SERVIETTE",
|
594 |
+
"TISSU",
|
595 |
+
"CISEAUX",
|
596 |
+
"SCOTCH",
|
597 |
+
"RUBAN",
|
598 |
+
"FIL",
|
599 |
+
"AIGUILLE",
|
600 |
+
"BOUTON",
|
601 |
+
"FERMETURE ÉCLAIR",
|
602 |
+
"PANTOUFLE",
|
603 |
+
"MANTEAU",
|
604 |
+
"MOUFLE",
|
605 |
+
"ÉCHARPE",
|
606 |
+
"GANT",
|
607 |
+
"PANTALON",
|
608 |
+
"CHEMISE",
|
609 |
+
"VESTE",
|
610 |
+
"ROBE",
|
611 |
+
"JUPE",
|
612 |
+
"CHAUSSETTE",
|
613 |
+
"BOTTE",
|
614 |
+
"SANDALE",
|
615 |
+
"CHAPEAU", // duplicate
|
616 |
+
"CASQUETTE",
|
617 |
+
"MASQUE",
|
618 |
+
"LUNETTES DE SOLEIL",
|
619 |
+
"MONTRE",
|
620 |
+
"COLLIER",
|
621 |
+
"BRACELET",
|
622 |
+
"BAGUE",
|
623 |
+
"BOUCLE D'OREILLE",
|
624 |
+
"SAC À DOS",
|
625 |
+
"VALISE",
|
626 |
+
"BILLET",
|
627 |
+
"PASSEPORT",
|
628 |
+
"CARTE",
|
629 |
+
"BOUSSOLE",
|
630 |
+
"TORCHE",
|
631 |
+
"LAMPE DE POCHE",
|
632 |
+
"FEU DE CAMP",
|
633 |
+
"TENTE",
|
634 |
+
"SAC DE COUCHAGE",
|
635 |
+
"PIQUE-NIQUE",
|
636 |
+
"BANC",
|
637 |
+
"CLÔTURE", // duplicate
|
638 |
+
"PORTAIL",
|
639 |
+
"PANNEAU",
|
640 |
+
"PASSAGE PIÉTON",
|
641 |
+
"FEU DE SIGNALISATION",
|
642 |
+
"TROTTOIR",
|
643 |
+
"LANTERNE", // duplicate
|
644 |
+
"BALLON", // duplicate
|
645 |
+
"CARTE POSTALE",
|
646 |
+
"TIMBRE",
|
647 |
+
"LETTRE",
|
648 |
+
"ENVELOPPE",
|
649 |
+
"PARKING",
|
650 |
+
"RUE",
|
651 |
+
"AUTOROUTE",
|
652 |
+
"PONT", // duplicate
|
653 |
+
"TUNNEL",
|
654 |
+
"STATUE",
|
655 |
+
"FONTAINE",
|
656 |
+
"TOUR",
|
657 |
+
"CHÂTEAU",
|
658 |
+
"PYRAMIDE",
|
659 |
+
"PLANÈTE",
|
660 |
+
"GALAXIE",
|
661 |
+
"SATELLITE",
|
662 |
+
"ASTRONAUTE",
|
663 |
+
"TÉLESCOPE",
|
664 |
+
"MICROSCOPE",
|
665 |
+
"AIMANT",
|
666 |
+
"PILE", // duplicate
|
667 |
+
"AMPOULE",
|
668 |
+
"PRISE",
|
669 |
+
"FICHE",
|
670 |
+
"FIL",
|
671 |
+
"INTERRUPTEUR",
|
672 |
+
"CIRCUIT",
|
673 |
+
"ROBOT",
|
674 |
+
"ORDINATEUR",
|
675 |
+
"SOURIS",
|
676 |
+
"CLAVIER",
|
677 |
+
"ÉCRAN",
|
678 |
+
"IMPRIMANTE",
|
679 |
+
"HAUT-PARLEUR",
|
680 |
+
"CASQUE",
|
681 |
+
"TÉLÉPHONE",
|
682 |
+
"APPAREIL PHOTO"
|
683 |
+
];
|
684 |
+
|
685 |
+
export const italianWords = [
|
686 |
+
"CANE",
|
687 |
+
"GATTO",
|
688 |
+
"SOLE",
|
689 |
+
"PIOGGIA",
|
690 |
+
"ALBERO",
|
691 |
+
"STELLA",
|
692 |
+
"LUNA",
|
693 |
+
"PESCE",
|
694 |
+
"UCCELLO",
|
695 |
+
"NUVOLA",
|
696 |
+
"CIELO",
|
697 |
+
"VENTO",
|
698 |
+
"NEVE",
|
699 |
+
"FIORE",
|
700 |
+
"FARFALLA",
|
701 |
+
"ACQUA",
|
702 |
+
"OCEANO",
|
703 |
+
"FIUME",
|
704 |
+
"MONTAGNA",
|
705 |
+
"FORESTA",
|
706 |
+
"CASA",
|
707 |
+
"CANDELA",
|
708 |
+
"GIARDINO",
|
709 |
+
"PONTE",
|
710 |
+
"ISOLA",
|
711 |
+
"BREZZA",
|
712 |
+
"LUCE",
|
713 |
+
"TUONO",
|
714 |
+
"ARCOBALENO",
|
715 |
+
"SORRISO",
|
716 |
+
"AMICO",
|
717 |
+
"FAMIGLIA",
|
718 |
+
"MELA",
|
719 |
+
"BANANA",
|
720 |
+
"AUTO",
|
721 |
+
"BARCA",
|
722 |
+
"PALLA",
|
723 |
+
"TORTA",
|
724 |
+
"RANA",
|
725 |
+
"CAVALLO",
|
726 |
+
"LEONE",
|
727 |
+
"SCIMMIA",
|
728 |
+
"PANDA",
|
729 |
+
"AEREO",
|
730 |
+
"TRENO",
|
731 |
+
"CARAMELLA",
|
732 |
+
"AQUILONE",
|
733 |
+
"PALLONCINO",
|
734 |
+
"PARCO",
|
735 |
+
"SPIAGGIA",
|
736 |
+
"GIOCATTOLO",
|
737 |
+
"LIBRO",
|
738 |
+
"BOLLA",
|
739 |
+
"CONCHIGLIA",
|
740 |
+
"PENNA",
|
741 |
+
"GHIACCIO",
|
742 |
+
"CAPPELLO",
|
743 |
+
"SCARPA",
|
744 |
+
"OROLOGIO",
|
745 |
+
"LETTO",
|
746 |
+
"TAZZA",
|
747 |
+
"CHIAVE",
|
748 |
+
"PORTA",
|
749 |
+
"POLLO",
|
750 |
+
"ANATRA",
|
751 |
+
"PECORA",
|
752 |
+
"MUCCA",
|
753 |
+
"MAIALE",
|
754 |
+
"CAPRA",
|
755 |
+
"VOLPE",
|
756 |
+
"ORSO",
|
757 |
+
"CERVO",
|
758 |
+
"GUFO",
|
759 |
+
"UOVO",
|
760 |
+
"NIDO",
|
761 |
+
"ROCCIA",
|
762 |
+
"FOGLIA",
|
763 |
+
"PENNELLO",
|
764 |
+
"DENTE",
|
765 |
+
"MANO",
|
766 |
+
"PIEDI",
|
767 |
+
"OCCHIO",
|
768 |
+
"NASO",
|
769 |
+
"ORECCHIO",
|
770 |
+
"BOCCA",
|
771 |
+
"BAMBINO",
|
772 |
+
"IMPERMEABILE",
|
773 |
+
"SCALA",
|
774 |
+
"FINESTRA",
|
775 |
+
"MEDICO",
|
776 |
+
"INFERMIERA",
|
777 |
+
"INSEGNANTE",
|
778 |
+
"STUDENTE",
|
779 |
+
"MATITA",
|
780 |
+
"TAVOLO",
|
781 |
+
"SEDIA",
|
782 |
+
"LAMPADA",
|
783 |
+
"SPECCHIO",
|
784 |
+
"CIOTOLA",
|
785 |
+
"PIATTO",
|
786 |
+
"CUCCHIAIO",
|
787 |
+
"FORCHETTA",
|
788 |
+
"COLTELLO",
|
789 |
+
"BICCHIERE",
|
790 |
+
"CANNUCCIA",
|
791 |
+
"RIGHELLO",
|
792 |
+
"CARTA",
|
793 |
+
"CESTINO",
|
794 |
+
"TAPPETO",
|
795 |
+
"DIVANO",
|
796 |
+
"TELEVISIONE",
|
797 |
+
"RADIO",
|
798 |
+
"BATTERIA",
|
799 |
+
"CANDELA", // duplicate
|
800 |
+
"RECINTO",
|
801 |
+
"CASSETTA DELLE LETTERE",
|
802 |
+
"MATTONE",
|
803 |
+
"LANTERNA",
|
804 |
+
"RUOTA",
|
805 |
+
"CAMPANA",
|
806 |
+
"OMBRELLO",
|
807 |
+
"CAMION",
|
808 |
+
"MOTOCICLETTA",
|
809 |
+
"BICICLETTA",
|
810 |
+
"FORNELLO",
|
811 |
+
"FRIGORIFERO",
|
812 |
+
"MICROONDE",
|
813 |
+
"LAVATRICE",
|
814 |
+
"ASCIUGATRICE",
|
815 |
+
"FORNO",
|
816 |
+
"VENTILATORE",
|
817 |
+
"PENNELLO", // paintbrush
|
818 |
+
"SECCHIO",
|
819 |
+
"SPUGNA",
|
820 |
+
"SAPONE",
|
821 |
+
"ASCIUGAMANO",
|
822 |
+
"PANNO",
|
823 |
+
"FORBICI",
|
824 |
+
"NASTRO ADESIVO",
|
825 |
+
"NASTRO",
|
826 |
+
"FILO",
|
827 |
+
"AGO",
|
828 |
+
"BOTTONE",
|
829 |
+
"CERNIERA",
|
830 |
+
"PANTOFOLA",
|
831 |
+
"CAPPOTTO",
|
832 |
+
"MOFFOLA",
|
833 |
+
"SCIARPA",
|
834 |
+
"GUANTO",
|
835 |
+
"PANTALONI",
|
836 |
+
"CAMICIA",
|
837 |
+
"GIACCA",
|
838 |
+
"VESTITO",
|
839 |
+
"GONNA",
|
840 |
+
"CALZINO",
|
841 |
+
"STIVALE", // corrected translation for BOOT
|
842 |
+
"SANDALO",
|
843 |
+
"CAPPELLO", // duplicate
|
844 |
+
"BERRETTO", // instead of MÜTZE, let's keep it consistent in Italian
|
845 |
+
"MASCHERA",
|
846 |
+
"OCCHIALI DA SOLE",
|
847 |
+
"OROLOGIO",
|
848 |
+
"COLLANA",
|
849 |
+
"BRACCIALETTO",
|
850 |
+
"ANELLO",
|
851 |
+
"ORECCHINO",
|
852 |
+
"ZAINO",
|
853 |
+
"VALIGIA",
|
854 |
+
"BIGLIETTO",
|
855 |
+
"PASSAPORTO",
|
856 |
+
"CARTINA",
|
857 |
+
"BUSSOLA",
|
858 |
+
"TORCIA",
|
859 |
+
"TORCIA ELETTRICA",
|
860 |
+
"FALÒ",
|
861 |
+
"TENDA",
|
862 |
+
"SACCO A PELO",
|
863 |
+
"PICNIC",
|
864 |
+
"PANCHINA",
|
865 |
+
"RECINTO", // duplicate
|
866 |
+
"CANCELLO",
|
867 |
+
"SEGNALE",
|
868 |
+
"STRISCE PEDONALI",
|
869 |
+
"SEMAFORO",
|
870 |
+
"MARCIAPIEDE",
|
871 |
+
"LANTERNA", // duplicate
|
872 |
+
"PALLONCINO", // duplicate
|
873 |
+
"CARTOLINA",
|
874 |
+
"FRANCOBOLLO",
|
875 |
+
"LETTERA",
|
876 |
+
"BUSTA",
|
877 |
+
"PARCHEGGIO",
|
878 |
+
"STRADA",
|
879 |
+
"AUTOSTRADA",
|
880 |
+
"PONTE", // duplicate
|
881 |
+
"TUNNEL",
|
882 |
+
"STATUA",
|
883 |
+
"FONTANA",
|
884 |
+
"TORRE",
|
885 |
+
"CASTELLO",
|
886 |
+
"PIRAMIDE",
|
887 |
+
"PIANETA",
|
888 |
+
"GALASSIA",
|
889 |
+
"SATELLITE",
|
890 |
+
"ASTRONAUTA",
|
891 |
+
"TELESCOPIO",
|
892 |
+
"MICROSCOPIO",
|
893 |
+
"MAGNETE",
|
894 |
+
"BATTERIA", // duplicate
|
895 |
+
"LAMPADINA",
|
896 |
+
"PRESA",
|
897 |
+
"SPINA",
|
898 |
+
"FILO",
|
899 |
+
"INTERRUTTORE",
|
900 |
+
"CIRCUITO",
|
901 |
+
"ROBOT",
|
902 |
+
"COMPUTER",
|
903 |
+
"MOUSE",
|
904 |
+
"TASTIERA",
|
905 |
+
"SCHERMO",
|
906 |
+
"STAMPANTE",
|
907 |
+
"ALTOPARLANTE",
|
908 |
+
"CUFFIE",
|
909 |
+
"TELEFONO",
|
910 |
+
"FOTOCAMERA"
|
911 |
+
];
|
912 |
+
|
913 |
+
export const spanishWords = [
|
914 |
+
"PERRO",
|
915 |
+
"GATO",
|
916 |
+
"SOL",
|
917 |
+
"LLUVIA",
|
918 |
+
"ÁRBOL",
|
919 |
+
"ESTRELLA",
|
920 |
+
"LUNA",
|
921 |
+
"PEZ",
|
922 |
+
"PÁJARO",
|
923 |
+
"NUBE",
|
924 |
+
"CIELO",
|
925 |
+
"VIENTO",
|
926 |
+
"NIEVE",
|
927 |
+
"FLOR",
|
928 |
+
"MARIPOSA",
|
929 |
+
"AGUA",
|
930 |
+
"OCÉANO",
|
931 |
+
"RÍO",
|
932 |
+
"MONTAÑA",
|
933 |
+
"BOSQUE",
|
934 |
+
"CASA",
|
935 |
+
"VELA",
|
936 |
+
"JARDÍN",
|
937 |
+
"PUENTE",
|
938 |
+
"ISLA",
|
939 |
+
"BRISA",
|
940 |
+
"LUZ",
|
941 |
+
"TRUENO",
|
942 |
+
"ARCOÍRIS",
|
943 |
+
"SONRISA",
|
944 |
+
"AMIGO",
|
945 |
+
"FAMILIA",
|
946 |
+
"MANZANA",
|
947 |
+
"BANANA",
|
948 |
+
"COCHE",
|
949 |
+
"BARCO",
|
950 |
+
"PELOTA",
|
951 |
+
"PASTEL",
|
952 |
+
"RANA",
|
953 |
+
"CABALLO",
|
954 |
+
"LEÓN",
|
955 |
+
"MONO",
|
956 |
+
"OSO PANDA",
|
957 |
+
"AVIÓN",
|
958 |
+
"TREN",
|
959 |
+
"CARAMELO",
|
960 |
+
"COMETA",
|
961 |
+
"GLOBO",
|
962 |
+
"PARQUE",
|
963 |
+
"PLAYA",
|
964 |
+
"JUGUETE",
|
965 |
+
"LIBRO",
|
966 |
+
"BURBUJA",
|
967 |
+
"CONCHA",
|
968 |
+
"BOLÍGRAFO",
|
969 |
+
"HIELO",
|
970 |
+
"SOMBRERO",
|
971 |
+
"ZAPATO",
|
972 |
+
"RELOJ",
|
973 |
+
"CAMA",
|
974 |
+
"TAZA",
|
975 |
+
"LLAVE",
|
976 |
+
"PUERTA",
|
977 |
+
"POLLO",
|
978 |
+
"PATO",
|
979 |
+
"OVEJA",
|
980 |
+
"VACA",
|
981 |
+
"CERDO",
|
982 |
+
"CABRA",
|
983 |
+
"ZORRO",
|
984 |
+
"OSO",
|
985 |
+
"CIERVO",
|
986 |
+
"BÚHO",
|
987 |
+
"HUEVO",
|
988 |
+
"NIDO",
|
989 |
+
"ROCA",
|
990 |
+
"HOJA",
|
991 |
+
"PINCEL",
|
992 |
+
"DIENTE",
|
993 |
+
"MANO",
|
994 |
+
"PIES",
|
995 |
+
"OJO",
|
996 |
+
"NARIZ",
|
997 |
+
"OREJA",
|
998 |
+
"BOCA",
|
999 |
+
"NIÑO",
|
1000 |
+
"IMPERMEABLE",
|
1001 |
+
"ESCALERA",
|
1002 |
+
"VENTANA",
|
1003 |
+
"MÉDICO",
|
1004 |
+
"ENFERMERA",
|
1005 |
+
"MAESTRO",
|
1006 |
+
"ESTUDIANTE",
|
1007 |
+
"LÁPIZ",
|
1008 |
+
"MESA",
|
1009 |
+
"SILLA",
|
1010 |
+
"LÁMPARA",
|
1011 |
+
"ESPEJO",
|
1012 |
+
"CUENCO",
|
1013 |
+
"PLATO",
|
1014 |
+
"CUCHARA",
|
1015 |
+
"TENEDOR",
|
1016 |
+
"CUCHILLO",
|
1017 |
+
"VASO",
|
1018 |
+
"PAJITA",
|
1019 |
+
"REGLA",
|
1020 |
+
"PAPEL",
|
1021 |
+
"CESTA",
|
1022 |
+
"ALFOMBRA",
|
1023 |
+
"SOFÁ",
|
1024 |
+
"TELEVISIÓN",
|
1025 |
+
"RADIO",
|
1026 |
+
"BATERÍA",
|
1027 |
+
"VELA", // duplicate
|
1028 |
+
"VALLA",
|
1029 |
+
"BUZÓN",
|
1030 |
+
"LADRILLO",
|
1031 |
+
"FAROL",
|
1032 |
+
"RUEDA",
|
1033 |
+
"CAMPANA",
|
1034 |
+
"PARAGUAS",
|
1035 |
+
"CAMIÓN",
|
1036 |
+
"MOTOCICLETA",
|
1037 |
+
"BICICLETA",
|
1038 |
+
"ESTUFA",
|
1039 |
+
"REFRIGERADOR",
|
1040 |
+
"MICROONDAS",
|
1041 |
+
"LAVADORA",
|
1042 |
+
"SECADORA",
|
1043 |
+
"HORNO",
|
1044 |
+
"VENTILADOR",
|
1045 |
+
"PINCEL", // paintbrush
|
1046 |
+
"CUBO",
|
1047 |
+
"ESPONJA",
|
1048 |
+
"JABÓN",
|
1049 |
+
"TOALLA",
|
1050 |
+
"TELA",
|
1051 |
+
"TIJERAS",
|
1052 |
+
"CINTA",
|
1053 |
+
"CINTA", // RIBBON (could also say “LISTÓN”)
|
1054 |
+
"HILO",
|
1055 |
+
"AGUJA",
|
1056 |
+
"BOTÓN",
|
1057 |
+
"CREMALLERA",
|
1058 |
+
"PANTUFLA",
|
1059 |
+
"ABRIGO",
|
1060 |
+
"MANOPLA",
|
1061 |
+
"BUFANDA",
|
1062 |
+
"GUANTE",
|
1063 |
+
"PANTALONES",
|
1064 |
+
"CAMISA",
|
1065 |
+
"CHAQUETA",
|
1066 |
+
"VESTIDO",
|
1067 |
+
"FALDA",
|
1068 |
+
"CALCETÍN",
|
1069 |
+
"BOTA",
|
1070 |
+
"SANDALIA",
|
1071 |
+
"SOMBRERO", // duplicate
|
1072 |
+
"GORRA",
|
1073 |
+
"MÁSCARA",
|
1074 |
+
"GAFAS DE SOL",
|
1075 |
+
"RELOJ",
|
1076 |
+
"COLLAR",
|
1077 |
+
"PULSERA",
|
1078 |
+
"ANILLO",
|
1079 |
+
"PENDIENTE",
|
1080 |
+
"MOCHILA",
|
1081 |
+
"MALETA",
|
1082 |
+
"BILLETE",
|
1083 |
+
"PASAPORTE",
|
1084 |
+
"MAPA",
|
1085 |
+
"BRÚJULA",
|
1086 |
+
"ANTORCHA",
|
1087 |
+
"LINTERNA",
|
1088 |
+
"HOGUERA",
|
1089 |
+
"TIENDA DE CAMPAÑA",
|
1090 |
+
"SACO DE DORMIR",
|
1091 |
+
"PICNIC",
|
1092 |
+
"BANCO",
|
1093 |
+
"VALLA", // duplicate
|
1094 |
+
"PUERTA", // gate can be “PUERTA” or “PORTÓN”; used PUERTA earlier for “door,” so let's keep “PORTÓN” for gate below
|
1095 |
+
"SEÑAL",
|
1096 |
+
"PASO DE PEATONES",
|
1097 |
+
"SEMÁFORO",
|
1098 |
+
"ACERA",
|
1099 |
+
"FAROL", // duplicate
|
1100 |
+
"GLOBO", // duplicate
|
1101 |
+
"POSTAL",
|
1102 |
+
"SELLO",
|
1103 |
+
"CARTA",
|
1104 |
+
"SOBRE",
|
1105 |
+
"ESTACIONAMIENTO",
|
1106 |
+
"CALLE",
|
1107 |
+
"AUTOPISTA",
|
1108 |
+
"PUENTE", // duplicate
|
1109 |
+
"TÚNEL",
|
1110 |
+
"ESTATUA",
|
1111 |
+
"FUENTE",
|
1112 |
+
"TORRE",
|
1113 |
+
"CASTILLO",
|
1114 |
+
"PIRÁMIDE",
|
1115 |
+
"PLANETA",
|
1116 |
+
"GALAXIA",
|
1117 |
+
"SATÉLITE",
|
1118 |
+
"ASTRONAUTA",
|
1119 |
+
"TELESCOPIO",
|
1120 |
+
"MICROSCOPIO",
|
1121 |
+
"IMÁN",
|
1122 |
+
"BATERÍA", // duplicate
|
1123 |
+
"BOMBILLA",
|
1124 |
+
"ENCHUFE",
|
1125 |
+
"ENCHUFE", // PLUG (maybe “CLAVIJA,” but “ENCHUFE” is also used)
|
1126 |
+
"CABLE",
|
1127 |
+
"INTERRUPTOR",
|
1128 |
+
"CIRCUITO",
|
1129 |
+
"ROBOT",
|
1130 |
+
"ORDENADOR",
|
1131 |
+
"RATÓN",
|
1132 |
+
"TECLADO",
|
1133 |
+
"PANTALLA",
|
1134 |
+
"IMPRESORA",
|
1135 |
+
"ALTAVOZ",
|
1136 |
+
"AURICULARES",
|
1137 |
+
"TELÉFONO",
|
1138 |
+
"CÁMARA"
|
1139 |
+
];
|
1140 |
+
|
1141 |
+
export const getRandomWord = (language: string = 'en') => {
|
1142 |
+
let wordList;
|
1143 |
+
switch (language) {
|
1144 |
+
case 'de':
|
1145 |
+
wordList = germanWords;
|
1146 |
+
break;
|
1147 |
+
case 'fr':
|
1148 |
+
wordList = frenchWords;
|
1149 |
+
break;
|
1150 |
+
case 'it':
|
1151 |
+
wordList = italianWords;
|
1152 |
+
break;
|
1153 |
+
case 'es':
|
1154 |
+
wordList = spanishWords;
|
1155 |
+
break;
|
1156 |
+
default:
|
1157 |
+
wordList = englishWords;
|
1158 |
+
}
|
1159 |
+
return wordList[Math.floor(Math.random() * wordList.length)];
|
1160 |
+
};
|
src/lib/words.ts
DELETED
@@ -1,337 +0,0 @@
|
|
1 |
-
export const englishWords = [
|
2 |
-
"DOG",
|
3 |
-
"CAT",
|
4 |
-
"SUN",
|
5 |
-
"RAIN",
|
6 |
-
"TREE",
|
7 |
-
"STAR",
|
8 |
-
"MOON",
|
9 |
-
"FISH",
|
10 |
-
"BIRD",
|
11 |
-
"CLOUD",
|
12 |
-
"SKY",
|
13 |
-
"WIND",
|
14 |
-
"SNOW",
|
15 |
-
"FLOWER",
|
16 |
-
"BUTTERFLY",
|
17 |
-
"WATER",
|
18 |
-
"OCEAN",
|
19 |
-
"RIVER",
|
20 |
-
"MOUNTAIN",
|
21 |
-
"FOREST",
|
22 |
-
"HOUSE",
|
23 |
-
"CANDLE",
|
24 |
-
"GARDEN",
|
25 |
-
"BRIDGE",
|
26 |
-
"ISLAND",
|
27 |
-
"BREEZE",
|
28 |
-
"LIGHT",
|
29 |
-
"THUNDER",
|
30 |
-
"RAINBOW",
|
31 |
-
"SMILE",
|
32 |
-
"FRIEND",
|
33 |
-
"FAMILY",
|
34 |
-
"APPLE",
|
35 |
-
"BANANA",
|
36 |
-
"CAR",
|
37 |
-
"BOAT",
|
38 |
-
"BALL",
|
39 |
-
"CAKE",
|
40 |
-
"FROG",
|
41 |
-
"HORSE",
|
42 |
-
"LION",
|
43 |
-
"MONKEY",
|
44 |
-
"PANDA",
|
45 |
-
"PLANE",
|
46 |
-
"TRAIN",
|
47 |
-
"CANDY",
|
48 |
-
"JUMP",
|
49 |
-
"PLAY",
|
50 |
-
"SLEEP",
|
51 |
-
"LAUGH",
|
52 |
-
"DREAM",
|
53 |
-
"HAPPY",
|
54 |
-
"FUN",
|
55 |
-
"COLOR",
|
56 |
-
"BRIGHT",
|
57 |
-
"WISH",
|
58 |
-
"LOVE",
|
59 |
-
"PEACE",
|
60 |
-
"HUG",
|
61 |
-
"KISS",
|
62 |
-
"ZOO",
|
63 |
-
"PARK",
|
64 |
-
"BEACH",
|
65 |
-
"TOY",
|
66 |
-
"BOOK",
|
67 |
-
"BUBBLE",
|
68 |
-
"SHELL",
|
69 |
-
"PEN",
|
70 |
-
"ICE",
|
71 |
-
"CAKE",
|
72 |
-
"HAT",
|
73 |
-
"SHOE",
|
74 |
-
"CLOCK",
|
75 |
-
"BED",
|
76 |
-
"CUP",
|
77 |
-
"KEY",
|
78 |
-
"DOOR",
|
79 |
-
"CHICKEN",
|
80 |
-
"DUCK",
|
81 |
-
"SHEEP",
|
82 |
-
"COW",
|
83 |
-
"PIG",
|
84 |
-
"GOAT",
|
85 |
-
"FOX",
|
86 |
-
"BEAR",
|
87 |
-
"DEER",
|
88 |
-
"OWL",
|
89 |
-
"EGG",
|
90 |
-
"NEST",
|
91 |
-
"ROCK",
|
92 |
-
"LEAF",
|
93 |
-
"BRUSH",
|
94 |
-
"TOOTH",
|
95 |
-
"HAND",
|
96 |
-
"FEET",
|
97 |
-
"EYE",
|
98 |
-
"NOSE",
|
99 |
-
"EAR",
|
100 |
-
"MOUTH",
|
101 |
-
"CHILD",
|
102 |
-
"KITE",
|
103 |
-
"BALLON"
|
104 |
-
];
|
105 |
-
|
106 |
-
export const germanWords = [
|
107 |
-
"HUND",
|
108 |
-
"KATZE",
|
109 |
-
"SONNE",
|
110 |
-
"REGEN",
|
111 |
-
"BAUM",
|
112 |
-
"STERN",
|
113 |
-
"MOND",
|
114 |
-
"FISCH",
|
115 |
-
"VOGEL",
|
116 |
-
"WOLKE",
|
117 |
-
"HIMMEL",
|
118 |
-
"WIND",
|
119 |
-
"SCHNEE",
|
120 |
-
"BLUME",
|
121 |
-
"WASSER",
|
122 |
-
"OZEAN",
|
123 |
-
"FLUSS",
|
124 |
-
"BERG",
|
125 |
-
"WALD",
|
126 |
-
"HAUS",
|
127 |
-
"KERZE",
|
128 |
-
"GARTEN",
|
129 |
-
"BRÜCKE",
|
130 |
-
"INSEL",
|
131 |
-
"LICHT",
|
132 |
-
"DONNER",
|
133 |
-
"LÄCHELN",
|
134 |
-
"FREUND",
|
135 |
-
"FAMILIE",
|
136 |
-
"APFEL",
|
137 |
-
"BANANE",
|
138 |
-
"AUTO",
|
139 |
-
"BOOT",
|
140 |
-
"BALL",
|
141 |
-
"KUCHEN",
|
142 |
-
"FROSCH",
|
143 |
-
"PFERD",
|
144 |
-
"LÖWE",
|
145 |
-
"AFFE",
|
146 |
-
"PANDA",
|
147 |
-
"FLUGZEUG",
|
148 |
-
"ZUG",
|
149 |
-
"BONBON",
|
150 |
-
"SPRINGEN",
|
151 |
-
"SPIELEN",
|
152 |
-
"SCHLAFEN",
|
153 |
-
"LACHEN",
|
154 |
-
"TRAUM",
|
155 |
-
"GLÜCK",
|
156 |
-
"FARBE"
|
157 |
-
];
|
158 |
-
|
159 |
-
export const frenchWords = [
|
160 |
-
"CHIEN",
|
161 |
-
"CHAT",
|
162 |
-
"SOLEIL",
|
163 |
-
"PLUIE",
|
164 |
-
"ARBRE",
|
165 |
-
"ÉTOILE",
|
166 |
-
"LUNE",
|
167 |
-
"POISSON",
|
168 |
-
"OISEAU",
|
169 |
-
"NUAGE",
|
170 |
-
"CIEL",
|
171 |
-
"VENT",
|
172 |
-
"NEIGE",
|
173 |
-
"FLEUR",
|
174 |
-
"EAU",
|
175 |
-
"OCÉAN",
|
176 |
-
"RIVIÈRE",
|
177 |
-
"MONTAGNE",
|
178 |
-
"FORÊT",
|
179 |
-
"MAISON",
|
180 |
-
"BOUGIE",
|
181 |
-
"JARDIN",
|
182 |
-
"PONT",
|
183 |
-
"ÎLE",
|
184 |
-
"LUMIÈRE",
|
185 |
-
"TONNERRE",
|
186 |
-
"SOURIRE",
|
187 |
-
"AMI",
|
188 |
-
"FAMILLE",
|
189 |
-
"POMME",
|
190 |
-
"BANANE",
|
191 |
-
"VOITURE",
|
192 |
-
"BATEAU",
|
193 |
-
"BALLON",
|
194 |
-
"GÂTEAU",
|
195 |
-
"GRENOUILLE",
|
196 |
-
"CHEVAL",
|
197 |
-
"LION",
|
198 |
-
"SINGE",
|
199 |
-
"PANDA",
|
200 |
-
"AVION",
|
201 |
-
"TRAIN",
|
202 |
-
"BONBON",
|
203 |
-
"SAUTER",
|
204 |
-
"JOUER",
|
205 |
-
"DORMIR",
|
206 |
-
"RIRE",
|
207 |
-
"RÊVE",
|
208 |
-
"BONHEUR",
|
209 |
-
"COULEUR"
|
210 |
-
];
|
211 |
-
|
212 |
-
export const italianWords = [
|
213 |
-
"CANE",
|
214 |
-
"GATTO",
|
215 |
-
"SOLE",
|
216 |
-
"PIOGGIA",
|
217 |
-
"ALBERO",
|
218 |
-
"STELLA",
|
219 |
-
"LUNA",
|
220 |
-
"PESCE",
|
221 |
-
"UCCELLO",
|
222 |
-
"NUVOLA",
|
223 |
-
"CIELO",
|
224 |
-
"VENTO",
|
225 |
-
"NEVE",
|
226 |
-
"FIORE",
|
227 |
-
"ACQUA",
|
228 |
-
"OCEANO",
|
229 |
-
"FIUME",
|
230 |
-
"MONTAGNA",
|
231 |
-
"FORESTA",
|
232 |
-
"CASA",
|
233 |
-
"CANDELA",
|
234 |
-
"GIARDINO",
|
235 |
-
"PONTE",
|
236 |
-
"ISOLA",
|
237 |
-
"LUCE",
|
238 |
-
"TUONO",
|
239 |
-
"SORRISO",
|
240 |
-
"AMICO",
|
241 |
-
"FAMIGLIA",
|
242 |
-
"MELA",
|
243 |
-
"BANANA",
|
244 |
-
"AUTO",
|
245 |
-
"BARCA",
|
246 |
-
"PALLA",
|
247 |
-
"TORTA",
|
248 |
-
"RANA",
|
249 |
-
"CAVALLO",
|
250 |
-
"LEONE",
|
251 |
-
"SCIMMIA",
|
252 |
-
"PANDA",
|
253 |
-
"AEREO",
|
254 |
-
"TRENO",
|
255 |
-
"CARAMELLA",
|
256 |
-
"SALTARE",
|
257 |
-
"GIOCARE",
|
258 |
-
"DORMIRE",
|
259 |
-
"RIDERE",
|
260 |
-
"SOGNO",
|
261 |
-
"FELICITÀ",
|
262 |
-
"COLORE"
|
263 |
-
];
|
264 |
-
|
265 |
-
export const spanishWords = [
|
266 |
-
"PERRO",
|
267 |
-
"GATO",
|
268 |
-
"SOL",
|
269 |
-
"LLUVIA",
|
270 |
-
"ÁRBOL",
|
271 |
-
"ESTRELLA",
|
272 |
-
"LUNA",
|
273 |
-
"PEZ",
|
274 |
-
"PÁJARO",
|
275 |
-
"NUBE",
|
276 |
-
"CIELO",
|
277 |
-
"VIENTO",
|
278 |
-
"NIEVE",
|
279 |
-
"FLOR",
|
280 |
-
"AGUA",
|
281 |
-
"OCÉANO",
|
282 |
-
"RÍO",
|
283 |
-
"MONTAÑA",
|
284 |
-
"BOSQUE",
|
285 |
-
"CASA",
|
286 |
-
"VELA",
|
287 |
-
"JARDÍN",
|
288 |
-
"PUENTE",
|
289 |
-
"ISLA",
|
290 |
-
"LUZ",
|
291 |
-
"TRUENO",
|
292 |
-
"SONRISA",
|
293 |
-
"AMIGO",
|
294 |
-
"FAMILIA",
|
295 |
-
"MANZANA",
|
296 |
-
"PLÁTANO",
|
297 |
-
"COCHE",
|
298 |
-
"BARCO",
|
299 |
-
"PELOTA",
|
300 |
-
"PASTEL",
|
301 |
-
"RANA",
|
302 |
-
"CABALLO",
|
303 |
-
"LEÓN",
|
304 |
-
"MONO",
|
305 |
-
"PANDA",
|
306 |
-
"AVIÓN",
|
307 |
-
"TREN",
|
308 |
-
"CARAMELO",
|
309 |
-
"SALTAR",
|
310 |
-
"JUGAR",
|
311 |
-
"DORMIR",
|
312 |
-
"REÍR",
|
313 |
-
"SUEÑO",
|
314 |
-
"FELICIDAD",
|
315 |
-
"COLOR"
|
316 |
-
];
|
317 |
-
|
318 |
-
export const getRandomWord = (language: string = 'en') => {
|
319 |
-
let wordList;
|
320 |
-
switch (language) {
|
321 |
-
case 'de':
|
322 |
-
wordList = germanWords;
|
323 |
-
break;
|
324 |
-
case 'fr':
|
325 |
-
wordList = frenchWords;
|
326 |
-
break;
|
327 |
-
case 'it':
|
328 |
-
wordList = italianWords;
|
329 |
-
break;
|
330 |
-
case 'es':
|
331 |
-
wordList = spanishWords;
|
332 |
-
break;
|
333 |
-
default:
|
334 |
-
wordList = englishWords;
|
335 |
-
}
|
336 |
-
return wordList[Math.floor(Math.random() * wordList.length)];
|
337 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
supabase/functions/submit-high-score/index.ts
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { createClient } from 'https://esm.sh/@supabase/[email protected]'
|
2 |
+
|
3 |
+
const corsHeaders = {
|
4 |
+
'Access-Control-Allow-Origin': '*',
|
5 |
+
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
6 |
+
}
|
7 |
+
|
8 |
+
Deno.serve(async (req) => {
|
9 |
+
if (req.method === 'OPTIONS') {
|
10 |
+
return new Response(null, { headers: corsHeaders })
|
11 |
+
}
|
12 |
+
|
13 |
+
try {
|
14 |
+
const { playerName, score, avgWordsPerRound, sessionId, theme } = await req.json()
|
15 |
+
|
16 |
+
if (!playerName || !score || !avgWordsPerRound || !sessionId || !theme) {
|
17 |
+
throw new Error('Missing required fields')
|
18 |
+
}
|
19 |
+
|
20 |
+
const supabaseClient = createClient(
|
21 |
+
Deno.env.get('SUPABASE_URL') ?? '',
|
22 |
+
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
|
23 |
+
{
|
24 |
+
auth: {
|
25 |
+
persistSession: false,
|
26 |
+
},
|
27 |
+
}
|
28 |
+
)
|
29 |
+
|
30 |
+
console.log('Submitting score:', { playerName, score, avgWordsPerRound, sessionId, theme })
|
31 |
+
|
32 |
+
const { data, error } = await supabaseClient.rpc('check_and_update_high_score', {
|
33 |
+
p_player_name: playerName,
|
34 |
+
p_score: score,
|
35 |
+
p_avg_words_per_round: avgWordsPerRound,
|
36 |
+
p_session_id: sessionId,
|
37 |
+
p_theme: theme
|
38 |
+
})
|
39 |
+
|
40 |
+
if (error) {
|
41 |
+
throw error
|
42 |
+
}
|
43 |
+
|
44 |
+
return new Response(
|
45 |
+
JSON.stringify({ success: true, data }),
|
46 |
+
{
|
47 |
+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
48 |
+
status: 200,
|
49 |
+
},
|
50 |
+
)
|
51 |
+
} catch (error) {
|
52 |
+
console.error('Error:', error.message)
|
53 |
+
return new Response(
|
54 |
+
JSON.stringify({ error: error.message }),
|
55 |
+
{
|
56 |
+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
57 |
+
status: 400,
|
58 |
+
},
|
59 |
+
)
|
60 |
+
}
|
61 |
+
})
|
supabase/functions/validate-sentence/index.ts
DELETED
@@ -1,56 +0,0 @@
|
|
1 |
-
import { serve } from "https://deno.land/[email protected]/http/server.ts";
|
2 |
-
import { Mistral } from "npm:@mistralai/mistralai";
|
3 |
-
|
4 |
-
const corsHeaders = {
|
5 |
-
'Access-Control-Allow-Origin': '*',
|
6 |
-
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
7 |
-
};
|
8 |
-
|
9 |
-
serve(async (req) => {
|
10 |
-
if (req.method === 'OPTIONS') {
|
11 |
-
return new Response(null, { headers: corsHeaders });
|
12 |
-
}
|
13 |
-
|
14 |
-
try {
|
15 |
-
const { sentence } = await req.json();
|
16 |
-
console.log('Validating sentence:', sentence);
|
17 |
-
|
18 |
-
const client = new Mistral({
|
19 |
-
apiKey: Deno.env.get('MISTRAL_API_KEY'),
|
20 |
-
});
|
21 |
-
|
22 |
-
const response = await client.chat.complete({
|
23 |
-
model: "mistral-large-latest",
|
24 |
-
messages: [
|
25 |
-
{
|
26 |
-
role: "system",
|
27 |
-
content: `You are an English language validator. Your task is to determine if the given sentence is grammatically correct and makes sense in English.
|
28 |
-
Respond with ONLY "true" or "false".`
|
29 |
-
},
|
30 |
-
{
|
31 |
-
role: "user",
|
32 |
-
content: `Is this a valid, grammatically correct English sentence that makes sense: "${sentence}"?`
|
33 |
-
}
|
34 |
-
],
|
35 |
-
maxTokens: 10,
|
36 |
-
temperature: 0.1
|
37 |
-
});
|
38 |
-
|
39 |
-
const isValid = response.choices[0].message.content.trim().toLowerCase() === 'true';
|
40 |
-
console.log('Sentence validation result:', isValid);
|
41 |
-
|
42 |
-
return new Response(
|
43 |
-
JSON.stringify({ isValid }),
|
44 |
-
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
45 |
-
);
|
46 |
-
} catch (error) {
|
47 |
-
console.error('Error validating sentence:', error);
|
48 |
-
return new Response(
|
49 |
-
JSON.stringify({ error: error.message }),
|
50 |
-
{
|
51 |
-
status: 500,
|
52 |
-
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
53 |
-
}
|
54 |
-
);
|
55 |
-
}
|
56 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|