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 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|