Challenge mode working; more bugs squashed

This commit is contained in:
Gene Mecija 2020-01-21 22:58:35 -08:00
parent 43255cf792
commit 69b910bf5e
17 changed files with 395 additions and 148 deletions

View file

@ -1,76 +1,69 @@
import React, {useContext, useState} from 'react';
import React, {useContext} from 'react';
import './css/App.css';
import MorseButtons from './components/MorseButtons'
import { GameModeContext } from "./contexts/gameModeContext"
import { KeyTypeContext } from "./contexts/keyTypeContext"
import { MorseBufferContextProvider } from "./contexts/morseBufferContext"
import { WordFeederContextProvider } from './contexts/wordFeederContext';
import { WordListPickerContextProvider } from './contexts/wordListPickerContext';
import ModePicker from './components/ModePicker'
import KeyTypePicker from './components/KeyTypePicker'
// import MorseDisplay from './components/MorseDisplay'
// import MorseBufferDisplay from './components/MorseBufferDisplay'
import WordListPicker from './components/WordListPicker';
import Legend from './components/Legend';
// import GameClock from "./components/GameClock"
// import ChallengeWord from "./components/ChallengeWord"
import {GameModeContext} from "./contexts/gameContext"
import {KeyTypeContext} from "./contexts/keyTypeContext"
import {MorseBufferContextProvider} from "./contexts/morseBufferContext"
import MorseButtons from './components/MorseButtons'
import PracticeMode from './app-modes/PracticeMode';
import TimedMode from './app-modes/TimedMode'
import ChallengeMode from './app-modes/ChallengeMode'
import Legend from './components/Legend';
import MorseBufferDisplay from './components/MorseBufferDisplay'
import MorseHistory from './components/MorseHistory'
import StraightKey from './components/StraightKey';
import ElectronicKey from './components/ElectronicKey';
function App() {
export default React.memo(function App() {
console.log('App.js rendered')
const {gameMode} = useContext(GameModeContext)
const {keyType} = useContext(KeyTypeContext)
// const [keyType, setKeyType] = useState('straight')
// function handleClick(e) {
// setKeyType(e.target.id)
// console.log("Switched to " + e.target.id + " keyType.");
// }
let wordList = ['morse', 'code', 'hello', 'gene']
let wordIndex = 0
function getNextWord() {
let word = wordList[wordIndex]
wordIndex += 1
return word
}
return (
<div id='main-content'>
<Legend />
<ModePicker />
<MorseBufferContextProvider>
<KeyTypePicker />
{gameMode === 'practice' &&
<>
{keyType === "straight" ?
<StraightKey gameMode='practice' /> : <ElectronicKey gameMode='practice' />}
<PracticeMode /><br/>
<MorseBufferDisplay /><br/>
<MorseHistory /><br/>
</>
}
{/* {gameMode === 'timed' && <TimedMode />} */}
{gameMode === 'challenge' &&
<>
{keyType === "straight" ?
<StraightKey gameMode='challenge' /> : <ElectronicKey gameMode='challenge' />}
<ChallengeMode getNextWord={getNextWord} />
</>
}
<MorseButtons />
<ModePicker />
<KeyTypePicker />
{gameMode === 'practice' &&
<>
{keyType === "straight" ?
<StraightKey gameMode='practice' /> : <ElectronicKey gameMode='practice' />}
<PracticeMode /><br/>
<MorseBufferDisplay /><br/>
<MorseHistory /><br/>
</>
}
{/* {gameMode === 'timed' && <TimedMode />} */}
{gameMode === 'challenge' &&
<>
<WordListPickerContextProvider>
<WordFeederContextProvider>
<WordListPicker />
{keyType === "straight" ?
<StraightKey gameMode='challenge' /> : <ElectronicKey gameMode='challenge' />}
<ChallengeMode />
</WordFeederContextProvider>
</WordListPickerContextProvider>
</>
}
<MorseButtons />
</MorseBufferContextProvider>
</div>
);
}
export default React.memo(App);
})

View file

@ -4,19 +4,17 @@ import morseCode from '../data/morse-reverse.json'
import ChallengeWord from '../components/ChallengeWord'
import ChallengeBufferDisplay from '../components/ChallengeBufferDisplay';
import { MorseBufferContext } from '../contexts/morseBufferContext';
import { WordFeederContext } from '../contexts/wordFeederContext';
import { WordListPickerContext } from '../contexts/wordListPickerContext';
export default React.memo(function ChallengeMode(props) {
console.log("ChallengeMode loaded");
let wordList = ['morse', 'code', 'hello', 'gene']
// let word = wordList.shift()
// let word = props.word
let getNextWord = props.getNextWord
let word = getNextWord()
let challengeLetters = word.split('')
const {word, getNextWord} = useContext(WordFeederContext)
let challengeWordClass = ''
const {morseCharBuffer, setMorseCharBuffer} = useContext(MorseBufferContext)
let morseArray = morseCharBuffer.split('_').filter(l => l !== '')
@ -24,14 +22,22 @@ export default React.memo(function ChallengeMode(props) {
let correctCharIndexes = [] // Indexes of correct letters in Challenge Word
let incorrectCharIndex = null
let incorrectMorseIndexes = [] // Indexes of incorrect morse characters in morse character buffer
let offset = 0
if (!word) {
console.log('FINISHED ALL WORDS!')
alert('No more words.')
return
}
let challengeLetters = word.split('')
// Iterate through the morse character buffer and compare with each letter of challenge word
morseArray.forEach((item, index) => {
// if (morseCharBuffer.slice(-1) === '_') { // If end of morse character
let morseLetter = morseCode[morseArray[index]]
if (morseCharBuffer.slice(-1) === '_') { // If end of morse character
let morseLetter = morseCode[morseArray[index]] || '[?]'
let challengeLetter = challengeLetters[index-offset]
if (morseLetter === challengeLetter) {
@ -41,25 +47,44 @@ export default React.memo(function ChallengeMode(props) {
else {
incorrectCharIndex = index-offset
incorrectMorseIndexes.push(index)
if (incorrectMorseIndexes.length > 0) {
setMorseCharBuffer(prev => {
let newState = prev.split('_').filter(l => l !== '')
let x = newState.splice(incorrectMorseIndexes[0], 1)
newState = newState.join('_') + '_'
return newState
})
incorrectMorseIndexes.splice(1,incorrectMorseIndexes.length)
}
offset = incorrectMorseIndexes.length
}
// }
}
})
console.log('morseArray', morseArray);
function timeout(delay) {
new Promise((resolve) => {
setTimeout(() => {
resolve()
}, delay)
})
}
// Next word once all correct
if (correctCharIndexes.length === challengeLetters.length) {
word = wordList.shift()
setMorseCharBuffer('')
//
challengeWordClass = 'correct'
setTimeout(() => {
getNextWord()
setMorseCharBuffer('')
}, 1000)
}
return (
<>
<ChallengeWord word={word} correctCharIndexes={correctCharIndexes} incorrectCharIndex={incorrectCharIndex} />
<ChallengeWord className={challengeWordClass} word={word} correctCharIndexes={correctCharIndexes} incorrectCharIndex={incorrectCharIndex} />
<ChallengeBufferDisplay morseArray={morseArray} incorrectMorseIndexes={incorrectMorseIndexes} />
{/* <button onClick={() => console.log(morseCharBuffer)}>morseCharBuffer</button> */}
</>
);
)
});

View file

@ -1,52 +1,27 @@
import React, { useContext } from "react"
import DitDahDisplay from "./DitDahDisplay"
import React from "react"
import morseCode from '../data/morse-reverse.json'
// import { MorseBufferContext } from "../contexts/morseBufferContext"
function ChallengeBufferDisplay(props) {
// // INCREMENTING COUNTER TO MONITOR COMPONENT RELOADING
// // console.log('ChallengeMode')
// if (!document.getElementById('counter')) {
// let counter = document.createElement('h1')
// let holder = document.createElement('h3')
// counter.id = 'counter'
// holder.id = 'holder'
// counter.innerText = "0"
// document.querySelector('#main-content').appendChild(counter)
// document.querySelector('#main-content').appendChild(holder)
// } else {
// let num = document.getElementById('counter').innerText
// document.getElementById('counter').innerText = Number(num) + 1
// document.getElementById('holder').innerText = buffer
// }
// const {morseCharBuffer, setMorseCharBuffer} = useContext(MorseBufferContext)
export default React.memo(function ChallengeBufferDisplay(props) {
const morseArray = props.morseArray
let incorrectMorseIndexes = props.incorrectMorseIndexes
console.log('morseArray', morseArray);
let ditDahs = []
let alphanumeric = []
for (let i in morseArray) {
let morseChar = morseArray[i]
// Alphanumeric
let alpha = morseCode[morseChar] || '[?]'
let alphaClass = (incorrectMorseIndexes.includes(Number(i))) ? 'strike morseError' : ''
alphanumeric.push(<span className={alphaClass}>{morseCode[morseChar].toUpperCase()}</span>)
alphanumeric.push(<span key={i} className={alphaClass}>{alpha.toUpperCase()}</span>)
// DitDahs
let ditDahClass = (incorrectMorseIndexes.includes(Number(i))) ? 'morseError' : ''
ditDahs.push(<span className={ditDahClass}>{morseChar}</span>)
ditDahs.push(<span className='space'>&nbsp;</span>)
ditDahs.push(<span key={i} className={ditDahClass}>{morseChar}</span>)
ditDahs.push(<span key={i+100} className='space'>&nbsp;</span>)
}
// if (incorrectIndex) {
// setTimeout(() => {
// setMorseCharBuffer(prev => prev.slice(0,prev.length-2))
// }, 500)
// }
return (
<div id="challengeBufferDisplay">
@ -60,6 +35,4 @@ function ChallengeBufferDisplay(props) {
</div>
</div>
)
}
export default React.memo(ChallengeBufferDisplay)
})

View file

@ -2,6 +2,7 @@ import React from "react"
export default React.memo(function ChallengeWord(props) {
let challengeWordClass= props.className
const word = props.word
const correctCharIndexes = props.correctCharIndexes
const incorrectCharIndex = props.incorrectCharIndex
@ -24,6 +25,6 @@ export default React.memo(function ChallengeWord(props) {
})
return (
<div id="challengeWord">{spannedWord}</div>
<div id="challengeWord" className={challengeWordClass}>{spannedWord}</div>
)
})

View file

@ -1,11 +1,14 @@
import React, {useContext} from "react"
import {GameModeContext} from "../contexts/gameContext"
import {GameModeContext} from "../contexts/gameModeContext"
import { MorseBufferContext } from "../contexts/morseBufferContext"
function ModePicker() {
const {setGameMode} = useContext(GameModeContext)
const {setMorseCharBuffer} = useContext(MorseBufferContext)
function handleClick(e) {
setMorseCharBuffer('')
setGameMode(e.target.id)
console.log("Switched to " + e.target.id + " mode.");
}

View file

@ -0,0 +1,29 @@
import React, {useContext} from "react"
import { WordListPickerContext } from "../contexts/wordListPickerContext";
import { WordFeederContext } from "../contexts/wordFeederContext";
export default React.memo(function WordListPicker() {
const {setWordListCategory} = useContext(WordListPickerContext)
const {resetFeeder} = useContext(WordFeederContext)
function handleClick(e) {
resetFeeder()
setWordListCategory(e.target.id)
console.log("Switched to " + e.target.id + " mode.");
}
return (
<div id="wordListPicker" className="mode-picker">
<button id="common100" onClick={handleClick}>
100 Most Common Words
</button>
<button id="alphabet" onClick={handleClick}>
Alphabet
</button>
<button id="test" onClick={handleClick}>
Test List
</button>
</div>
)
})

View file

@ -0,0 +1,46 @@
import React, {useState, useContext} from "react"
import { WordListPickerContext } from "./wordListPickerContext"
const WordFeederContext = React.createContext()
function WordFeederContextProvider(props) {
// let wordList = ['hi', 'morse', 'code', 'hello', 'gene']
const {wordList} = useContext(WordListPickerContext)
const [wordIndex, setWordIndex] = useState(0)
const [order, setOrder] = useState('sequential')
let indexCache = []
let word = wordList[wordIndex]
function resetFeeder() {
setWordIndex(0)
indexCache = []
}
function getNextWord() {
if (order === 'sequential') {
setWordIndex(prev => prev + 1)
word = wordList[wordIndex] || null
} else if (order === 'random') {
let index = Math.floor(Math.random*wordList.length)
indexCache.push(index)
// Need to account for what to do when all words in wordList have been exhausted
while (indexCache.includes(index) && wordList.length !== indexCache.length) {
index = Math.floor(Math.random*wordList.length)
indexCache.push(index)
}
setWordIndex(index)
word = wordList[wordIndex]
}
}
return (
<WordFeederContext.Provider value={{word: word, getNextWord: getNextWord, resetFeeder: resetFeeder}}>
{props.children}
</WordFeederContext.Provider>
)
}
export {WordFeederContextProvider, WordFeederContext}

View file

@ -0,0 +1,30 @@
import React, {useState} from "react"
import alphabet from '../data/alphabet.json'
import common100 from '../data/common100.json'
const WordListPickerContext = React.createContext()
function WordListPickerContextProvider(props) {
const [wordListCategory, setWordListCategory] = useState('alphabet')
let wordList = []
const testList = ['gene', 'anya', 'ali', 'liam', 'last']
wordList = testList
if (wordListCategory === 'alphabet') {
wordList = alphabet.words
} else if (wordListCategory === 'common100') {
wordList = common100.words
} else if (wordListCategory === 'test') {
wordList = testList
}
return (
<WordListPickerContext.Provider value={{wordList: wordList, setWordListCategory: setWordListCategory}}>
{props.children}
</WordListPickerContext.Provider>
)
}
export {WordListPickerContextProvider, WordListPickerContext}

View file

@ -5,7 +5,7 @@ html, body {
width: 100%;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background: #FFF;
background: #444;
}
#root {
@ -159,6 +159,7 @@ html, body {
margin-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
border-radius: 5px;
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
@ -170,17 +171,24 @@ html, body {
text-transform: uppercase;
-ms-flex-item-align: center;
align-self: center;
-webkit-transition: all 300ms ease-in-out;
transition: all 300ms ease-in-out;
}
#challengeWord span {
padding: 4px;
margin: 1px;
-webkit-transition: background 100ms ease-in-out;
transition: background 100ms ease-in-out;
-webkit-transition: background 300ms ease-in-out;
transition: background 300ms ease-in-out;
}
#challengeWord.correct {
background: rgba(0, 200, 0, 0.7);
}
.strike {
text-decoration: line-through;
opacity: 30%;
}
#morseBufferDisplay, #challengeBufferDisplay {
@ -228,8 +236,8 @@ html, body {
#morseBufferDisplay #alphanumeric-container #alphanumeric span, #challengeBufferDisplay #alphanumeric-container #alphanumeric span {
padding: 4px;
-webkit-transition: background 100ms ease-in-out;
transition: background 100ms ease-in-out;
-webkit-transition: background 300ms ease-in-out;
transition: background 300ms ease-in-out;
}
#morseBufferDisplay #ditDahs, #challengeBufferDisplay #ditDahs {

File diff suppressed because one or more lines are too long

28
src/data/alphabet.json Normal file
View file

@ -0,0 +1,28 @@
{ "words": [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"x",
"y",
"z"
]
}

103
src/data/common100.json Normal file
View file

@ -0,0 +1,103 @@
{ "words": [
"the",
"be",
"to",
"of",
"and",
"a",
"in",
"that",
"have",
"I",
"it",
"for",
"not",
"on",
"with",
"he",
"as",
"you",
"do",
"at",
"this",
"but",
"his",
"by",
"from",
"they",
"we",
"say",
"her",
"she",
"or",
"an",
"will",
"my",
"one",
"all",
"would",
"there",
"their",
"what",
"so",
"up",
"out",
"if",
"about",
"who",
"get",
"which",
"go",
"me",
"when",
"make",
"can",
"like",
"time",
"no",
"just",
"him",
"know",
"take",
"person",
"into",
"year",
"your",
"good",
"some",
"could",
"them",
"see",
"other",
"than",
"then",
"now",
"look",
"only",
"come",
"its",
"over",
"think",
"also",
"back",
"after",
"use",
"two",
"how",
"our",
"work",
"first",
"well",
"way",
"even",
"new",
"want",
"because",
"any",
"these",
"give",
"day",
"most",
"us"
]
}

View file

@ -135,12 +135,19 @@ function useElectronicKey(gameMode) {
gapTimer = setInterval(() => {
gapTime += 1
if (gapTime >= wordGapMaxTime) {
// if (gapTime >= wordGapMaxTime) {
if (gameMode === 'practice' && gapTime >= wordGapMaxTime) {
setMorseCharBuffer(prev => prev + '/')
clearInterval(gapTimer)
gapTimer = 0
gapTime = 0
}
else if (gameMode === 'challenge' && gapTime >= letterGapMinTime) {
setMorseCharBuffer(prev => prev + '_')
clearInterval(gapTimer)
gapTimer = 0
gapTime = 0
}
}, timingUnit)
resolve();
@ -218,10 +225,10 @@ function useElectronicKey(gameMode) {
function handleInputStart(event) {
event.preventDefault()
if (event.keyCode === 71) {
queue = ['.',' ','.',' ','.',' ','.',' ','-','.','.','.','.','-']
executeQueue()
}
// if (event.keyCode === 71) {
// queue = ['.',' ','.',' ','.',' ','.',' ','-','.','.','.','.','-']
// executeQueue()
// }
paddlesReleasedSimultaneously = false
@ -292,14 +299,15 @@ function useElectronicKey(gameMode) {
depressSyncTime = 0
}
function checkGapBetweenInputs() {
console.log('ditMaxTime', ditMaxTime)
console.log('gapTime', gapTime);
console.log('letterGapMinTime', letterGapMinTime);
console.log('wordGapMaxTime', wordGapMaxTime);
// Check Gap between letters
if (gapTime >= letterGapMinTime && gapTime < wordGapMaxTime) {
// setMorseCharBuffer(prev => prev + ' ')
if (gameMode === 'practice') {
setMorseCharBuffer(prev => prev + ' ')
gapTime = 0
} else if (gameMode === 'challenge') {
setMorseCharBuffer(prev => prev + '_')
}
gapTime = 0
clearInterval(gapTimer)
gapTimer = 0
}
@ -336,7 +344,8 @@ function useElectronicKey(gameMode) {
useEffect(() => {
// PRACTICE MODE
if (morseCharBuffer.slice(-1) === '/') {
// if (morseCharBuffer.slice(-1) === '/') {
if (morseCharBuffer.slice(-1) === '/' && gameMode === 'practice') {
// Remove forward slash
let val = morseCharBuffer.slice(0,morseCharBuffer.length-1)

View file

@ -1,14 +1,11 @@
import {useEffect, useContext} from 'react'
import {MorseBufferContext} from '../contexts/morseBufferContext'
import config from '../config.json'
// import {GameModeContext} from '../contexts/gameContext'
// STRAIGHT KEY TELEGRAPH
function useStraightKey(gameMode) {
const {morseCharBuffer, setMorseCharBuffer, morseWords, setMorseWords} = useContext(MorseBufferContext)
// const {gameMode} = useContext(GameModeContext)
let charTimer = 0
let charTime = 0
@ -44,22 +41,19 @@ function useStraightKey(gameMode) {
function handleInputStart(event) {
event.preventDefault()
console.log("down", event.keyCode);
if (isRunning) {
return
} else {
isRunning = true
// TODO:
// Make sure only one touchdown event registered at a time
if ((event.keyCode !== 32 &&
event.target.id !== "morseButton" &&
event.target.id !== "left" &&
event.target.id !== "right") ||
(event.repeat)) {
return
}
event.target.className !== "paddle") ||
(event.repeat)) {
return
}
isRunning = true
if (context.state === 'interrupted') {
context.resume()
}
@ -73,7 +67,7 @@ function useStraightKey(gameMode) {
o.connect(g)
g.connect(context.destination)
o.start()
checkGapBetweenInputs()
clearInterval(gapTimer)
@ -91,17 +85,16 @@ function useStraightKey(gameMode) {
function handleInputEnd(event) {
event.preventDefault()
console.log("up", event.keyCode);
if (isRunning) {
if ((event.keyCode !== 32 &&
event.target.id !== "morseButton" &&
event.target.id !== "left" &&
event.target.id !== "right") ||
event.target.className !== "paddle") ||
(event.repeat)) {
return
}
isRunning = false
// console.log('charTime:', charTime);
if (charTime <= ditMaxTime) {
@ -131,7 +124,6 @@ function useStraightKey(gameMode) {
gapTimer = setInterval(() => {
gapTime += 1
console.log('useStraightKey-gameMode:',gameMode);
// Gap between words
if (gameMode === 'practice' && gapTime >= wordGapMaxTime) {
setMorseCharBuffer(prev => prev + '/')
@ -139,7 +131,7 @@ function useStraightKey(gameMode) {
gapTimer = 0
gapTime = 0
}
if (gameMode === 'challenge' && gapTime >= letterGapMinTime) {
else if (gameMode === 'challenge' && gapTime >= letterGapMinTime) {
setMorseCharBuffer(prev => prev + '_')
clearInterval(gapTimer)
gapTimer = 0
@ -150,10 +142,6 @@ function useStraightKey(gameMode) {
function checkGapBetweenInputs() {
// Check Gap between letters
// console.log('ditMaxTime', ditMaxTime)
// console.log('gapTime', gapTime);
// console.log('letterGapMinTime', letterGapMinTime);
// console.log('wordGapMaxTime', wordGapMaxTime);
if (gapTime >= letterGapMinTime && gapTime < wordGapMaxTime) {
if (gameMode === 'practice') {
setMorseCharBuffer(prev => prev + ' ')
@ -164,7 +152,7 @@ function useStraightKey(gameMode) {
gapTimer = 0
}
}
useEffect(() => {
document.addEventListener('keydown', handleInputStart)
document.addEventListener('keyup', handleInputEnd)

View file

@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {GameModeContextProvider} from "./contexts/gameContext"
import {GameModeContextProvider} from "./contexts/gameModeContext"
import { KeyTypeContextProvider } from './contexts/keyTypeContext';

View file

@ -10,8 +10,8 @@ html, body {
height: 100%;
width: 100%;
box-sizing: border-box;
background: $main-bg-color;
// background: #444
// background: $main-bg-color;
background: #444;
}
#root {
height: 100%;
@ -117,6 +117,7 @@ html, body {
margin-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
border-radius: 5px;
width: fit-content;
display: flex;
font-size: 40px;
@ -125,15 +126,25 @@ html, body {
background: #EEE;
text-transform: uppercase;
align-self: center;
transition: all 300ms ease-in-out;
span {
padding: 4px;
margin: 1px;
transition: background 100ms ease-in-out;
transition: background 300ms ease-in-out; //, opacity 100ms ease-in-out;
}
&.correct {
background: rgba(0,200,0,0.7);
}
}
// #challengeWord.green {
// background: green;
// }
.strike {
text-decoration: line-through;
opacity: 30%;
}
#morseBufferDisplay, #challengeBufferDisplay {
@ -168,7 +179,7 @@ html, body {
span {
padding: 4px;
transition: background 100ms ease-in-out;
transition: background 300ms ease-in-out;
}
}
}