From 6dbd6e965439a5cce29bccc27cbb9d88afc984f7 Mon Sep 17 00:00:00 2001 From: sanine-a Date: Fri, 16 Apr 2021 13:07:17 -0500 Subject: add Game component and reset() --- App.js | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 145 insertions(+), 22 deletions(-) diff --git a/App.js b/App.js index 7b7a22c..d72edf4 100644 --- a/App.js +++ b/App.js @@ -31,14 +31,17 @@ const setState = update => { */ // IMPURE -const App = ({state, setState}) => { - const { guessInput, } = state; - - return h(GuessInput, { - value: guessInput.value, - isShaking: guessInput.isShaking, - handleInput: updateStateOnEvent(state, handleGuessInputInput), - handleKeyDown: updateStateOnEvent(state, handleGuessInputKeyDown), +const App = ({state}) => { + const { currentLevel, guessInput, } = state; + + if (guessInput.isShaking) + // side-effect!! + setTimeout(() => setState({ guessInput: { isShaking: false } }), 300); + + return h(Game, { + state, + currentLevel, + guessInput, }); }; @@ -50,8 +53,29 @@ const App = ({state, setState}) => { * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +const Game = ({ state, currentLevel, guessInput }) => { + const { characters, index, running } = currentLevel; + + if (!running) + return h('p', {}, 'Finished'); + else { + const [ character ] = characters[index]; + + return h('div', {}, [ + h('p', {}, character), + h(GuessInput, { + value: guessInput.value, + isShaking: guessInput.isShaking, + handleInput: updateStateOnEvent(state, handleGuessInputInput), + handleKeyDown: updateStateOnEvent(state, handleGuessInputKeyDown), + }), + ]); + } +}; + + const GuessInput = ({value, isShaking, handleInput, handleKeyDown}) => { - const inputTag = isShaking ? 'input.shaking' : 'input'; + const inputTag = isShaking ? 'input.shake' : 'input'; return h(inputTag, { type: 'text', @@ -74,39 +98,138 @@ const updateStateOnEvent = (state, handler) => event => setState(handler(state, event)); -const handleGuessInputInput = (state, event) => - ({ guessInput: { value: event.target.value } }); +const handleGuessInputInput = (state, event) => ( + { guessInput: { value: event.target.value } } +); const handleGuessInputKeyDown = (state, event) => { const KEYCODE_ENTER = 13; + const { guessInput, currentLevel } = state; + + const guessValue = guessInput.value; + const { characters, index, correctGuesses, totalGuesses } = currentLevel; + const [ _, pinyin ] = characters[index]; + if (event.keyCode === KEYCODE_ENTER) { - console.log(state.guessInput.value); - return { guessInput: { value: '' } }; + if (guessValue === pinyin) + return { + guessInput: { + value: '', + }, + currentLevel: { + index: index + 1, + correctGuesses: correctGuesses + 1, + totalGuesses: totalGuesses + 1, + running: (index + 1) < characters.length, + } + }; + else + return { + guessInput: { + isShaking: true, + }, + currentLevel: { + totalGuesses: totalGuesses + 1, + } + }; } - - return {}; + else + // enter wasn't pressed, no update + return {}; }; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - * Document load + * Document load and game reset * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -// IMPURE -window.onload = () => { - const initialState = { +const availableGameModes = (level, levelSize) => { + const clampNonNeg = value => value < 0 ? 0 : value; + + const startLevels = [...new Set([ + clampNonNeg(level), + clampNonNeg(level-3), + clampNonNeg(level-7), + clampNonNeg(level-12), + clampNonNeg(0), + ])]; + + const modeName = range => { + const [start, end] = range; + + if (start === end) + return `Characters from level ${start+1}`; + else if (start === 0) + return `Characters from levels 1-${end+1}`; + else + return `Characters from levels ${start+1}-${end+1}`; + } + + const mode = startLevel => { + const levelRange = [startLevel, level]; + const range = levelRange.map((x, i) => levelSize * (x+i)); + + return { + name: modeName(levelRange), + range, + }; + }; + + const modes = startLevels.map(mode); + return modes; +}; + + +const reset = (state, update) => { + const newLevel = 0; + const gameModes = availableGameModes(newLevel, 20); + const newGameMode = 0; + + // new level characters + const shuffled = array => { + const shuffle = [...array]; + for (let i=shuffle.length-1; i>1; i--) { + const j = Math.floor(Math.random() * (i+1)); + [shuffle[i], shuffle[j]] = [shuffle[j], shuffle[i]]; + } + return shuffle; + }; + + const levelCharacters = gameMode => { + const [start, end] = gameMode.range; + return shuffled(characters.slice(start, end)); + }; + + return { + gameParams: { + level: newLevel, + gameModes, + gameMode: newGameMode, + }, + currentLevel: { + characters: levelCharacters(gameModes[newGameMode]), + index: 0, + correctGuesses: 0, + totalGuesses: 0, + running: true, + }, guessInput: { value: '', isShaking: false, - }, + } }; - +}; + + +// IMPURE +window.onload = () => { + const initialState = reset(); setState(initialState); -} +}; -- cgit v1.2.1