import React, { createContext, Dispatch, FunctionComponent, Reducer, useMemo, useReducer, } from 'react'; import twl06 = require('./twl06.json'); export interface State { currentWord: string; letters: string[]; score: number; view: 'GAME' | 'SETUP'; words: string[]; } const initialState: State = { currentWord: '', letters: [], score: 0, view: 'SETUP', words: [], }; export interface DeleteLetterAction { type: 'DELETE_LETTER'; } export interface EnterLetterAction { type: 'ENTER_LETTER'; payload: string; } export interface EnterWordAction { type: 'ENTER_WORD'; } export interface SetLettersAction { type: 'SET_LETTERS'; payload: string[]; } export interface ShuffleLettersAction { type: 'SHUFFLE_LETTERS'; } export interface SwitchViewAction { type: 'SWITCH_VIEW'; payload: State['view']; } type Action = DeleteLetterAction | EnterLetterAction | EnterWordAction | SetLettersAction | ShuffleLettersAction | SwitchViewAction; const shuffle = (letters: string[]): string[] => { const newLetters = letters.slice(0, 1); const choices = letters.slice(1); for (let i = 1; i < letters.length; i += 1) { const choice = Math.floor(Math.random() * choices.length); newLetters.push(choices.splice(choice, 1)[0]); } return newLetters; }; const canSubmitWord = (state: State): boolean => { const { currentWord, words } = state; return currentWord.length > 3 && !words.includes(currentWord) && currentWord.split('').includes(state.letters[0]) && twl06.includes(currentWord.toLowerCase()); }; const scoreWord = (word: string): number => (word.length === 4 ? 1 : word.length); const enterWordReducer: Reducer = (state) => { const valid = canSubmitWord(state); return { ...state, currentWord: '', score: state.score + (valid ? scoreWord(state.currentWord) : 0), words: valid ? [...state.words, state.currentWord] : state.words, }; }; const reducer: Reducer = (state, action) => { switch (action.type) { case 'DELETE_LETTER': return { ...state, currentWord: state.currentWord.slice(0, -1) }; case 'ENTER_LETTER': return { ...state, currentWord: `${state.currentWord}${action.payload}` }; case 'ENTER_WORD': return enterWordReducer(state, action); case 'SET_LETTERS': return action.payload.length === state.letters.length && action.payload.every((ch, i) => state.letters[i] === ch) ? state : { ...state, currentWord: '', letters: action.payload, score: 0, words: [], }; case 'SHUFFLE_LETTERS': return { ...state, letters: shuffle(state.letters) }; case 'SWITCH_VIEW': return { ...state, view: action.payload }; default: throw new Error(); } }; interface ContextValue { state: State; dispatch: Dispatch; } export const StoreContext = createContext({ state: initialState, dispatch: () => undefined, } as ContextValue); export const StoreProvider: FunctionComponent = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); const ctxVal = useMemo(() => ({ state, dispatch }), [state, dispatch]); return ( {children} ); };