refactor: Move Game logic to new component

This commit is contained in:
sigmasternchen 2024-09-20 22:05:04 +02:00
parent 641e476ad6
commit 749057040f
2 changed files with 115 additions and 94 deletions

View file

@ -1,111 +1,30 @@
import React, {useState} from "react";
import {Field} from "./Field";
import {calculateDifference} from "../logic/game-logic";
import {id, objectMap, zip} from "../utils";
import {Keyboard} from "./Keyboard";
import {CellState, sortCellStates} from "../model/CellState";
import {Toast} from "./Toast";
import React from "react";
import dictionary from "../data/dictionary.json"
import {newSFC32} from "../random/sfc32";
import {GameState} from "../model/GameState";
import {Game} from "./Game";
const validWordsRegex = /[A-Z]+/;
export const App = () => {
const [gameState, setGameState] = useState(GameState.Active);
const [pastGuesses, setPastGuesses] = useState([]);
const [currentGuess, setCurrentGuess] = useState("");
const [message, setMessage] = useState("");
const wordLength = 5;
const numberOfGuesses = 6;
const availableWords = dictionary
.filter(word => validWordsRegex.test(word))
.filter(word => word.length === wordLength);
const today = Date.now() / 1000 / 60 / 60 / 24;
const random = newSFC32(today);
const availableWords = dictionary
.filter(word => validWordsRegex.test(word))
.filter(word => word.length === wordLength);
const correct = availableWords[Math.floor(random() * availableWords.length)];
console.log(correct)
const fieldDataForPastGuesses = pastGuesses
.map(guess => guess.toUpperCase())
.map(guess => [
guess.split(""),
calculateDifference(correct.toUpperCase(), guess)
])
.map(([guess, difference]) => zip(guess, difference))
.map(guessWithDifference => guessWithDifference
.map(([content, state]) => ({
state: state,
content: content,
}))
);
const usedWithState = objectMap(
Object.groupBy(
fieldDataForPastGuesses
.flatMap(id)
.flatMap(id),
cell => cell.content
),
states => states
.map(state => state.state)
.toSorted(sortCellStates)
.at(-1)
);
const inputHandler = key => {
if (key === "ENTER") {
if (currentGuess.length === wordLength) {
if (availableWords.indexOf(currentGuess) === -1) {
setMessage("Not in word list.");
} else {
setPastGuesses(pastGuesses.concat([currentGuess]));
setCurrentGuess("");
if (currentGuess.toUpperCase() === correct.toUpperCase()) {
setGameState(GameState.Won);
} else {
if (pastGuesses.length === numberOfGuesses - 1) {
setGameState(GameState.Lost);
}
}
}
} else {
setMessage("Not enough letters.");
}
} else if (key === "BACK") {
if (currentGuess.length > 0) {
setCurrentGuess(currentGuess.substring(0, currentGuess.length - 1));
}
} else {
if (currentGuess.length < wordLength) {
setCurrentGuess(currentGuess + key);
}
}
};
const fieldData = fieldDataForPastGuesses
.concat([currentGuess
.split("")
.map(char => ({
state: CellState.Unknown,
content: char,
}))
])
return <div>
<Field
size={[wordLength, numberOfGuesses]}
fieldData={fieldData}
return <Game
wordLength={wordLength}
numberOfGuesses={numberOfGuesses}
availableWords={availableWords}
correctWord={correct}
/>
<Keyboard enabled={gameState === GameState.Active} used={usedWithState} onKey={inputHandler}/>
<Toast message={message}/>
<div className={"result won " + (gameState === GameState.Won ? "show" : "")}>You won!</div>
<div className={"result lost " + (gameState === GameState.Lost ? "show" : "")}>You lost!</div>
</div>
};

102
src/components/Game.jsx Normal file
View file

@ -0,0 +1,102 @@
import React, {useState} from "react";
import {GameState} from "../model/GameState";
import {calculateDifference} from "../logic/game-logic";
import {id, objectMap, zip} from "../utils";
import {CellState, sortCellStates} from "../model/CellState";
import {Field} from "./Field";
import {Keyboard} from "./Keyboard";
import {Toast} from "./Toast";
export const Game = ({wordLength, numberOfGuesses, correctWord, availableWords, reset}) => {
const [gameState, setGameState] = useState(GameState.Active);
const [pastGuesses, setPastGuesses] = useState([]);
const [currentGuess, setCurrentGuess] = useState("");
const [message, setMessage] = useState("");
const fieldDataForPastGuesses = pastGuesses
.map(guess => guess.toUpperCase())
.map(guess => [
guess.split(""),
calculateDifference(correctWord.toUpperCase(), guess)
])
.map(([guess, difference]) => zip(guess, difference))
.map(guessWithDifference => guessWithDifference
.map(([content, state]) => ({
state: state,
content: content,
}))
);
const usedWithState = objectMap(
Object.groupBy(
fieldDataForPastGuesses
.flatMap(id)
.flatMap(id),
cell => cell.content
),
states => states
.map(state => state.state)
.toSorted(sortCellStates)
.at(-1)
);
const inputHandler = key => {
if (key === "ENTER") {
if (currentGuess.length === wordLength) {
if (availableWords.indexOf(currentGuess) === -1) {
setMessage("Not in word list.");
} else {
setPastGuesses(pastGuesses.concat([currentGuess]));
setCurrentGuess("");
if (currentGuess.toUpperCase() === correctWord.toUpperCase()) {
setGameState(GameState.Won);
} else {
if (pastGuesses.length === numberOfGuesses - 1) {
setGameState(GameState.Lost);
}
}
}
} else {
setMessage("Not enough letters.");
}
} else if (key === "BACK") {
if (currentGuess.length > 0) {
setCurrentGuess(currentGuess.substring(0, currentGuess.length - 1));
}
} else {
if (currentGuess.length < wordLength) {
setCurrentGuess(currentGuess + key);
}
}
};
const resetHandler = () => {
setGameState(GameState.Active);
setPastGuesses([]);
setCurrentGuess("")
reset();
};
const fieldData = fieldDataForPastGuesses
.concat([currentGuess
.split("")
.map(char => ({
state: CellState.Unknown,
content: char,
}))
])
return <div>
<Field
size={[wordLength, numberOfGuesses]}
fieldData={fieldData}
/>
<Keyboard enabled={gameState === GameState.Active} used={usedWithState} onKey={inputHandler}/>
<Toast message={message}/>
<div className={"result won " + (gameState === GameState.Won ? "show" : "")}>You won!</div>
<div className={"result lost " + (gameState === GameState.Lost ? "show" : "")}>You lost!</div>
</div>
}