Five indexes · Turn-based play · Explainable AI

Scrabble Engine
Laboratory

Build a playable Computer Science Capstone: compare five indexes, inspect every legal move, then compose an AI policy from probability, future setup, retaliation risk and board control.

YOUR RACK. THEIR MOVE.

Seven tiles. One board. How much trouble can one word create?

Do you take the biggest score now, reach for a triple-word square, keep a promising leave, or lock an anchor until the opponent has nothing comfortable to play? Build the position, ask five indexes, then challenge their answer. The fastest move is not always the smartest move.

① Find the score② Read the risk③ Deny the reply
1

Trie — follow prefixes

Each edge is a letter. Starting at an anchor, the solver follows only prefixes that still exist and prunes the rest immediately.

Exact word / prefix
O(L)
Build
O(ΣL)
Memory
O(number of nodes)
L = word length. Full board search is output-sensitive; O(L) is the lookup path, not the whole Scrabble solve.
2

Hash — jump to anagram buckets

Letters become a frequency signature, so LISTEN and SILENT open the same bucket. Rack subsets retrieve candidate groups before the board validates anchors and cross-words.

Build signature
O(L)
Average bucket lookup
O(1)
Rack subsets
O(2ᴿ)
R ≤ 7, so at most 127 non-empty subsets. O(1) covers one average hash lookup—not signature creation, subset generation, or placement validation.
CAPSTONE INTEGRATION

A Computer Science Capstone in one playable engine

Students integrate discrete mathematics, data structures, databases, probability, explainable AI, APIs and performance engineering into a complete game engine that can be demonstrated, measured and defended. The playable product becomes both the Capstone artifact and the evidence behind every design decision.

LEVELUndergraduate
CLASS TIME90–120 min
LAB MODEPairs + discussion

After this lesson, students can

  1. trace insertion and lookup in a Trie and a hash-based anagram index;
  2. derive the costs of prefix search, signature construction and subset enumeration;
  3. implement leave-one-out and bit-mask subset selection without duplicate multisets;
  4. explain why an O(1) hash lookup does not make the whole solver O(1);
  5. combine score, expected retaliation, future setup and anchor control into an explainable AI decision.
SUBJECT 01

Design and Analysis of Data Structures and Algorithms

Represent the lexicon, rack and board so impossible candidates disappear early; then derive the real cost of prefix traversal, recursion, subset generation and output-sensitive move enumeration.

TrieHash tableRecursionSubset algorithmsComplexityBenchmarking
01
TRIE INTERNALS

From dictionary words to a prefix machine

Nodes, edges and terminal flags

Insert CAT by starting at the root and following or creating C, A and T edges. Mark T terminal. Inserting CAR reuses C and A, creates R, then marks R terminal. A lookup succeeds only when every character edge exists and the final node is terminal; prefix search omits the terminal requirement.

INSERT(word):
  node = root
  for ch in word:
    node = node.child.get_or_create(ch)
  node.terminal = true

CONTAINS(word):
  follow every edge; return final.terminal

How board traversal differs

At an empty square, traversal branches only to child letters available in the rack and allowed by the cross-check mask. At an occupied square it follows exactly the fixed board letter without consuming the rack. The state therefore contains Trie node, board position, remaining multiset, placed tiles and whether an anchor was covered.

EXTEND(node, square, rack):
  if square is fixed: follow its one edge
  else for child in node.children:
    if rack has child AND crossMask allows child:
      consume tile; recurse
  emit only terminal states covering an anchor

Complexity: exact lookup is O(L). Move generation is output-sensitive and depends on anchors A, branching b, rack states and legal outputs Z—not simply O(L).

SUBJECT 02

Discrete Mathematics

Sets, multisets, equivalence relations, combinatorics and the fundamental theorem of arithmetic explain why anagrams can share one identity and why a seven-tile rack has a bounded but exponential family of choices.

SetsMultisetsEquivalence classesPrime factorizationCombinatoricsProof
02
DISCRETE MATH → HASH

Prime products, packed counts and actual hash tables

Prime-number anagram key

Assign a different prime to each letter: A=2, B=3, C=5, …, Z=101. Multiply the primes of a word. Fundamental uniqueness of prime factorization makes anagrams share one product: ABBA and BABA both equal 2²×3²=36, while another multiset has another exact integer product.

primeKey("LISTEN")
= p[L] × p[I] × p[S] × p[T] × p[E] × p[N]
= primeKey("SILENT")

This is excellent for teaching, but a fixed 64-bit product eventually overflows. Reducing modulo a table size also introduces collisions, so equality must still compare the original signature.

Production representation

The engine stores 26 letter counts. Because a Scrabble word needs only a small count per letter, two 4-bit counts are packed into each byte: 26 counts become a collision-free 13-byte key. FNV-1a 64-bit maps that key to an open-addressed table; linear probing resolves occupied slots, and all 13 bytes are compared before accepting a hit.

offset = 1469598103934665603
for byte in packedSignature:
  hash = (hash XOR byte) × 1099511628211
slot = hash AND (capacity − 1)
while occupied AND key != storedKey:
  slot = (slot + 1) AND (capacity − 1)

Average: O(1) probe at a controlled load factor. Worst case: O(M) if clustering forces a scan of the table.

How a blank enters the hash search

A blank has score zero but may represent any A–Z. It must not receive one fixed prime. For every subset containing a blank, the candidate generator branches over 26 substitutions, increments that letter count, packs the result, and inserts it into a set. Two blanks branch over letter pairs. Deduplication collapses substitutions that produce the same count vector. During final placement, the move records which rack position was blank so scoring remains zero even though the board displays the chosen letter.

?A…Zcount vector13-byte keydeduplicate
SUBSET SIGNATURE GENERATION · NUMBA · 100,000 RACKS × 7 REPEATS

Prime products are faster at subset identity

Median warm time in the same persistent worker. This measures subset generation only; full-word lookup still uses the packed database key.

RackPrimePackedSpeedup
AEINRST3.917 µs9.935 µs2.536×
AABBCDE2.742 µs6.770 µs2.470×
AAAAAAA1.008 µs5.657 µs5.612×
The first prime call paid about 125 ms for new Numba JIT compilation; a persistent worker amortizes that one-time cost. Blank expansion remains on the separate substitution path.
END-TO-END CANDIDATE RETRIEVAL · 3,000 × 7 REPEATS

Prime Product versus Packed Trie

All engines returned identical candidate sets. Times are warm, uncached medians including subset generation, index lookup, bucket expansion, deduplication and sorting. They do not include board placement.

RackWordsPrimeTriePrime faster
AEINRST343141.986 µs1,283.597 µs9.040×
AABBCDE4222.478 µs226.228 µs10.064×
AAAAAAA16.428 µs13.447 µs2.092×
Trade-off: Prime index startup took 1.49–1.83 s versus 3.7–4.0 ms to map the packed Trie. A persistent service amortizes startup. The Prime index covers 55,784 signatures of length 2–7 and its key/slot arrays occupy 970,560 bytes, excluding word buckets.
PAPER TABLE · IDENTICAL SOWPODS CANDIDATE SETS

Five-engine comparison

Warm uncached median latency in microseconds; 1,000 iterations × 7 repeats. Lower is better. Every row passed exact candidate-set equivalence before timing.

RackWordsPrime ProductCompact/Numba PackedPython Memory HashSQLite HashPacked Trie
AEINRST343145.223157.805528.645661.3961,258.651
AABBCDE4222.55628.167229.290287.744216.450
AAAAAAA16.49811.42620.36431.19513.226
Observed startup range (ms)SQLite 0.124–0.205 · Trie 2.972–3.677 · Memory 348.974–363.382 · Packed 932.876–966.611 · Prime 1,473.505–1,535.111
EnvironmentIntel Core Ultra 7 155H · 16C/22T · 95.3 GB RAM · Windows 11 Pro host · Python 3.12.13 · NumPy 2.4.6 · Numba 0.66.0 · Docker worker
ScopeRack-only candidate retrieval for 2–7 letter words; excludes blanks, board anchors, placement scoring, API and rendering. Prime/Packed use Numba; Trie/SQLite/Memory are readable Python implementations, so language and optimization level remain a threat to validity.
03
LEAVE-ONE-OUT

LEAVE-ONE-OUT

Leave-one-out starts with the complete rack, removes one position, records the remaining multiset, then repeats recursively. For BEGIN the first layer contains EGIN, BGIN, BEIN, BEGN and BEGI. Continuing produces every smaller subset. Sorting or frequency-count encoding is essential: equal letters removed from different positions must not create duplicate work.

LEAVE-ONE-OUT(tiles, seen):
  key ← canonical(tiles)
  if key in seen: return
  seen.add(key); HASH-LOOKUP(key)
  for each distinct position i:
    recurse(tiles without i, seen)

Analysis

  • At most 2ᴿ distinct position subsets exist.
  • Sorting each subset costs O(R log R); a fixed 26-counter costs O(R).
  • With R = 7, only 127 non-empty subsets exist: an exponential algorithm bounded by the game rule.
  • Memoization removes repeated states caused by duplicate letters.
04
SUBSET SELECTION

A bit mask makes every choice explicit

Number rack positions 0…R−1. Integer mask m selects position i when bit i is 1. Iterating from 1 to 2ᴿ−1 generates every non-empty position subset exactly once. Canonical signatures then merge subsets that differ only by exchanging identical tiles.

for mask = 1 .. (1 << R) - 1:
  counts = [0] × 26; blanks = 0
  for i = 0 .. R - 1:
    if mask & (1 << i):
      add rack[i] to counts or blanks
  signatures.add(counts, blanks)

Leave-one-out or bit mask?

MethodStrengthRisk
Leave-one-outShows recursion and memoizationDuplicate states without canonical keys
Bit maskCompact, iterative and parallelizablePosition duplicates before deduplication
Count-state DPWorks directly with a multisetMore complex indexing

Hash pipeline: subset → merge fixed board letters → O(L) signature → average O(1) bucket lookup → validate anchor, blank, premium and every cross-word.

SUBJECT 03

Database Design

After designing a key, students must decide how to persist it, group anagrams, guarantee integrity, and separate a build-time database from a read-optimized runtime index.

SchemaPrimary keyBLOBNormalizationIndex
05
DATABASE DESIGN

One signature, one row, many words

The source lexicon contains 267,752 normalized SOWPODS words but only 237,740 distinct signatures. Anagrams belong to the same equivalence class, so storing one row per word would repeat the key. The database instead stores one row per signature and a zero-byte-separated word bucket.

anagrams

ColumnTypeRole
signatureBLOB PK13-byte packed count vector
wordsBLOB NOT NULLNUL-separated anagram bucket
word_countINTEGERBucket cardinality and diagnostics
min_lengthINTEGEREarly length filtering

metadata

ColumnTypeExample
keyTEXT PKschema, source, words
valueTEXTwarin.scrabble.hash-lexicon.v1

SHA-256 values bind the binary index to the exact normalized input. A mismatched manifest is rejected rather than silently serving a different lexicon.

Why WITHOUT ROWID?

SQLite tables normally have a hidden integer rowid plus a separate primary-key index. Here the BLOB signature is already the identity and access path. WITHOUT ROWID stores rows in the primary-key B-tree, avoiding a redundant identity and an extra lookup.

Database versus runtime hash table

SQLite is durable, portable and easy to rebuild, but its B-tree lookup is O(log S), not a true hash lookup. At worker startup all rows are loaded into compact NumPy arrays and an open-addressed table. SQLite becomes the source of truth; the in-memory table becomes the hot read path.

Database design questions for students
  1. Should words be normalized to uppercase before or after deduplication? What invariant follows?
  2. When is a child table signature_words(signature, word) better than one packed bucket?
  3. Which transaction boundary makes a rebuilt index appear atomically?
  4. What metadata is required for reproducible benchmarks?
05A
ENGINE MODES

Five indexes, one legal-move evaluator

Every mode uses the same board, rack, lexicon and final PHP placement evaluator. Only candidate retrieval changes. One average O(1) probe does not make the complete solver O(1).

Packed Trie · PHP

A read-only binary prefix tree that prunes impossible prefixes at board anchors. Word lookup is O(L), and this mode never queries SQLite at runtime.

SQLite Hash · B-tree

A durable 13-byte letter-count signature maps to an anagram bucket. SQLite uses a primary-key B-tree: O(log S) for S signatures, then expands the bucket.

In-memory Hash · Python dict

Loads SQLite rows once into a persistent dictionary. Probes are O(1) on average; exact 13-byte key comparison handles collisions.

Compact/Numba Hash

The default fast path uses immutable NumPy keys, int32 open-addressing slots, and Numba-compiled subset generation and batch probes.

Prime-product Hash

A=2, B=3, … turns a word into an exact product. Prime numbers are like indivisible pigments: multiplication mixes letters while preserving how many times each pigment occurs. Order disappears—LISTEN and SILENT become one mathematical silhouette—yet unique factorization can recover the multiset. An abstract theorem becomes an elegant, executable index. Seven-tile racks use uint64/Numba; longer board words use exact Python integers.

Complete solve time also includes candidate-Trie construction for Hash modes, anchor traversal, cross-word validation, scoring, sorting and API overhead. In-memory and Prime initialize lazily, so report cold and warm runs separately.

SUBJECT 04

Statistics and Probability

The board is observable but the opponent rack and future draws are latent. Sampling without replacement, conditional probability and expected value turn incomplete information into quantities that can be checked and simulated.

HypergeometricConditional probabilityExpected valueVarianceSimulation
06
PROBABILITY-BASED AI

A strong move is more than its immediate score

The opponent rack is hidden. The engine never invents it. Instead it subtracts visible board tiles and our rack from the official distribution, then estimates whether a required reply tile occurs in a seven-tile sample without replacement.

At least one useful tileP(X ≥ 1) = 1 − C(N−K, d) / C(N, d)N = unseen tiles, K = matching tiles including blanks, d = opponent rack size up to 7.

Immediate score

Official score with premiums, cross-words and bingo bonus.

Retaliation risk

Risk EV = P(reply) × counter score
Tests S, ES, ED, ING and other hooks.

Waiting / setup

Setup EV = P(draw) × future score
Values a useful leave and a hook for our next turn.

Opening risk

Penalizes new anchors leading toward double- or triple-word squares.

SUBJECT 05

Artificial Intelligence and Machine Learning

A move becomes an explainable feature vector. Students compose or remove heuristic terms, audit the resulting policy, then use self-play data to learn and evaluate weights without hiding the decision process.

Heuristic searchFeature engineeringPolicySelf-playAblationModel evaluation
06A
PROBABILITY → AI POLICY

From a hidden rack to an explainable decision

Sampling without replacement

Tile draws are dependent: drawing one S changes the chance of drawing another S. A binomial model assumes independent trials and is therefore only an approximation. The hypergeometric distribution exactly counts size-d racks drawn from N unseen tiles containing K useful tiles.

P(X=x) = C(K,x) C(N−K,d−x) / C(N,d)
P(X≥1) = 1 − C(N−K,d) / C(N,d)

For a multi-letter extension such as ING, the engine uses a multivariate count: every required I, N and G must be present, with blanks treated as alternative resources.

Expectation is not certainty

A 40-point counterattack with probability 0.20 has Risk EV 8. That does not predict an eight-point reply; it says repeated equivalent positions lose eight points on average. Variance matters: a risk-neutral policy compares means, while a risk-averse policy can penalize tail loss or conditional value-at-risk.

Risk EVP(reply) × reply scoreSetup EVP(draw) × future scoreNetreward − expected costs

Explainable AI today

Each move becomes a feature vector: immediate score, hook risk, setup value, opening risk, anchor denial, rack leave and premium exposure. A hand-written linear policy ranks moves. Every term is visible, so students can audit why HARN beats HARNS.

How this becomes Machine Learning

Record feature vectors and outcomes from self-play. Fit linear regression to score difference, logistic regression to win probability, or a ranking model to pairwise move preference. Split by complete games—not individual moves—to avoid leakage. Standardize features before learning coefficients, compare against the hand policy, and report confidence intervals across seeds.

07
ANCHORING

Control where the next word must connect

Anchor + cross-check mask

An anchor is an empty square adjacent to an existing tile. Perpendicular fixed letters create a pattern. In L_B only A makes a legal word, so its mask contains one bit. A broad mask offers choices; a narrow mask denies them.

allowed(anchor) = letters making
every perpendicular word legal

L _ B → allowed = { A }

Anchor Denial and Pass pressure

For each constrained anchor, AI finds the probability that the opponent lacks every allowed letter, then averages it as Anchor Denial. 1-Tile Pass checks the union across the board. Neither is an exact pass probability because a multi-tile word may remain.

Read it this way: high denial means difficult gates; low opening risk means those gates do not point to valuable premiums.

Explainable decision functionStrategic = Score − Risk EV + Setup EV − 0.25·Opening Risk + 6·Anchor DenialThese are teaching defaults, not universal constants. Test them with ablation and self-play.
SUBJECT 06

Computer Networks / Network Computing

Treat one solve as a message travelling through a small distributed system: the browser serializes board state, the PHP gateway validates and routes it, and a persistent Python service performs the indexed search. Students trace the request across process and container boundaries, distinguish transmission latency from computation time, define timeout and retry policy, and preserve the semantic difference between an empty legal-move set and an unavailable service.

HTTP request/responseJSON serializationTCP keep-aliveLatency budgetTimeout & retryHealth checkFailure semanticsDocker network
08
API ARCHITECTURE

One click crosses three execution boundaries

BrowserJavaScript UI
POST JSON
PHP APIvalidation + scoring
HTTP Docker network
FastAPI workerNumba hash lookup
JSON candidates
Solverplacement + risk

Client state and the DOM

The board is rendered from one state model: 225 cells, two racks, scores, turn, bag and history. Event handlers update state first and render second. Keeping state separate from HTML prevents the visible board from becoming an unreliable database. Responsive CSS preserves square geometry and reading order across desktop and mobile.

state → render(board, rack, score)
user event → validate → update state → render

Asynchronous UI as a state machine

A Solve click passes through idle → loading → success, empty result or failure. Disable duplicate submission while loading, distinguish “no legal move” from a network error, and render server-provided evidence rather than recomputing it inconsistently in the browser. Controls require labels, keyboard access and visible focus.

fetch(request)
  .then(validateHTTP)
  .then(renderMoves)
  .catch(renderFailure)

Public endpoint contract

POST /pages/scrabble-api.php
Content-Type: application/json

{
  "board": [15 strings × 15 cells],
  "rack": "AEINRST",
  "limit": 20,
  "engine": "trie" | "sqlite-hash" | "memory-hash"
          | "compact-numba" | "prime-product",
  "adversarial": true
}

The response returns lexicon identity, timing, engine statistics, unseen probabilities and a list of moves. Each move includes coordinates, direction, newly placed tiles, score and optional heuristic decomposition.

Internal worker contract

GET  /health
→ status, backend, signatures,
  table_bytes, startup_ms

POST /candidates
{ "board": [...], "rack": "AEINRST" }
→ words, hash_lookups, hash_hits,
  signature_ms, hash_query_ms,
  worker_ms, cache_hit

FastAPI validates exactly 15 ASCII rows and a 1–7 tile rack. The worker loads 237,740 signatures once during lifespan startup, JIT-compiles Numba probes once, then serves warm requests without rebuilding the index.

Status semantics

200 success · 422 invalid client data · 500 internal failure · 502 gateway cannot obtain a valid worker response.

Latency budget

Measure serialization, network transfer, signature generation, hash lookup, placement and rendering separately. Wall time is not algorithm time.

Trust boundary

Validate length and character sets at every external boundary. Never trust browser limits alone; clients can send arbitrary HTTP.

Resilience

Use finite timeouts, health checks and a documented fallback. Do not convert every worker failure into “no legal move”; failure and an empty answer are different states.

Network laboratory questions
  1. Capture one request and calculate payload bytes versus response bytes.
  2. Compare keep-alive with a new TCP connection per solve. Where is handshake cost visible?
  3. Inject a worker timeout. Which HTTP status and UI message preserve the correct meaning?
  4. Which fields make POST /candidates cacheable by a canonical request hash?
SUBJECT 07

Web Programming

Build the laboratory as a stateful single-page interaction without hiding the underlying experiment. Semantic HTML gives the board, controls and result table meaningful structure; responsive CSS keeps 225 squares usable across screens; JavaScript owns turn, rack and pending-request state; Fetch connects that state to the API. Students validate input twice, render loading, error, empty and success states explicitly, prevent duplicate submissions, and keep keyboard and screen-reader interaction equivalent to pointer input.

Semantic HTMLCSS GridResponsive designDOM stateFetch & asyncForm validationAccessible UIProgressive enhancement
09
WEB APPLICATION ARCHITECTURE

Turn an algorithm into a trustworthy interaction

State, events and rendering

The board array is the source of truth. Click and keyboard events update that model first; one render function then derives tiles, scores and controls from it. Keeping model and DOM responsibilities separate prevents a square from looking occupied while the solver receives an empty cell. During Fetch, a pending flag disables repeated Solve actions and an AbortController can cancel a stale request after the board changes.

event → validate → update state → render
solve → pending → fetch → parse → render result
error → restore controls → explain failure

A visible state machine

A robust interface distinguishes idle, editing, loading, success, no-move, validation-error and service-error states. HTTP 200 with an empty array is not HTTP 502; showing both as “no move” teaches the wrong system model. The language parameter, selected engine and form values should also survive predictable navigation and refresh behavior.

Client validation

Give immediate feedback about rack length, characters and board shape.

Server validation

This is the authoritative boundary; never trust editable browser state.

Accessible status

Announce progress and errors with focus management and live regions.

Responsive evidence

Preserve scores, coordinates and sortability—not merely appearance.

Web Programming laboratory questions
  1. Draw the UI state machine and identify which transitions originate from users, timers and network responses.
  2. Make Solve safe against double-clicks and out-of-order responses, then write a reproducible test.
  3. Use only a keyboard to edit the board, choose an engine, solve and play a result. Record any inaccessible step.
  4. Compare desktop and mobile rendering: which information may reflow, and which evidence must never disappear?
10
CAPSTONE INVESTIGATION

Connect seven subjects through one playable system

Algorithms

Trace Trie nodes and count unique subset signatures.

Discrete Mathematics

Prove the prime-product mapping and count unique rack multisets.

Database

Normalize the bucket schema, then compare query plans and storage.

Statistics

Verify hypergeometric probabilities by simulation and confidence intervals.

AI/ML

Toggle one heuristic at a time, run self-play and measure policy sensitivity.

Network Computing

Separate API latency from solver time and test failure semantics.

Web Programming

Model UI states, validate inputs and test responsive, keyboard-accessible interaction.

WHY THIS LAB EXISTS

From a hashing lecture to a game engine

I have taught Design and Analysis of Data Structures and Algorithms for many years. I did not want students to meet searching only as formulas and isolated code, so I turned the lesson into a playable engine: every data structure must find a legal Scrabble move, score it, and explain the cost of its search.

1

Anagram signature

SILENT and LISTEN become the same letter-frequency signature. Building the signature is O(n); looking up its bucket in a hash table is O(1) on average.

2

Leave-one-out & subsets

For BEGIN, remove letters recursively and look up EGIN, BGIN, BEIN … down to GIN or IN. With a seven-tile rack, the subset space is bounded and can be generated before placement checks.

3

Board validation

A hash hit only says which words share the available multiset. The engine must still test anchors, direction, premium squares, blanks, and every perpendicular cross-word.

Why Hash can still lose to Trie

The current Hash path creates signatures, queries SQLite, expands word buckets, builds a temporary candidate Trie, and then runs the same board solver. Packed Trie starts pruning at board anchors immediately, so its smaller constant factors often win on constrained boards. O(1) describes one average bucket lookup - not the complete Scrabble search.

What Hash already does better

It retrieves complete anagram groups directly, makes subset selection observable, and can beat Trie candidate enumeration for racks with many valid combinations. In a local 1,000-run microbenchmark, in-memory Hash was 2.03× faster for AEINRST candidate retrieval, but only 1.27× for CARTSAA; end-to-end placement may still favor Trie.

How to make Hash faster next
  1. Keep the anagram index resident in shared memory; remove SQLite and per-request deserialization from the hot path.
  2. Replace the temporary candidate Trie with direct word-ID buckets and validate only returned word IDs.
  3. Precompute the 127 rack-subset signatures incrementally and cache them by canonical rack signature.
  4. Index board patterns too: combine rack multiset, fixed letters, length, and cross-check masks before lookup.
  5. Move the compact index and placement loop to a long-lived Python/C++ service or PHP extension, then benchmark warm requests separately from startup.

Click a square and type A-Z for a custom position. Lowercase board state is reserved for played blanks.

STUDENT CAPSTONE CONTRIBUTION

From a Unity prototype to a research-ready teaching engine

This web laboratory extends ideas documented by four student contributors: make Scrabble playable without waiting for an opponent, use a word index for responsive validation, and make an AI turn observable as a sequence of candidate generation, validation, scoring and selection.

Editorial sketch of the four student contributors
01 · PROBLEM

Practice without waiting for a partner

The report begins with a practical learning problem: limited opponents interrupt vocabulary practice. A bot turns the game into an always-available English learning activity.

02 · DESIGN

Figma → Unity / C#

The team used collaborative UI prototyping, then implemented the board, tile bag, drag-and-drop interaction, word validation and real-time score display as a Unity game.

03 · STATE MODEL

Validate → Score → End? → AI

Their sequence model separates invalid words from valid turns and routes both player and AI moves through the same validation, scoring and game-end checks—the same invariant preserved by this web version.

04 · REPORTED EVALUATION

A learning signal worth testing again

In a preliminary questionnaire of 35 participants, the student report states that more than 80% viewed the game as supporting new vocabulary, recall and enjoyment. This is project-reported evidence—not an independent controlled trial—and motivates a reproducible classroom study.

Source note: Summarized from the student Capstone report “Scrabble game”. Claims about runtime are intentionally not repeated here unless this web implementation measures them under identical data and workloads.