import * as React from "react"; import { GameWasm, Letter as LetterData, MyResult, PlayedTile, PlayerAndScore, ScoreResult, Tray, TurnAction, TurnAdvanceResult } from "word_grid"; import { HighlightableLetterData, LocationType, matchCoordinate, mergeTrays, PlayableLetterData, Settings, TileDispatchAction, TileDispatchActionType } from "./utils"; import {useEffect, useMemo, useReducer, useRef, useState} from "react"; import {TileExchangeModal} from "./TileExchange"; import {Grid, Scores, TileTray} from "./UI"; function addLogInfo(existingLog: React.JSX.Element[], newItem: React.JSX.Element) { newItem = React.cloneElement(newItem, { key: existingLog.length }) existingLog.push(newItem); return existingLog.slice(); } export function Game(props: { wasm: GameWasm, settings: Settings, end_game_fn: () => void, }) { const cellTypes = useMemo(() => { return props.wasm.get_board_cell_types(); }, []); const [confirmedScorePoints, setConfirmedScorePoints] = useState(-1); function movePlayableLetters(playerLetters: PlayableLetterData[], update: TileDispatchAction) { if(update.action === TileDispatchActionType.RETRIEVE) { let tray: Tray = props.wasm.get_tray("Player"); return mergeTrays(playerLetters, tray.letters); } else if (update.action === TileDispatchActionType.MOVE) { let startIndex = matchCoordinate(playerLetters, update.start); let endIndex = matchCoordinate(playerLetters, update.end); if(startIndex != null) { let startLetter = playerLetters[startIndex]; startLetter.location = update.end.location; startLetter.index = update.end.index; } if(endIndex != null) { let endLetter = playerLetters[endIndex]; endLetter.location = update.start.location; endLetter.index = update.start.index; } setConfirmedScorePoints(-1); return playerLetters.slice(); } if (update.action === TileDispatchActionType.SET_BLANK) { const blankLetter = playerLetters[update.blankIndex]; if(blankLetter.text !== update.newBlankValue) { blankLetter.text = update.newBlankValue; if (blankLetter.location == LocationType.GRID) { setConfirmedScorePoints(-1); } } return playerLetters.slice(); } else if (update.action === TileDispatchActionType.RETURN) { return mergeTrays(playerLetters, playerLetters); } else { console.error("Unknown tray update"); console.error({update}); } } function exchangeFunction(selectedArray: Array) { const result: MyResult = props.wasm.exchange_tiles(selectedArray); console.log({result}); if(result.response_type === "ERR") { logDispatch(
{(result.value as string)}
); } else { const action = result.value as {type: "ExchangeTiles"; tiles_exchanged: number;}; logDispatch(
You exchanged {action.tiles_exchanged} tiles.
); setTurnCount(turnCount + 1); } } function addWordFn(word: string) { props.wasm.add_word(word); logDispatch(
{word} was added to dictionary.
); } function runAI() { const result: TurnAdvanceResult = props.wasm.advance_turn(); console.info({result}); setTurnCount(turnCount + 1); } const [playerLetters, trayDispatch] = useReducer(movePlayableLetters, []); const [logInfo, logDispatch] = useReducer(addLogInfo, []); const [turnCount, setTurnCount] = useState(1); const playerAndScores: PlayerAndScore[] = useMemo(() => { return props.wasm.get_scores(); }, [turnCount]); const [boardLetters, setBoardLetters] = useState(() => { const newLetterData = [] as HighlightableLetterData[]; for(let i=0; i<15*15; i++) { newLetterData.push(undefined); } return newLetterData; }); useEffect(() => { const newLetterData = props.wasm.get_board_letters() as LetterData[]; // we need to go through and set 'highlight' field to either true or false // it will always be false if the player that just went was the AI // TODO - build in support for multiple other players for(let i=0; i { return props.wasm.get_current_player(); }, [turnCount]); useEffect(() => { logDispatch(

Turn {turnCount}

); logDispatch(
{playerTurnName}'s turn
) setConfirmedScorePoints(-1); trayDispatch({action: TileDispatchActionType.RETRIEVE}); if(playerTurnName != props.settings.playerName) { runAI(); } }, [turnCount]); const logDivRef = useRef(null); const [isTileExchangeOpen, setIsTileExchangeOpen] = useState(false); useEffect(() => { // Effect is to keep the log window scrolled down if (logDivRef.current != null) { logDivRef.current.scrollTo(0, logDivRef.current.scrollHeight); // scroll down } }, [logInfo]) return <>
{logInfo}
; } function AddWordButton(props: {word: string, addWordFn: (x: string) => void}) { const [isClicked, setIsClicked] = useState(false); return }