aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/components/Game.tsx
blob: 1b07b897517183bcdd6e3f7540eb86d3a86a1b6a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { useEffect, useRef, useState } from "react";
import { fetchPokeApi } from "../api/api";
import css from "../css/Game.module.css";
import OptionsModel from "../models/OptionsModel";
import { PokeApiModel } from "../models/PokeApiModel";
import Card from "./Card";
import GameOver from "./GameOver";
import Victory from "./Victory";

interface GameProps {
	opts: OptionsModel,
	onReturnToOptions: () => void
}

const Game = ( {opts, onReturnToOptions } : GameProps) => {

	const [cards, setCards] = useState<PokeApiModel["results"][number][]>([]);
	const [reload, setReload] = useState(true);
	const [loadError, setLoadError] = useState(false);
	const [gameOver, setGameOver] = useState(false);
	const [victory, setVictory] = useState(false);

	const setRef = useRef<Set<number>>(new Set());
	const scoreRef = useRef<number>(0);

	useEffect(() => {
		async function getRandomCards() {
			try {
				setLoadError(false);
				setRef.current.clear();
				const data = await fetchPokeApi();
				const count = data.count - 1;
				const set: Set<PokeApiModel["results"][number]> = new Set();
				while (set.size < opts.difficulty) {
					const randIdx = Math.floor(Math.random() * count);
					const pokemon = data.results[randIdx];
					pokemon.index = randIdx + 1;
					set.add(pokemon);
				}
				const slides: PokeApiModel["results"] = Array.from(set);
				setCards(slides);
			} catch (err) {
				console.error(err);
				setLoadError(true);
			} finally {
				setReload(false);
			}
		}
		if (reload) {
			getRandomCards();
		}
	}, [opts.difficulty, reload, gameOver]);

	function handleCardClick(index: number) {
		if (setRef.current.has(index)) {
			setGameOver(true);
			return;
		}
		scoreRef.current++;
		if (scoreRef.current === opts.difficulty) {
			setVictory(true)
			return;
		}
		setRef.current.add(index);

		const newCards = [...cards];
		let currIdx = newCards.length;
		while (0 !== currIdx) {
			const randIdx = Math.floor(Math.random() * currIdx);
			currIdx -= 1;

			const tmp = newCards[currIdx];
			newCards[currIdx] = newCards[randIdx];
			newCards[randIdx] = tmp;
		}
		setCards(newCards);
	}

	return (
		<>
			{reload && <>Loading...</>}
			{loadError && <>An unknown error occurred. Please refresh.</>}
			{gameOver &&
				<GameOver
				onReplayClick = {() => {
					setGameOver(false);
					scoreRef.current = 0;
					setReload(true);
				}}
				score={scoreRef.current}
				maxScore={opts.difficulty} />
			}
			{victory &&
				<Victory
				maxScore={opts.difficulty}
				onReplayClick = {() => {
					setVictory(false);
					scoreRef.current = 0;
					setReload(true);
					onReturnToOptions();
				}}
				/>
			}
			{!reload && !loadError && !gameOver && !victory &&
				<div className={css.gameContainer}>
					{cards.map(card => (
						<Card
						key={card.index}
						index={card.index}
						cardName={card.name}
						imgUrl={"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/" + card.index + ".png"}
						onCardClick={() => {handleCardClick(card.index)}}
						/>
					))}
				</div>
			}
		</>
	);
}

export default Game;
(git 2.53.0) at 2026-05-15 08:28:30 +0000