2023-06-27 04:29:44 +02:00
|
|
|
import React, { useEffect, useState, useRef } from "react";
|
|
|
|
|
2022-04-25 01:51:30 +02:00
|
|
|
import { Box, Paper, Typography } from "@mui/material";
|
2023-06-12 06:34:20 +02:00
|
|
|
import { AugmentationName } from "@enums";
|
2022-10-10 00:42:14 +02:00
|
|
|
import { Player } from "@player";
|
2022-03-23 15:31:56 +01:00
|
|
|
import { Settings } from "../../Settings/Settings";
|
2022-04-25 01:51:30 +02:00
|
|
|
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";
|
2021-06-13 17:05:40 +02:00
|
|
|
|
|
|
|
interface Difficulty {
|
2021-09-05 01:09:30 +02:00
|
|
|
[key: string]: number;
|
|
|
|
timer: number;
|
|
|
|
wiresmin: number;
|
|
|
|
wiresmax: number;
|
|
|
|
rules: number;
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const difficulties: {
|
2021-09-05 01:09:30 +02:00
|
|
|
Trivial: Difficulty;
|
|
|
|
Normal: Difficulty;
|
|
|
|
Hard: Difficulty;
|
|
|
|
Impossible: Difficulty;
|
2021-06-13 17:05:40 +02:00
|
|
|
} = {
|
2021-09-05 01:09:30 +02:00
|
|
|
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 },
|
|
|
|
};
|
2021-06-13 17:05:40 +02:00
|
|
|
|
2022-03-23 15:31:56 +01:00
|
|
|
const types = [KEY.PIPE, KEY.DOT, KEY.FORWARD_SLASH, KEY.HYPHEN, "█", KEY.HASH];
|
2021-06-13 17:05:40 +02:00
|
|
|
|
2021-09-05 01:09:30 +02:00
|
|
|
const colors = ["red", "#FFC107", "blue", "white"];
|
2021-06-13 17:05:40 +02:00
|
|
|
|
2022-07-15 07:51:30 +02:00
|
|
|
const colorNames: Record<string, string> = {
|
2021-09-05 01:09:30 +02:00
|
|
|
red: "red",
|
|
|
|
"#FFC107": "yellow",
|
|
|
|
blue: "blue",
|
|
|
|
white: "white",
|
|
|
|
};
|
2021-06-13 17:05:40 +02:00
|
|
|
|
|
|
|
interface Wire {
|
2021-09-05 01:09:30 +02:00
|
|
|
tpe: string;
|
|
|
|
colors: string[];
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
interface Question {
|
2021-09-05 01:09:30 +02:00
|
|
|
toString: () => string;
|
|
|
|
shouldCut: (wire: Wire, index: number) => boolean;
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
2023-06-27 04:29:44 +02:00
|
|
|
export function WireCuttingGame({ onSuccess, onFailure, ...otherProps }: IMinigameProps): React.ReactElement {
|
2021-09-05 01:09:30 +02:00
|
|
|
const difficulty: Difficulty = {
|
|
|
|
timer: 0,
|
|
|
|
wiresmin: 0,
|
|
|
|
wiresmax: 0,
|
|
|
|
rules: 0,
|
|
|
|
};
|
2023-06-27 04:29:44 +02:00
|
|
|
interpolate(difficulties, otherProps.difficulty, difficulty);
|
2022-03-24 15:26:24 +01:00
|
|
|
const timer = difficulty.timer;
|
2023-06-27 04:29:44 +02:00
|
|
|
const wiresRef = useRef(generateWires(difficulty));
|
|
|
|
const questionsRef = useRef(generateQuestion(wiresRef.current, difficulty));
|
2022-03-23 15:31:56 +01:00
|
|
|
|
2023-06-27 04:29:44 +02:00
|
|
|
const [cutWires, setCutWires] = useState(new Array(wiresRef.current.length).fill(false));
|
|
|
|
const hasAugment = Player.hasAugmentation(AugmentationName.KnowledgeOfApollo, true);
|
2021-09-05 01:09:30 +02:00
|
|
|
|
2023-06-27 04:29:44 +02:00
|
|
|
// TODO: refactor, move the code from this effect to a `press` function
|
2022-03-26 03:50:28 +01:00
|
|
|
useEffect(() => {
|
|
|
|
// check if we won
|
|
|
|
const wiresToBeCut = [];
|
2023-06-27 04:29:44 +02:00
|
|
|
for (let j = 0; j < wiresRef.current.length; j++) {
|
2022-03-26 03:50:28 +01:00
|
|
|
let shouldBeCut = false;
|
2023-06-27 04:29:44 +02:00
|
|
|
for (let i = 0; i < questionsRef.current.length; i++) {
|
|
|
|
shouldBeCut = shouldBeCut || questionsRef.current[i].shouldCut(wiresRef.current[j], j);
|
2022-03-26 03:50:28 +01:00
|
|
|
}
|
|
|
|
wiresToBeCut.push(shouldBeCut);
|
|
|
|
}
|
|
|
|
if (wiresToBeCut.every((b, i) => b === cutWires[i])) {
|
2023-06-27 04:29:44 +02:00
|
|
|
onSuccess();
|
2022-03-26 03:50:28 +01:00
|
|
|
}
|
2023-06-27 04:29:44 +02:00
|
|
|
}, [cutWires, onSuccess]);
|
|
|
|
|
|
|
|
function checkWire(wireNum: number): boolean {
|
|
|
|
return questionsRef.current.some((q) => q.shouldCut(wiresRef.current[wireNum - 1], wireNum - 1));
|
|
|
|
}
|
2022-03-26 03:50:28 +01:00
|
|
|
|
2021-09-25 04:15:19 +02:00
|
|
|
function press(this: Document, event: KeyboardEvent): void {
|
2021-09-05 01:09:30 +02:00
|
|
|
event.preventDefault();
|
|
|
|
const wireNum = parseInt(event.key);
|
|
|
|
|
2023-06-27 04:29:44 +02:00
|
|
|
if (wireNum < 1 || wireNum > wiresRef.current.length || isNaN(wireNum)) return;
|
2021-09-05 01:09:30 +02:00
|
|
|
setCutWires((old) => {
|
|
|
|
const next = [...old];
|
|
|
|
next[wireNum - 1] = true;
|
2022-03-23 15:31:56 +01:00
|
|
|
if (!checkWire(wireNum)) {
|
2023-06-27 04:29:44 +02:00
|
|
|
onFailure();
|
2021-09-05 01:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return next;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2022-04-24 23:50:19 +02:00
|
|
|
<>
|
2023-06-27 04:29:44 +02:00
|
|
|
<GameTimer millis={timer} onExpire={onFailure} />
|
2022-04-24 23:50:19 +02:00
|
|
|
<Paper sx={{ display: "grid", justifyItems: "center", pb: 1 }}>
|
|
|
|
<Typography variant="h4" sx={{ width: "75%", textAlign: "center" }}>
|
|
|
|
Cut the wires with the following properties! (keyboard 1 to 9)
|
|
|
|
</Typography>
|
2023-06-27 04:29:44 +02:00
|
|
|
{questionsRef.current.map((question, i) => (
|
2021-10-01 19:36:59 +02:00
|
|
|
<Typography key={i}>{question.toString()}</Typography>
|
2021-09-05 01:09:30 +02:00
|
|
|
))}
|
2022-04-24 23:50:19 +02:00
|
|
|
<Box
|
|
|
|
sx={{
|
|
|
|
display: "grid",
|
2023-06-27 04:29:44 +02:00
|
|
|
gridTemplateColumns: `repeat(${wiresRef.current.length}, 1fr)`,
|
2022-04-24 23:50:19 +02:00
|
|
|
columnGap: 3,
|
|
|
|
justifyItems: "center",
|
|
|
|
}}
|
|
|
|
>
|
2023-06-27 04:29:44 +02:00
|
|
|
{Array.from({ length: wiresRef.current.length }).map((_, i) => {
|
2022-04-22 21:30:49 +02:00
|
|
|
const isCorrectWire = checkWire(i + 1);
|
|
|
|
const color = hasAugment && !isCorrectWire ? Settings.theme.disabled : Settings.theme.primary;
|
|
|
|
return (
|
2022-04-24 23:50:19 +02:00
|
|
|
<Typography key={i} style={{ color: color }}>
|
|
|
|
{i + 1}
|
|
|
|
</Typography>
|
2022-04-22 21:30:49 +02:00
|
|
|
);
|
|
|
|
})}
|
2022-04-24 23:50:19 +02:00
|
|
|
{new Array(8).fill(0).map((_, i) => (
|
|
|
|
<React.Fragment key={i}>
|
2023-06-27 04:29:44 +02:00
|
|
|
{wiresRef.current.map((wire, j) => {
|
2022-03-23 15:31:56 +01:00
|
|
|
if ((i === 3 || i === 4) && cutWires[j]) {
|
2022-04-24 23:50:19 +02:00
|
|
|
return <Typography key={j}></Typography>;
|
2022-03-23 15:31:56 +01:00
|
|
|
}
|
2022-03-25 16:39:14 +01:00
|
|
|
const isCorrectWire = checkWire(j + 1);
|
2022-03-23 15:31:56 +01:00
|
|
|
const wireColor =
|
|
|
|
hasAugment && !isCorrectWire ? Settings.theme.disabled : wire.colors[i % wire.colors.length];
|
2021-09-05 01:09:30 +02:00
|
|
|
return (
|
2022-04-24 23:50:19 +02:00
|
|
|
<Typography key={j} style={{ color: wireColor }}>
|
|
|
|
|{wire.tpe}|
|
|
|
|
</Typography>
|
2021-09-05 01:09:30 +02:00
|
|
|
);
|
|
|
|
})}
|
2022-04-24 23:50:19 +02:00
|
|
|
</React.Fragment>
|
|
|
|
))}
|
|
|
|
</Box>
|
2023-06-27 04:29:44 +02:00
|
|
|
<KeyHandler onKeyDown={press} onFailure={onFailure} />
|
2022-04-24 23:50:19 +02:00
|
|
|
</Paper>
|
|
|
|
</>
|
2021-09-05 01:09:30 +02:00
|
|
|
);
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function randomPositionQuestion(wires: Wire[]): Question {
|
2021-09-05 01:09:30 +02:00
|
|
|
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;
|
|
|
|
},
|
|
|
|
};
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function randomColorQuestion(wires: Wire[]): Question {
|
2021-09-05 01:09:30 +02:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
};
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function generateQuestion(wires: Wire[], difficulty: Difficulty): Question[] {
|
2021-09-05 01:09:30 +02:00
|
|
|
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;
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function generateWires(difficulty: Difficulty): Wire[] {
|
2021-09-05 01:09:30 +02:00
|
|
|
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)]);
|
2021-06-13 17:05:40 +02:00
|
|
|
}
|
2021-09-05 01:09:30 +02:00
|
|
|
wires.push({
|
|
|
|
tpe: types[Math.floor(Math.random() * types.length)],
|
|
|
|
colors: wireColors,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return wires;
|
|
|
|
}
|