import React, { useEffect, useState } from "react"; import { Box, Paper, Typography } from "@mui/material"; import { AugmentationName } from "@enums"; import { Player } from "@player"; import { Settings } from "../../Settings/Settings"; import { KEY } from "../../utils/helpers/keyCodes"; import { random } from "../utils"; import { interpolate } from "./Difficulty"; import { GameTimer } from "./GameTimer"; import { IMinigameProps } from "./IMinigameProps"; import { KeyHandler } from "./KeyHandler"; import { isPositiveInteger } from "../../types"; interface Difficulty { [key: string]: number; timer: number; wiresmin: number; wiresmax: number; rules: number; } const difficulties: { Trivial: Difficulty; Normal: Difficulty; Hard: Difficulty; Impossible: Difficulty; } = { Trivial: { timer: 9000, wiresmin: 4, wiresmax: 4, rules: 2 }, Normal: { timer: 7000, wiresmin: 6, wiresmax: 6, rules: 2 }, Hard: { timer: 5000, wiresmin: 8, wiresmax: 8, rules: 3 }, Impossible: { timer: 4000, wiresmin: 9, wiresmax: 9, rules: 4 }, }; const types = [KEY.PIPE, KEY.DOT, KEY.FORWARD_SLASH, KEY.HYPHEN, "█", KEY.HASH]; const colors = ["red", "#FFC107", "blue", "white"]; const colorNames: Record = { red: "red", "#FFC107": "yellow", blue: "blue", white: "white", }; interface Wire { wireType: string; colors: string[]; } interface Question { toString: () => string; shouldCut: (wire: Wire, index: number) => boolean; } export function WireCuttingGame({ onSuccess, onFailure, difficulty }: IMinigameProps): React.ReactElement { const [questions, setQuestions] = useState([]); const [wires, setWires] = useState([]); const [timer, setTimer] = useState(0); const [cutWires, setCutWires] = useState([]); const [wiresToCut, setWiresToCut] = useState(new Set()); const [hasAugment, setHasAugment] = useState(false); useEffect(() => { // Determine game difficulty const gameDifficulty: Difficulty = { timer: 0, wiresmin: 0, wiresmax: 0, rules: 0, }; interpolate(difficulties, difficulty, gameDifficulty); // Calculate initial game data const gameWires = generateWires(gameDifficulty); const gameQuestions = generateQuestion(gameWires, gameDifficulty); const gameWiresToCut = new Set(); gameWires.forEach((wire, index) => { for (const question of gameQuestions) { if (question.shouldCut(wire, index)) { gameWiresToCut.add(index); return; // go to next wire } } }); // Initialize the game state setTimer(gameDifficulty.timer); setWires(gameWires); setCutWires(gameWires.map((__) => false)); setQuestions(gameQuestions); setWiresToCut(gameWiresToCut); setHasAugment(Player.hasAugmentation(AugmentationName.KnowledgeOfApollo, true)); }, [difficulty]); function press(this: Document, event: KeyboardEvent): void { event.preventDefault(); const wireNum = parseInt(event.key); if (!isPositiveInteger(wireNum) || wireNum > wires.length) return; const wireIndex = wireNum - 1; if (cutWires[wireIndex]) return; // Check if game has been lost if (!wiresToCut.has(wireIndex)) return onFailure(); // Check if game has been won const newWiresToCut = new Set(wiresToCut); newWiresToCut.delete(wireIndex); if (newWiresToCut.size === 0) return onSuccess(); // Rerender with new state if game has not been won or lost yet const newCutWires = cutWires.map((old, i) => (i === wireIndex ? true : old)); setWiresToCut(newWiresToCut); setCutWires(newCutWires); } return ( <> Cut the wires with the following properties! (keyboard 1 to 9) {questions.map((question, i) => ( {question.toString()} ))} {Array.from({ length: wires.length }).map((_, i) => { const isCorrectWire = cutWires[i + 1] || wiresToCut.has(i + 1); const color = hasAugment && !isCorrectWire ? Settings.theme.disabled : Settings.theme.primary; return ( {i + 1} ); })} {new Array(8).fill(0).map((_, i) => ( {wires.map((wire, j) => { if ((i === 3 || i === 4) && cutWires[j]) { return ; } const isCorrectWire = cutWires[j + 1] || wiresToCut.has(j + 1); const wireColor = hasAugment && !isCorrectWire ? Settings.theme.disabled : wire.colors[i % wire.colors.length]; return ( |{wire.wireType}| ); })} ))} ); } function randomPositionQuestion(wires: Wire[]): Question { const index = Math.floor(Math.random() * wires.length); return { toString: (): string => { return `Cut wires number ${index + 1}.`; }, shouldCut: (wire: Wire, i: number): boolean => { return index === i; }, }; } function randomColorQuestion(wires: Wire[]): Question { const index = Math.floor(Math.random() * wires.length); const cutColor = wires[index].colors[0]; return { toString: (): string => { return `Cut all wires colored ${colorNames[cutColor]}.`; }, shouldCut: (wire: Wire): boolean => { return wire.colors.includes(cutColor); }, }; } function generateQuestion(wires: Wire[], difficulty: Difficulty): Question[] { const numQuestions = difficulty.rules; const questionGenerators = [randomPositionQuestion, randomColorQuestion]; const questions = []; for (let i = 0; i < numQuestions; i++) { questions.push(questionGenerators[i % 2](wires)); } return questions; } function generateWires(difficulty: Difficulty): Wire[] { const wires = []; const numWires = random(difficulty.wiresmin, difficulty.wiresmax); for (let i = 0; i < numWires; i++) { const wireColors = [colors[Math.floor(Math.random() * colors.length)]]; if (Math.random() < 0.15) { wireColors.push(colors[Math.floor(Math.random() * colors.length)]); } wires.push({ wireType: types[Math.floor(Math.random() * types.length)], colors: wireColors, }); } return wires; }