Five indexes · Turn-based play · Explainable AI

ห้องทดลอง
Scrabble Engine

สร้าง Computer Science Capstone ที่เล่นได้จริง: เปรียบเทียบดัชนีห้าแบบ ตรวจสอบทุก legal move แล้วประกอบนโยบาย AI จากความน่าจะเป็น การเตรียมตาถัดไป ความเสี่ยงตอบโต้ และการควบคุมกระดาน

RACK ของเรา · ตาต่อไปของเขา

เจ็ดตัวอักษร หนึ่งกระดาน—คำเดียวจะสร้างเรื่องได้แค่ไหน?

จะรีบเอาคะแนนสูงสุด ลากคำไปหา Triple Word เก็บตัวดีไว้รอตาหน้า หรือกั๊กช่อง anchor จนคู่แข่งหาคำสบาย ๆ ไม่ได้? ลองวางกระดาน ใส่ Rack แล้วให้ดัชนีทั้งห้าแบบเสนอคำตอบ จากนั้นลองจับผิดมันดู—เพราะคำที่เร็วและแรงที่สุดอาจไม่ใช่คำที่ฉลาดที่สุดเสมอไป

① ล่าคะแนน② อ่านความเสี่ยง③ ปิดทางตอบโต้
1

Trie — follow prefixes

แต่ละขอบแทนตัวอักษร เริ่มจาก anchor แล้วเดินต่อเฉพาะ prefix ที่ยังมีในพจนานุกรม กิ่งที่เป็นไปไม่ได้ถูกตัดทันที

Exact word / prefix
O(L)
Build
O(ΣL)
Memory
O(number of nodes)
L = ความยาวคำ การค้นทั้งกระดานขึ้นกับจำนวน anchor และคำตอบ; O(L) คือเส้นทาง lookup ไม่ใช่เวลาของ solver ทั้งหมด
2

Hash — jump to anagram buckets

ตัวอักษรถูกแปลงเป็น frequency signature ทำให้ LISTEN และ SILENT เปิด bucket เดียวกัน จากนั้นใช้ subset ของ rack ดึงกลุ่มคำก่อนตรวจ anchor และคำตัด

Build signature
O(L)
Average bucket lookup
O(1)
Rack subsets
O(2ᴿ)
R ≤ 7 จึงมี subset ไม่ว่างไม่เกิน 127 ชุด ส่วน O(1) หมายถึง hash lookup เฉลี่ยหนึ่งครั้ง ไม่รวมสร้าง signature, สร้าง subset และตรวจการวางคำ
การบูรณาการผ่าน CAPSTONE PROJECT

บูรณาการ Computer Science ผ่านวิชา Capstone Project

นักศึกษาบูรณาการคณิตศาสตร์ไม่ต่อเนื่อง โครงสร้างข้อมูล ฐานข้อมูล ความน่าจะเป็น AI ที่อธิบายได้ API และวิศวกรรมประสิทธิภาพเป็น Game Engine ที่สาธิต วัดผล และปกป้องเหตุผลการออกแบบได้ ผลงานที่เล่นได้จึงเป็นทั้งชิ้นงาน Capstone และหลักฐานของทุกการตัดสินใจเชิงวิศวกรรม

ระดับปริญญาตรี
เวลาเรียน90–120 min
รูปแบบคู่ทดลอง + อภิปราย

เมื่อจบบทเรียน นักศึกษาสามารถ

  1. ไล่ลำดับการ insert และ lookup ใน Trie และ hash-based anagram index;
  2. วิเคราะห์ต้นทุนของ prefix search, signature construction และ subset enumeration;
  3. เขียน leave-one-out และ bit-mask subset selection โดยไม่สร้าง multiset ซ้ำ;
  4. อธิบายว่าทำไม hash lookup แบบ O(1) ไม่ได้ทำให้ solver ทั้งหมดเป็น O(1);
  5. รวมคะแนน ความเสี่ยงตอบโต้ การรอตาหน้า และการควบคุม anchor เป็น AI ที่อธิบายได้
SUBJECT 01

Design and Analysis of Data Structures and Algorithms

ออกแบบการแทนพจนานุกรม Rack และกระดานให้กำจัด candidate ที่เป็นไปไม่ได้ตั้งแต่ต้น แล้ววิเคราะห์ต้นทุนจริงของ prefix traversal, recursion, subset generation และ move enumeration แบบ output-sensitive

TrieHash tableRecursionSubset algorithmsComplexityBenchmarking
01
TRIE INTERNALS

จากพจนานุกรมสู่เครื่องจักร prefix

Node, edge และ terminal flag

การ insert CAT เริ่มจาก root แล้วเดินหรือสร้าง edge C, A และ T จากนั้น mark T เป็น terminal การ insert CAR ใช้ C และ A ร่วมกัน สร้าง R แล้ว mark terminal การค้นคำสำเร็จเมื่อ edge ครบทุกตัวและ node สุดท้ายเป็น terminal ส่วน prefix search ไม่ต้องตรวจ terminal

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

การเดินบนกระดานต่างจาก lookup อย่างไร

ที่ช่องว่าง traversal แตกกิ่งเฉพาะ child ที่มีใน rack และผ่าน cross-check mask ส่วนช่องที่มีตัวอยู่แล้วจะเดินตามตัวนั้นโดยไม่ใช้ rack ดังนั้น state ประกอบด้วย Trie node, ตำแหน่งกระดาน, multiset ที่เหลือ, ตัวที่วาง และสถานะว่าครอบ anchor แล้วหรือยัง

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 เป็น O(L) แต่ move generation เป็น output-sensitive ขึ้นกับจำนวน anchor A, branching b, rack states และคำตอบ Z ไม่ใช่เพียง O(L)

SUBJECT 02

Discrete Mathematics

Set, multiset, equivalence relation, combinatorics และทฤษฎีบทมูลฐานของเลขคณิตอธิบายว่าเหตุใด anagram จึงมี identity ร่วมกัน และเหตุใด Rack เจ็ดตัวจึงมีครอบครัวทางเลือกแบบ exponential ที่ยังมีขอบเขต

SetsMultisetsEquivalence classesPrime factorizationCombinatoricsProof
02
DISCRETE MATH → HASH

Prime product, packed count และ hash table ที่ใช้จริง

Anagram key จากจำนวนเฉพาะ

กำหนดจำนวนเฉพาะต่างกันให้แต่ละตัว A=2, B=3, C=5, …, Z=101 แล้วคูณ prime ของทุกตัว ทฤษฎีการแยกตัวประกอบเฉพาะทำให้ anagram มีผลคูณเดียวกัน เช่น ABBA และ BABA เท่ากับ 2²×3²=36 ส่วน multiset อื่นให้จำนวนเต็มอื่น

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

วิธีนี้เหมาะสำหรับการสอน แต่ผลคูณ 64-bit อาจ overflow และการ mod ด้วยขนาดตารางทำให้เกิด collision จึงยังต้องเปรียบเทียบ signature ต้นฉบับเมื่อ key ชนกัน

Representation ที่ระบบจริงใช้

Engine เก็บจำนวนตัวอักษร 26 ค่า เนื่องจาก count ต่อตัวมีขนาดเล็ก จึง pack count 4-bit สองค่าต่อหนึ่ง byte ทำให้ 26 ค่าเป็น key 13 bytes ที่ไม่เสียข้อมูล จากนั้น FNV-1a 64-bit เลือก slot ใน open-addressed table, ใช้ linear probing เมื่อชน และเปรียบเทียบครบทั้ง 13 bytes ก่อนยอมรับ 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: probe O(1) เมื่อควบคุม load factor; Worst case: O(M) หาก clustering บังคับให้ไล่ตาราง

Blank เข้า Hash search อย่างไร

Blank มีคะแนนศูนย์แต่แทน A–Z ได้ จึงไม่ควรผูกกับ prime เดียว สำหรับ subset ที่มี blank ตัวสร้าง candidate แตก 26 ทาง เพิ่ม count ของตัวแทน pack signature แล้วเก็บใน set หากมีสอง blank จะแตกเป็นคู่ตัวอักษร จากนั้น deduplicate count vector ที่เหมือนกัน ตอนวางจริง move ต้องบันทึกว่าตัวใดมาจาก blank เพื่อให้คะแนนยังเป็นศูนย์แม้กระดานแสดงตัวอักษรที่เลือก

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

Prime product เร็วกว่าในการสร้าง subset identity

ค่า median แบบ warm ใน persistent worker เดียวกัน วัดเฉพาะการสร้าง subset; full-word lookup ยังใช้ packed database key

RackPrimePackedSpeedup
AEINRST3.917 µs9.935 µs2.536×
AABBCDE2.742 µs6.770 µs2.470×
AAAAAAA1.008 µs5.657 µs5.612×
prime call แรกเสียเวลาประมาณ 125 ms สำหรับ Numba JIT ใหม่ แต่ persistent worker เฉลี่ยต้นทุนครั้งเดียวนี้ออกไป ส่วน blank expansion ยังใช้ substitution path แยก
END-TO-END CANDIDATE RETRIEVAL · 3,000 × 7 REPEATS

Prime Product เทียบกับ Packed Trie

ทุก Engine คืน candidate set ตรงกัน เวลาเป็น warm uncached median รวมสร้าง subset, lookup, แตก bucket, deduplicate และ sort แต่ยังไม่รวมการวางบนกระดาน

RackWordsPrimeTriePrime faster
AEINRST343141.986 µs1,283.597 µs9.040×
AABBCDE4222.478 µs226.228 µs10.064×
AAAAAAA16.428 µs13.447 µs2.092×
ข้อแลกเปลี่ยน: Prime index ใช้ startup 1.49–1.83 s ขณะที่ map Packed Trie ใช้ 3.7–4.0 ms แต่ persistent service เฉลี่ย startup ออกไปได้ Prime index ครอบคลุม 55,784 signatures ความยาว 2–7 และ key/slot arrays ใช้ 970,560 bytes โดยยังไม่รวม word buckets
PAPER TABLE · IDENTICAL SOWPODS CANDIDATE SETS

เปรียบเทียบ Engine ทั้งห้าแบบ

Warm uncached median หน่วย microseconds; 1,000 iterations × 7 repeats ค่ายิ่งต่ำยิ่งดี ทุกแถวตรวจ candidate-set equivalence แบบ exact ก่อนจับเวลา

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
ช่วง startup ที่วัดได้ (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
สภาพแวดล้อมIntel 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
ขอบเขตcandidate retrieval จาก rack สำหรับคำยาว 2–7 ตัว ไม่รวม blank, board anchor, placement scoring, API และ rendering โดย Prime/Packed ใช้ Numba ส่วน Trie/SQLite/Memory เป็น readable Python implementation ดังนั้นภาษาและระดับ optimization ยังเป็น threat to validity
03
LEAVE-ONE-OUT

LEAVE-ONE-OUT

Leave-one-out เริ่มจาก rack เต็ม ตัดหนึ่งตำแหน่ง บันทึก multiset ที่เหลือ แล้วเรียกซ้ำ สำหรับ BEGIN ชั้นแรกได้ EGIN, BGIN, BEIN, BEGN และ BEGI เมื่อทำต่อจะได้ subset ขนาดเล็กทุกชุด ต้อง canonicalize ด้วยการ sort หรือ frequency count เพราะการตัดตัวซ้ำคนละตำแหน่งไม่ควรสร้างงานซ้ำ

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)

การวิเคราะห์

  • มี position subsets ไม่เกิน 2ᴿ ชุด
  • sort แต่ละ subset ใช้ O(R log R); counter 26 ช่องใช้ O(R)
  • เมื่อ R = 7 มี subset ไม่ว่างเพียง 127 ชุด เป็น exponential algorithm ที่ถูกจำกัดด้วยกติกาเกม
  • memoization กำจัด state ซ้ำที่เกิดจากตัวอักษรซ้ำ
04
SUBSET SELECTION

Bit mask ทำให้ทุกการเลือกตรวจสอบได้

กำหนดตำแหน่ง rack เป็น 0…R−1 จำนวนเต็ม mask m เลือกตำแหน่ง i เมื่อ bit i เป็น 1 การวนจาก 1 ถึง 2ᴿ−1 สร้าง position subset ที่ไม่ว่างครบหนึ่งครั้ง จากนั้น canonical signature รวมกรณีที่ต่างกันเพียงสลับตัวอักษรซ้ำ

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 หรือ bit mask?

วิธีจุดเด่นข้อควรระวัง
Leave-one-outเห็น recursion และ memoization ชัดเกิด state ซ้ำหากไม่มี canonical key
Bit maskสั้น เป็น iterative และแบ่งงานง่ายมี position duplicate ก่อน deduplicate
Count-state DPทำงานบน multiset โดยตรงindex ซับซ้อนขึ้น

Hash pipeline: subset → รวมตัวที่ตรึงบนกระดาน → signature O(L) → bucket lookup เฉลี่ย O(1) → ตรวจ anchor, blank, premium และคำตัดทุกคำ

SUBJECT 03

Database Design

หลังออกแบบ key นักศึกษาต้องตัดสินใจว่าจะจัดเก็บอย่างไร รวม anagram อย่างไร รับประกัน integrity อย่างไร และแยกฐานข้อมูลตอน build ออกจาก runtime index ที่เน้นการอ่านอย่างไร

SchemaPrimary keyBLOBNormalizationIndex
05
DATABASE DESIGN

หนึ่ง signature หนึ่งแถว หลายคำ

พจนานุกรม SOWPODS ที่ normalize แล้วมี 267,752 คำ แต่มีเพียง 237,740 signatures เพราะ anagram อยู่ใน equivalence class เดียวกัน หากเก็บหนึ่งแถวต่อหนึ่งคำจะทำ key ซ้ำ ฐานข้อมูลจึงเก็บหนึ่งแถวต่อ signature และรวมคำใน bucket ที่คั่นด้วย zero byte

anagrams

ColumnTypeหน้าที่
signatureBLOB PKpacked count vector 13 bytes
wordsBLOB NOT NULLกลุ่ม anagram คั่นด้วย NUL
word_countINTEGERขนาด bucket และ diagnostics
min_lengthINTEGERกรองความยาวเบื้องต้น

metadata

ColumnTypeตัวอย่าง
keyTEXT PKschema, source, words
valueTEXTwarin.scrabble.hash-lexicon.v1

ค่า SHA-256 ผูก binary index เข้ากับ input ที่ normalize แล้วอย่างแน่นอน หาก manifest ไม่ตรง ระบบปฏิเสธแทนการใช้พจนานุกรมคนละชุดโดยไม่แจ้ง

ทำไมใช้ WITHOUT ROWID?

ปกติ SQLite มี integer rowid ซ่อนอยู่และมี primary-key index แยก แต่ signature BLOB เป็นทั้ง identity และ access path อยู่แล้ว WITHOUT ROWID จึงเก็บแถวใน primary-key B-tree ลด identity และ lookup ที่ซ้ำซ้อน

Database กับ runtime hash table

SQLite durable, portable และ rebuild ง่าย แต่ B-tree lookup เป็น O(log S) ไม่ใช่ hash จริง เมื่อ worker เริ่มทำงานจึงโหลดทุกแถวเข้า compact NumPy arrays และ open-addressed table SQLite เป็น source of truth ส่วน in-memory tableเป็น hot read path

คำถามออกแบบฐานข้อมูลสำหรับนักศึกษา
  1. ควร uppercase ก่อนหรือหลัง deduplicate และเกิด invariant ใด?
  2. เมื่อใดตารางลูก signature_words(signature, word) ดีกว่า packed bucket?
  3. transaction boundary แบบใดทำให้ index ใหม่ปรากฏแบบ atomic?
  4. ต้องมี metadata ใดเพื่อ benchmark ที่ reproducible?
05A
ENGINE MODES

ดัชนี 5 แบบ ใช้ตัวตรวจคำและคะแนนชุดเดียวกัน

ทุก mode ใช้กระดาน Rack พจนานุกรม และตัวตรวจตำแหน่ง/คะแนน PHP ชุดเดียวกัน เปลี่ยนเฉพาะวิธีดึง candidate การ probe แบบ O(1) โดยเฉลี่ยหนึ่งครั้งไม่ได้ทำให้ Solver ทั้งระบบเป็น O(1)

Packed Trie · PHP

ต้นไม้ prefix แบบ binary อ่านอย่างเดียว ตัด prefix ที่เป็นไปไม่ได้ตั้งแต่ anchor การตรวจคำยาว L เป็น O(L) และ mode นี้ไม่ query SQLite ตอนทำงาน

SQLite Hash · B-tree

ลายเซ็นจำนวนตัวอักษร 13 byte ชี้ไปยัง anagram bucket โดย SQLite ใช้ primary-key B-tree: O(log S) เมื่อมี S signatures แล้วจึงแตก bucket เป็นคำ

In-memory Hash · Python dict

โหลด SQLite ครั้งเดียวเข้า dictionary ที่รันค้าง การ probe เฉลี่ย O(1) และเทียบ key 13 byte จริงเพื่อจัดการ collision

Compact/Numba Hash

fast path ตั้งต้นใช้ key แบบ immutable ใน NumPy, slot int32 แบบ open addressing และ compile การสร้าง subset กับ batch probe ด้วย Numba

Prime-product Hash

กำหนด A=2, B=3, … แล้วเปลี่ยนคำเป็นผลคูณที่แน่นอน จำนวนเฉพาะเปรียบเหมือนแม่สีที่แบ่งย่อยไม่ได้ การคูณผสมตัวอักษรโดยยังรักษาว่าแต่ละสีปรากฏกี่ครั้ง ลำดับหายไป—LISTEN และ SILENT กลายเป็นเงารูปเดียวกันทางคณิตศาสตร์—แต่การแยกตัวประกอบเฉพาะยังถอด multiset กลับได้ ทฤษฎีบทนามธรรมจึงกลายเป็นดัชนีที่ทั้งสง่างามและรันได้จริง Rack 7 ตัวใช้ uint64/Numba ส่วนคำยาวบนกระดานใช้จำนวนเต็ม exact ของ Python

เวลา solve ทั้งหมดยังรวมการสร้าง candidate Trie ของกลุ่ม Hash, anchor traversal, ตรวจคำตัด, คิดคะแนน, sort และ API overhead ส่วน In-memory และ Prime initialize แบบ lazy จึงควรรายงาน cold และ warm แยกกัน

SUBJECT 04

Statistics and Probability

กระดานสังเกตได้ แต่ Rack คู่แข่งและการจั่วในอนาคตเป็นตัวแปรแฝง การสุ่มแบบไม่คืนกลับ ความน่าจะเป็นแบบมีเงื่อนไข และ expected value เปลี่ยนข้อมูลที่ไม่สมบูรณ์เป็นปริมาณที่ตรวจและจำลองได้

HypergeometricConditional probabilityExpected valueVarianceSimulation
06
AI เชิงความน่าจะเป็น

Move ที่ดีไม่ได้มีเพียงคะแนนทันที

เราไม่เห็น rack คู่แข่ง Engine จึงไม่แต่ง rack ขึ้นเอง แต่หักตัวอักษรบนกระดานและ rack ของเราออกจากจำนวนตัวมาตรฐาน แล้วประเมินว่าตัวที่ต้องใช้ตอบโต้จะปรากฏในตัวอย่าง 7 ตัวแบบไม่คืนกลับหรือไม่

มีตัวที่ต้องการอย่างน้อยหนึ่งตัวP(X ≥ 1) = 1 − C(N−K, d) / C(N, d)N = ตัวที่ยังไม่เห็น, K = ตัวที่ใช้ได้รวม blank, d = ขนาด rack คู่แข่งไม่เกิน 7

คะแนนทันที

คะแนนกติกาจริง รวม premium, คำตัด และ bingo bonus

ความเสี่ยงตอบโต้

Risk EV = P(reply) × counter score
ตรวจ S, ES, ED, ING และ hook รูปแบบอื่น

การรอและ setup

Setup EV = P(draw) × future score
ให้คุณค่ากับ leave และ hook สำหรับตาหน้า

ความเสี่ยงเปิดทาง

ลงโทษ anchor ใหม่ที่พาไปหา Double/Triple Word

SUBJECT 05

Artificial Intelligence and Machine Learning

Move กลายเป็น feature vector ที่อธิบายได้ นักศึกษาเลือกเพิ่มหรือตัด heuristic ตรวจสอบ policy ที่เกิดขึ้น แล้วใช้ข้อมูล self-play เรียนและประเมินน้ำหนักโดยไม่ซ่อนกระบวนการตัดสินใจ

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

จาก Rack ที่ซ่อนอยู่สู่การตัดสินใจที่อธิบายได้

การสุ่มแบบไม่คืนกลับ

การหยิบตัวอักษรไม่เป็นอิสระ เพราะหยิบ S หนึ่งตัวแล้วโอกาส S ตัวต่อไปเปลี่ยน Binomial จึงเป็นเพียง approximation ส่วน Hypergeometric นับ rack ขนาด d จาก unseen N ตัวที่มี useful tile K ตัวได้ตรงกับการสุ่มแบบไม่คืนกลับ

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)

สำหรับ extension หลายตัว เช่น ING Engine ใช้ multivariate count โดย I, N และ G ที่ต้องใช้ต้องมีครบ และ blank เป็นทรัพยากรทดแทน

Expected value ไม่ใช่สิ่งที่จะเกิดแน่นอน

การตอบโต้ 40 คะแนนที่มีโอกาส 0.20 ให้ Risk EV 8 ไม่ได้ทำนายว่าคู่แข่งจะได้ 8 คะแนน แต่หมายถึงโจทย์เทียบเท่าที่เล่นซ้ำเสียเฉลี่ย 8 คะแนน Variance ยังสำคัญ: policy แบบ risk-neutral เปรียบเทียบค่าเฉลี่ย ส่วน risk-averse อาจลงโทษ tail loss หรือ conditional value-at-risk

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

Explainable AI ที่ใช้อยู่

แต่ละ move กลายเป็น feature vector ได้แก่ immediate score, hook risk, setup value, opening risk, anchor denial, rack leave และ premium exposure จากนั้น linear policy ที่กำหนดด้วยมือใช้จัดอันดับ ทุก term มองเห็นได้ นักศึกษาจึง audit ได้ว่าทำไม HARN ชนะ HARNS

ต่อยอดเป็น Machine Learning อย่างไร

เก็บ feature vector และผลจาก self-play แล้วใช้ linear regression ทำนาย score difference, logistic regression ทำนาย win probability หรือ ranking model เรียน pairwise preference ต้อง split ตามเกมเต็ม ไม่ใช่ราย move เพื่อกัน leakage, standardize features ก่อนเรียน coefficient, เปรียบเทียบกับ hand policy และรายงาน confidence interval ข้ามหลาย seed

07
ANCHORING

ควบคุมช่องที่คำถัดไปต้องเชื่อม

Anchor + cross-check mask

Anchor คือช่องว่างที่ติดกับตัวเดิม ตัวอักษรแนวตั้งฉากสร้าง pattern เช่น L_B ยอมรับเฉพาะ A ดังนั้น mask มีเพียงหนึ่ง bit mask กว้างเปิดทางเลือก ส่วน mask แคบช่วยกั๊กทางลง

allowed(anchor) = letters making
every perpendicular word legal

L _ B → allowed = { A }

Anchor Denial และแรงกดดันให้ Pass

AI คำนวณโอกาสที่คู่แข่งไม่มีตัวใน allowed set ของ constrained anchor แต่ละช่อง แล้วเฉลี่ยเป็น Anchor Denial ส่วน 1-Tile Pass ตรวจ union ทั้งกระดาน ทั้งสองค่าไม่ใช่โอกาส Pass ที่แน่นอน เพราะอาจยังมีคำที่ลงหลายตัว

อ่านค่าแบบนี้: denial สูงหมายถึงประตูเปิดยาก ส่วน opening risk ต่ำหมายถึงประตูนั้นไม่พาไปยังช่องโบนัสสำคัญ

ฟังก์ชันตัดสินใจที่อธิบายได้Strategic = Score − Risk EV + Setup EV − 0.25·Opening Risk + 6·Anchor Denialสัมประสิทธิ์เป็นค่าเริ่มต้นเพื่อการสอน ไม่ใช่ค่าคงที่สากล ควรทดสอบด้วย ablation และ self-play
SUBJECT 06

Computer Networks / Network Computing

มองการกด Solve หนึ่งครั้งเป็นข้อความที่เดินทางในระบบกระจายขนาดเล็ก: Browser serialize สถานะกระดาน, PHP gateway ตรวจสอบและส่งต่อ และ persistent Python service ทำ indexed search นักศึกษาไล่ request ข้าม process และ container แยก transmission latency ออกจาก computation time ออกแบบ timeout/retry policy และรักษาความหมายที่ต่างกันระหว่าง “ไม่มี legal move” กับ “service ติดต่อไม่ได้”

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

หนึ่งคลิกเดินทางข้ามสามขอบเขตการทำงาน

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

Client state และ DOM

กระดาน render จาก state model เดียว: 225 ช่อง, Rack สองชุด, คะแนน, turn, bag และ history Event handler ต้องแก้ state ก่อนแล้วจึง render การแยก state ออกจาก HTML ป้องกันไม่ให้กระดานที่มองเห็นกลายเป็นฐานข้อมูลที่ไม่น่าเชื่อถือ ส่วน responsive CSS รักษารูปทรงสี่เหลี่ยมและลำดับการอ่านทั้ง desktop และ mobile

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

Asynchronous UI คือ state machine

การกด Solve เดินผ่าน idle → loading → success, empty result หรือ failure ต้องกันการส่งซ้ำระหว่าง loading แยก “ไม่มีคำลง” ออกจาก network error และแสดงหลักฐานจาก server แทนการคำนวณซ้ำไม่ตรงกันใน Browser ทุก control ต้องมี label ใช้ keyboard ได้ และเห็น focus ชัดเจน

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

สัญญาของ public endpoint

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
}

response คืน identity ของ lexicon, timing, engine statistics, unseen probabilities และรายการ moves แต่ละ move มีพิกัด ทิศทาง ตัวที่วางใหม่ คะแนน และ heuristic decomposition เมื่อร้องขอ

สัญญาของ worker ภายใน

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 ตรวจว่ากระดานมี 15 แถว ASCII และ rack ยาว 1–7 ตัว worker โหลด 237,740 signatures ครั้งเดียวตอน lifespan startup และ JIT-compile Numba probe ครั้งเดียว จากนั้น request ปกติไม่ rebuild index

ความหมายของ status

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

งบเวลา

แยกวัด serialization, network, signature generation, hash lookup, placement และ rendering เพราะ wall time ไม่เท่ากับ algorithm time

ขอบเขตความเชื่อถือ

ตรวจความยาวและชุดตัวอักษรทุก external boundary อย่าเชื่อ limit ใน browser เพราะ client ส่ง HTTP เองได้

ความทนทาน

ใช้ finite timeout, health check และ fallback ที่ระบุชัด อย่าแปลง worker failure เป็น “ไม่มีคำลง” เพราะ failure กับ empty result เป็นคนละ state

โจทย์ทดลอง Computer Networks
  1. จับหนึ่ง request แล้วคำนวณ request/response bytes
  2. เปรียบเทียบ keep-alive กับเปิด TCP ใหม่ทุก solve และหาต้นทุน handshake
  3. จำลอง worker timeout แล้วเลือก HTTP status และข้อความ UI ที่ไม่บิดความหมาย
  4. field ใดต้องรวมใน canonical request hash เพื่อ cache POST /candidates?
SUBJECT 07

Web Programming

สร้างห้องทดลองเป็นหน้าเว็บแบบมี state โดยไม่ซ่อนกลไกการทดลอง: semantic HTML กำหนดความหมายให้กระดาน ตัวควบคุม และตารางผล, responsive CSS ทำให้ 225 ช่องยังใช้งานได้หลายขนาดจอ, JavaScript ดูแล turn, rack และ pending request ส่วน Fetch เชื่อม state เหล่านี้กับ API นักศึกษาต้อง validate input ทั้ง client และ server แสดงสถานะ loading, error, empty และ success ให้ชัด ป้องกันการส่งซ้ำ และทำให้ keyboard กับ screen reader ใช้งานได้เทียบเท่า pointer

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

เปลี่ยนอัลกอริทึมให้เป็น Web Interaction ที่เชื่อถือได้

State, event และการ render

board array เป็น source of truth เหตุการณ์จาก click และ keyboard ต้องแก้ model ก่อน แล้ว render function จึงสร้าง tile, score และ controls จาก state เดียวกัน การแยกหน้าที่ model กับ DOM ป้องกันกรณีช่องดูเหมือนมีตัวอักษรแต่ Solver ได้รับช่องว่าง ระหว่าง Fetch ใช้ pending flag ปิดการกด Solve ซ้ำ และใช้ AbortController ยกเลิก request เก่าเมื่อกระดานเปลี่ยน

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

State machine ที่ผู้ใช้มองเห็น

Interface ที่แข็งแรงต้องแยก idle, editing, loading, success, no-move, validation-error และ service-error โดย HTTP 200 ที่ได้ array ว่างไม่ใช่ HTTP 502 การแสดงทั้งสองอย่างว่า “ไม่มีคำลง” ทำให้นักศึกษาเข้าใจระบบผิด ส่วนพารามิเตอร์ภาษา Engine ที่เลือก และค่าฟอร์มควรมีพฤติกรรมที่คาดเดาได้เมื่อย้อนหน้า เปลี่ยนภาษา หรือ refresh

Client validation

แจ้ง rack, ตัวอักษร และรูปทรงกระดานที่ผิดได้ทันที

Server validation

เป็นขอบเขตตัดสินจริง ห้ามเชื่อ state จาก browser โดยตรง

Accessible status

ประกาศ progress และ error ด้วย focus management กับ live region

Responsive evidence

รักษาคะแนน พิกัด และการ sort ไม่ใช่เพียงย่อหน้าตา

โจทย์ทดลอง Web Programming
  1. วาด UI state machine แล้วแยกว่า transition ใดมาจากผู้ใช้ timer และ network response
  2. ทำให้ Solve ปลอดภัยจาก double-click และ response ที่กลับไม่ตามลำดับ แล้วเขียน test ที่ทำซ้ำได้
  3. ใช้ keyboard อย่างเดียวเพื่อแก้กระดาน เลือก Engine, Solve และเล่นคำ แล้วบันทึกขั้นที่เข้าถึงไม่ได้
  4. เทียบ desktop กับ mobile ว่าข้อมูลใดจัดตำแหน่งใหม่ได้ และหลักฐานใดห้ามหาย
10
โจทย์บูรณาการ CAPSTONE

เชื่อมเจ็ดวิชาผ่านระบบที่เล่นได้จริง

Algorithms

ไล่ Trie nodes และนับ unique subset signatures

Discrete Mathematics

พิสูจน์การแทนด้วย prime product และนับ unique multiset ของ Rack

Database

normalize bucket schema แล้วเทียบ query plan และ storage

Statistics

ตรวจ Hypergeometric ด้วย simulation และ confidence interval

AI/ML

เปิด–ปิด heuristic ทีละส่วน ทำ self-play และวัดความไวของ policy

Network Computing

แยก API latency จาก solver time และทดสอบ failure semantics

Web Programming

ออกแบบ UI state, ตรวจ input และทดสอบ responsive interaction ที่ใช้ keyboard ได้

ที่มาของห้องทดลอง

จากบทเรียน Hashing สู่ Game Engine ที่จับต้องได้

ผมสอน Design and Analysis of Data Structures and Algorithms มาหลายปี และไม่อยากให้นักศึกษาเบื่อกับสูตรหรือโค้ดที่แยกจากโลกจริง จึงออกแบบบทเรียนเป็น game engine ที่จับต้องได้: ทุกโครงสร้างข้อมูลต้องค้นคำที่ลงได้จริง คำนวณคะแนน และอธิบายต้นทุนของการค้นให้เห็นตรงหน้า

1

Anagram signature

SILENT และ LISTEN ถูกแปลงเป็นลายเซ็นความถี่ตัวอักษรเดียวกัน การสร้างลายเซ็นเป็น O(n) ส่วนการเปิด bucket ใน hash table เป็น O(1) โดยเฉลี่ย

2

Leave-one-out & subsets

สำหรับ BEGIN เราตัดตัวอักษรแบบ recursive แล้วค้น EGIN, BGIN, BEIN … จนถึง GIN หรือ IN เมื่อ rack มีเพียง 7 ตัว จำนวน subset จึงมีขอบเขตและเตรียมไว้ก่อนตรวจตำแหน่งได้

3

Board validation

การพบใน hash บอกเพียงว่าคำใดใช้ multiset ที่มีได้ Engine ยังต้องตรวจ anchor ทิศทาง ช่องโบนัส blank และคำตัดทุกคำก่อนถือว่าเป็น legal move

ทำไม Hash ยังแพ้ Trie ได้

เส้นทาง Hash ปัจจุบันต้องสร้าง signatures, query SQLite, แตก word buckets, สร้าง candidate Trie ชั่วคราว แล้วจึงเข้า board solver เดียวกัน ขณะที่ Packed Trie เริ่ม prune จาก anchor บนกระดานได้ทันที จึงมักชนะด้วย constant factor ที่ต่ำกว่า O(1) ในที่นี้หมายถึงการเปิด bucket หนึ่งครั้งโดยเฉลี่ย ไม่ใช่เวลาของการค้น Scrabble ทั้งกระบวนการ

สิ่งที่ Hash ทำได้ดีกว่าแล้ว

มันดึงกลุ่ม anagram ได้โดยตรง ทำให้ subset selection ตรวจสอบและสอนได้ง่าย และอาจชนะการไล่ candidate ของ Trie เมื่อ rack สร้างคำได้หลากหลาย จาก microbenchmark 1,000 รอบในเครื่องนี้ in-memory Hash เร็วกว่า 2.03× สำหรับ AEINRST แต่เพียง 1.27× สำหรับ CARTSAA; เมื่อรวมการตรวจตำแหน่งจริง Trie ยังอาจเร็วกว่า

แนวทางทำให้ Hash เร็วกว่าเดิม
  1. เก็บ anagram index ไว้ใน shared memory ตัด SQLite และการ deserialize ทุก request ออกจาก hot path
  2. เลิกสร้าง candidate Trie ชั่วคราว ใช้ bucket ของ word ID แล้วตรวจเฉพาะคำที่คืนมา
  3. precompute ลายเซ็น subset สูงสุด 127 แบบแบบ incremental และ cache ตาม canonical rack signature
  4. ทำ index สำหรับ pattern บนกระดานด้วย โดยรวม rack multiset, fixed letters, ความยาว และ cross-check mask ก่อน lookup
  5. ย้าย compact index และ placement loop ไปยัง Python/C++ service ที่รันค้างหรือ PHP extension แล้วแยก benchmark warm request ออกจาก startup

คลิกช่องแล้วพิมพ์ A-Z เพื่อสร้างโจทย์เอง ตัวพิมพ์เล็กในสถานะกระดานสงวนไว้แทน blank ที่วางแล้ว

ผลงานต่อยอดจาก CAPSTONE ของนักศึกษา

จากต้นแบบ Unity สู่ Teaching Engine ที่พร้อมใช้ทดลอง

ห้องทดลองเว็บนี้ต่อยอดแนวคิดที่นักศึกษา 4 คนร่วมบันทึกไว้: ทำให้ฝึก Scrabble ได้โดยไม่ต้องรอคู่เล่น ใช้ดัชนีคำเพื่อให้การตรวจคำตอบสนองรวดเร็ว และทำให้ตาของ AI ตรวจสอบได้เป็นลำดับตั้งแต่สร้าง candidate, validate, คิดคะแนน จนถึงเลือก move

ภาพสเก็ตช์นักศึกษาผู้มีส่วนร่วมทั้งสี่คน
01 · PROBLEM

ฝึกได้โดยไม่ต้องรอคู่เล่น

รายงานเริ่มจากปัญหาการเรียนรู้ที่จับต้องได้: คู่เล่นมีจำกัดทำให้ฝึกคำศัพท์ไม่ต่อเนื่อง บอทจึงเปลี่ยนเกมเป็นกิจกรรมฝึกภาษาอังกฤษที่เข้าใช้ได้ทุกเวลา

02 · DESIGN

Figma → Unity / C#

ทีมใช้ Figma ออกแบบ UI ร่วมกัน ก่อนพัฒนากระดาน ถุงตัวอักษร การลากวาง การตรวจคำ และคะแนนแบบ real-time เป็นเกม Unity/C#

03 · STATE MODEL

Validate → Score → End? → AI

Sequence model แยกคำผิดออกจากตาที่ถูกต้อง และให้ move ของผู้เล่นกับ AI ผ่าน validation, scoring และ game-end check ชุดเดียวกัน ซึ่งเป็น invariant ที่เว็บรุ่นนี้ยังรักษาไว้

04 · REPORTED EVALUATION

สัญญาณการเรียนรู้ที่ควรทดลองซ้ำ

แบบสอบถามเบื้องต้น 35 คนในรายงานนักศึกษาระบุว่า มากกว่า 80% มองว่าเกมช่วยคำศัพท์ใหม่ การจดจำ และความสนุก ผลนี้เป็นหลักฐานที่รายงานโดยโครงงาน ไม่ใช่ controlled trial อิสระ และเป็นแรงตั้งต้นให้ทดลองในชั้นเรียนแบบทำซ้ำได้

ที่มาของเนื้อหา: สรุปจากรายงาน Capstone ของนักศึกษาเรื่อง “เกมสแครบเบิล / Scrabble game” โดยจงใจไม่นำคำกล่าวด้านความเร็วมาใช้ซ้ำ จนกว่าเว็บรุ่นนี้จะวัดด้วยข้อมูลและ workload เดียวกันอย่างยุติธรรม