import * as React from "react"; import {ChangeEvent, JSX, useState} from "react"; import { cellTypeToDetails, CoordinateData, GridArrowData, GridArrowDispatch, GridArrowDispatchActionType, HighlightableLetterData, LocationType, PlayableLetterData, TileDispatch, TileDispatchActionType, } from "./utils"; import {API, APIPlayer, CellType, Difficulty} from "./api"; import {GameWasm} from "./wasm"; import {Game} from "./Game"; export function TileSlot(props: { tile?: React.JSX.Element | undefined, location: CoordinateData, tileDispatch: TileDispatch, arrowDispatch?: GridArrowDispatch, }): React.JSX.Element { let isDraggable = props.tile !== undefined; function onDragStart(e: React.DragEvent) { e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("wordGrid/coords", JSON.stringify(props.location)); } function onDrop(e: React.DragEvent) { const startLocation: CoordinateData = JSON.parse(e.dataTransfer.getData("wordGrid/coords")); const thisLocation = props.location; props.tileDispatch({action: TileDispatchActionType.MOVE, start: startLocation, end: thisLocation}); } let className = "tileSpot"; if (props.location.location === LocationType.GRID) { className += " ephemeral"; } let onClick: () => void; if(props.arrowDispatch != null && props.location.location == LocationType.GRID) { onClick = () => { props.arrowDispatch({action: GridArrowDispatchActionType.CYCLE, position: props.location.index}); } } else if(props.location.location == LocationType.TRAY && props.tile != null && props.tile.props.data.text != ' ' && props.tile.props.data.text != '') { onClick = () => { props.tileDispatch({ action: TileDispatchActionType.MOVE_TO_ARROW, end: undefined, start: props.location, }); } } return
{e.preventDefault()}} > {props.tile}
} export function Letter(props: { data: HighlightableLetterData, letterUpdater?: (value: string) => void }): React.JSX.Element { function modifyThisLetter(value:string){ props.letterUpdater(value); } function onBlankInput(e: ChangeEvent){ let value = e.target.value.toUpperCase(); if(value.length > 1){ value = value[value.length - 1]; } else if(value.length == 0){ modifyThisLetter(value); return; } // Now check that it's a letter let is_letter = value.match("[A-Z]") != null; if(is_letter){ modifyThisLetter(value); } else { // Cancel and do nothing e.preventDefault(); } } if(props.data.is_blank && props.data.ephemeral) { return
{props.data.points}
} else { let className = "text"; if (props.data.is_blank) { // not ephemeral className += " prev-blank"; } let letterClassName = "letter"; if (props.data.highlight) { letterClassName += " highlight"; } return
{props.data.text}
{props.data.points}
} } export function TileTray(props: { letters: Array, trayLength: number, trayDispatch: TileDispatch }): React.JSX.Element { let elements: JSX.Element[] = []; for (let i=0; i ); } props.letters .forEach((letter, i) => { if (letter != null && letter.location === LocationType.TRAY) { elements[letter.index] = { props.trayDispatch({action: TileDispatchActionType.SET_BLANK, blankIndex: i, newBlankValue: value}) }} />} key={"letter_tray" + letter.index} location={{location: LocationType.TRAY, index: letter.index}} tileDispatch={props.trayDispatch} /> } }); return (
{elements}
) } export function Arrow(props: { data: GridArrowData, dispatch: GridArrowDispatch, }) { return
; } export function Grid(props: { cellTypes: CellType[], playerLetters: Array, boardLetters: HighlightableLetterData[], tileDispatch: TileDispatch, arrow: GridArrowData, arrowDispatch: GridArrowDispatch, }) { const elements = props.cellTypes.map((ct, i) => { const {className, text} = cellTypeToDetails(ct); let tileElement: JSX.Element; if (props.boardLetters[i] !== undefined) { tileElement = ; } else { tileElement = <> {text} ; } let arrowElement: JSX.Element; if(props.arrow != null && props.arrow.position == i) { arrowElement = ; } return
{tileElement} {arrowElement}
; }); props.playerLetters .forEach((letter, i) => { if (letter != null && letter.location === LocationType.GRID) { const ct = props.cellTypes[letter.index]; const {className} = cellTypeToDetails(ct); elements[letter.index] =
{ props.tileDispatch({action: TileDispatchActionType.SET_BLANK, blankIndex: i, newBlankValue: value}); }} />} location={{location: LocationType.GRID, index: letter.index}} tileDispatch={props.tileDispatch} />
; } }); return
{elements}
} export function Scores(props: {playerScores: Array}){ let elements = props.playerScores.map((ps) => { return

{ps.name}

{ps.score}
; }); return
{elements}
} export function AISelection(props: { aiRandomness: number, setAIRandomness: (x: number) => void, proportionDictionary: number, setProportionDictionary: (x: number) => void, }) { // Can change log scale to control shape of curve using following equation: // aiRandomness = log(1 + x*(n-1))/log(n) when x, the user input, ranges between 0 and 1 const logBase: number = 10000; const processedAIRandomness = Math.log(1 + (logBase - 1)*props.aiRandomness/100) / Math.log(logBase); //const processedProportionDictionary = 1.0 - props.proportionDictionary / 100; return <>
{ props.setProportionDictionary(e.currentTarget.valueAsNumber); }} min={1} max={100}/> { props.setAIRandomness(e.currentTarget.valueAsNumber); }} min={0} max={100}/>
  • "AI's proportion of dictionary" controls what percent of the total AI dictionary the AI can form words with. At 100%, it has access to its entire dictionary - although this dictionary is still less than what the player has access to.
  • "Level of randomness in AI" controls the degree to which the AI picks the optimal move for each of its turns. At 0, it always picks the highest scoring move it can do using the dictionary it has access to. At 1, it picks from its available moves at random.
    Note that "Level of randomness in AI" is now mapped on a log scale. Your current setting is equivalent to {(100*processedAIRandomness).toFixed(1)}% on the previous scale.
}