Regalia v1.0.7
What's Changed
Full Changelog: v1.0.6...v1.0.7
The source code files include an outdated README.md file along with the manuals. For detailed information about the relevant revisions (which encompass the manuals), please refer to the following:
Regalia v1.0.7 — Stability & Performance Maintenance Release
versionCode 107 · versionName "1.0.7" · 2026.6.28 – 2026.6.30
Stockfish 18 (arm64-v8a-dotprod) · Android 5.0+ (API 21) · arm64-v8a only
v1.0.7 is a code-quality and stability maintenance release based on three independent code-review reports + two comprehensive first-principles code-review passes (Phase 18/19) + two rounds of UX refinement (Phase 20/21). Across 21 phases, 52+ improvements were implemented. No new features were introduced, but numerous latent bugs were fixed, and the two remaining architectural suggestions from the Kimi audit report (LRU cache eviction + virtual list) were implemented.
📊 Release Overview
| Metric | Value |
|---|---|
| Total Phases | 21 |
| Fixes / Optimizations | 52+ |
| Code-Review Subagents | 7 (Phase 19) |
| Lines Reviewed | 28,281 |
| Critical Bug Fixes | 8 |
| Performance Breakthroughs | 3 |
| Compatibility | Xiaomi HyperOS 3 ✅ |
🎯 Key Highlights
⚡ Performance Breakthroughs
1. _reviewEvalCache LRU Eviction (Phase 18)
The old code commented "Unlimited cache size", arguing 60k entries ≈ 12MB was "negligible". First-principles analysis found three errors:
Old Claim Actual Reality
───────────────────────────── ─────────────────────────────
~200B per entry ~400B (JSON persistence overhead)
60k entries ≈ 12MB ≈ 24MB JSON
"negligible" main-thread block JSON.stringify blocks 100-300ms
localStorage quota ample WebView ~5MB → QuotaExceededError silently swallowed
Fix:
MAX_ENTRIES = 2000soft cap (~800KB JSON, well under any quota)_evictIfOverCap()evicts oldest entries by Map insertion order (= LRU order)get()/set()refresh LRU order via delete+set- Does NOT evict
_reviewEvalRequestedStep(the in-flight step) to avoid "analyzing..." flicker - Persistence preserves LRU order via
Array.from(m.entries()) - Backward-compatible: persisted files >2000 entries auto-evict on next
set()
2. Review Move-List Virtual List (Phase 18)
Threshold: 80 moves Overscan: 10 rows Debounce: 80ms
When move count > 80, windowed rendering is enabled — only the viewport + 10 rows of overscan above/below are rendered as DOM nodes; top/bottom spacer <div>s fill the total scroll height.
| Feature | Implementation |
|---|---|
| Shared render function | _buildReviewMovesInnerHTML(start, end) eliminates ~40 lines of landscape/portrait duplication |
| Scroll listener | Passive (passive:true), cannot block scrolling or interfere with scroll-restore |
| Partial refresh | _refreshReviewMovesOnly() replaces only .review-moves innerHTML |
| Dynamic row height | First render measures average height of first 10 rows via requestAnimationFrame |
| Window forcing | _forceReviewWindowToStep() ensures reviewGoTo() target is in window |
| State reset | _resetRvVirtualState() on enter/exit review, new game, import |
Performance: Not enabled under 80 moves (full render is faster for short lists); long-game render cost reduced from O(total moves) to O(visible + overscan).
3. Move Animation Slowed 30% (Phase 20, 120fps Preserved)
Piece Old Duration New Duration
─────────────────────────────────────────
Pawn 180ms 240ms
Knight 240ms 320ms
Bishop 210ms 280ms
Rook 180ms 240ms
Queen 260ms 340ms
King 210ms 280ms
- 120fps high-frame-rate preserved — GPU-composited via
will-change:transform+translate3d cubic-bezier(.25,.1,.25,1)easing curve unchanged; only duration is longer- CSS transition durations updated to match JS
durationstable
🐛 Critical Bug Fixes
Chess960 Castling "King Self-Capture" Fatal Bug (Phase 17)
Symptom: In Chess960 mode, when the king's starting position happens to be its castling destination square (e.g. an SP-ID where the white king starts on g1), castling caused the king to "capture itself" and vanish from the board.
Root cause: The makeMv/makeMvInPlace castling branch unconditionally executed:
ns.board[to.row][to.col] = ns.board[from.row][from.col]; // self-copy
ns.board[from.row][from.col] = null; // ← BUG: when from===to, this nulled the king's own squareFix: When castling AND from === to, skip the king move entirely (per the Fischer Random Chess "castling in place" rule). Also fixed unmakeMv and animateMove symmetrically. Added 20 Node-sandbox regression tests.
Chess960 unmakeMv Board Corruption (Phase 18, Critical)
Symptom: In ~half of all Chess960 SP-ID positions (e.g. white king on f1 castling kingside), the makeMvInPlace → unmakeMv round-trip corrupted the board.
Root cause: unmakeMv step 2 restores the king at f (king source), but the rook's destination cr.to may equal f (the rook moved to the king's old square) — step 2 already overwrote the rook with the king; step 4 s.board[cr.from] = s.board[cr.to] then read the king instead of the rook.
Fix: Save the rook piece itself in undo.castlingRook.piece, restoring from the saved piece rather than relying on board state.
_reviewEvalCache Corruption Race (Phase 19, Critical)
Symptom: requestEngineEval's cache-hit fast path and terminal-position fast path did not clear the pending debounce timer, allowing a stale debounce timer capturing a prior step's FEN to fire after the cache-hit return and overwrite the current step's correct cached eval.
Fix: Clear _reviewEvalDebounceTimer and increment _evalStaleGen on both fast paths to invalidate any in-flight callback.
Cross-Mode Stale Callback Race (Phase 19, Critical)
Symptom: onEngineEval's two filters are mode-exclusive; after exitReview, a review-mode callback still in flight could pass the normal-mode gen check and overwrite the correct eval for up to 30 seconds.
Fix: Capture _evalRequestReviewMode at request time; onEngineEval rejects cross-mode callbacks at the top.
PGN [%eval] Placeholder Offset (Phase 19, Critical)
Symptom: FEN-start PGNs where Black is to move had evals attach to the wrong reviewStep (off by 1).
Root cause: _parsePGN was unaware of the null placeholder that importPGN prepends after parsing.
Fix: Apply _placeholderOffset when populating the eval/annotation/comment caches.
StockfishNative Thread Safety (Phase 19)
Five fields changed from non-volatile to volatile:
private volatile Process engineProcess;
private volatile BufferedReader engineReader;
private volatile OutputStreamWriter engineWriter;
private volatile Thread readerThread;
private volatile ExecutorService _engineExecutor;Root cause: These fields are written from multiple threads (_engineExecutor worker, heartbeat thread, JS binder) without volatile/synchronization. A read on one thread may never observe a null write from another, producing NPEs or writes to a destroyed process's stream.
stopAndWaitForBestmove Timeout Stale-Bestmove Corruption (Phase 19)
Symptom: When the engine didn't respond to "stop" within 1 second, the late bestmove was processed as a real move, potentially generating an AI move for the wrong position.
Fix: On timeout, set _discardingPonderBestmove = true to discard the late bestmove.
ChessApp UncaughtExceptionHandler Swallowed Error (Phase 19)
Symptom: Worker-thread Errors (OOM/StackOverflow/NoClassDefFoundError) were silently swallowed, leaving the JVM in a corrupted state — e.g. if the SF-Reader thread died from OOM, the engine silently stopped responding while isProcessAlive() still returned true, causing a 30-120s apparent hang.
Fix: Error subclasses are now chained to the default handler so the process dies cleanly and restarts.
🎨 UX Refinements (Phase 20 + 21)
Review Eval Bar / Analyze Button Enlarged (Phase 20)
| Element | Old | New |
|---|---|---|
| Eval bar font (portrait/landscape) | .75rem / .7rem | .85rem / .8rem |
| Emoji size | .95rem | 1.1rem / 1.05rem |
| max-height | 1.9em | 2.4em |
| padding | 3px 8px | 5px 10px |
| Analyze button font | .7rem | .8rem |
| Analyze button min-height | 28px | 34px |
Text and emoji now display fully with comfortable breathing room — no longer cramped.
Portrait Dialog Positioning Precision Fix (Phase 21)
Phase 20 attempt → Phase 21 precision fix:
Phase 20: padding-top:18vh + padding-bottom:10px
→ center = 18 + (100-18-1)/2 ≈ 59vh ❌ BELOW center
Phase 21: padding-top:10px + padding-bottom:12vh
→ center = 10px + (100vh-10px-12vh)/2 ≈ 44vh ✅ slightly above center
The fullscreen dialog (New Game Settings) uses negative margins to cancel both paddings, maintaining true full-screen fill.
Landscape Anti-Shake Clipping Root-Cause Fix (Phase 21)
Phase 20 attempt (.bsec margin-right:10px) was ineffective — the clip happens at .bsec's own overflow:hidden boundary, not at the gap.
Phase 21 triple fix:
- Rolled back
margin-right:10px .bsecchanged tooverflow-x:visible; overflow-y:hidden_antiShakenow reserves 8px in BOTH orientations (isLandscape?0:6→8, matchingMAX_DISPLACEMENT_PX=8.0f)
Landscape Move-History Panel Clipping Fix (Phase 21)
.panel now has overflow-x:auto, so wide content scrolls within the panel instead of being clipped by .main's overflow-x:hidden.
📋 All 21 Phases at a Glance
| Phase | Date | Theme | Key Content |
|---|---|---|---|
| Phase 1 | 6.28 | Code-review bug fixes | Critical-move cache, lightweight update, notification throttle, XSS, CSP, cross-game cache |
| Phase 2 | 6.28 | Quick Toolbar + setup markers | Main-screen Quick Toolbar, 🔁/⚡ manual marker buttons |
| Phase 3 | 6.28 | Portrait UI redesign | @media(orientation:portrait) no width threshold + vw/clamp() |
| Phase 4 | 6.29 | Chess960/X-FEN rule correctness | findCastlingRooks, Shredder-FEN, UCI_Chess960 |
| Phase 5 | 6.29 | Board sizing & layout optimization | _recalcCellSize first-principles rewrite |
| Phase 6–8 | 6.29 | Board sizing & layout optimization | Landscape right-edge, dialogs, setup markers |
| Phase 9–11 | 6.29 | ⚡ marker position anomaly never existed | |
| Phase 12 | 6.29 | ⚡ entering-setup-mode root-cause fix | enPassantTarget reverse-mapping |
| Phase 13 | 6.29 | Misreport cleanup + code review | Removed defensive guards, root-cause fix |
| Phase 14 | 6.29 | Eval bar font enlargement + chart sizing | .62→.75rem, min 120px / max 200px |
| Phase 15 | 6.29 | Analyze All includes step 0 | _totalSteps = moveRecords.length + 1 |
| Phase 16 | 6.29 | Final code review | PGN [%eval] off-by-one + redundancy cleanup |
| Phase 17 | 6.29 | Chess960 castling + review button + Kimi 5 | King self-capture fix, analyze-all auto-refresh |
| Phase 18 | 6.30 | LRU eviction + virtual list + 11 fixes | Two architectural suggestions implemented |
| Phase 19 | 6.30 | 7-subagent 28k-line comprehensive review | 20+ critical fixes |
| Phase 20 | 6.30 | UX refinements | Eval bar enlarged, animation slowed, dialog positioning |
| Phase 21 | 6.30 | Bug fixes | Dialog positioning precision fix, anti-shake root-cause fix |
🔍 Phase 19 Comprehensive Code Review Details
7 subagents reviewed all 20 source files (28,281 lines) in parallel:
┌─────────────────────────────────────────────────────────┐
│ Subagent 1: game-logic.js + chess960.js (3,005 lines)│
│ Subagent 2: ai-bridge.js (3,824 lines)│
│ Subagent 3: ui.js lines 1-2000 │
│ Subagent 4: ui.js lines 2000-4000 │
│ Subagent 5: ui.js lines 4000-5975 │
│ Subagent 6: tablebase.js + pgn-standard.js + worker-pool│
│ Subagent 7: index.html.tpl + stats.html + 9 Java + JNI │
└─────────────────────────────────────────────────────────┘
Total 28,281 lines, line-by-line first-principles analysis
20+ critical fixes implemented by priority: bug fix > functionality > performance > redundancy cleanup > simplification.
🛡️ Security & Compliance
stats.html CSP Added (Phase 19)
The stats page renders user-pasted PGN content but previously had no Content Security Policy. A CSP matching chess.html has been added:
Content-Security-Policy: default-src 'none';
script-src 'unsafe-inline';
style-src 'unsafe-inline';
img-src data:;
connect-src 'none';
frame-ancestors 'none';
base-uri 'self';File Header License Notice Completed (Phase 19 supplement)
stats.html was missing the full Copyright + GPL v3 license block (only had the AI-GEN line in the <style> comment). A complete HTML comment license block matching index.html.tpl's style has been added to <head>.
📁 Project Structure
Regalia/
├── src/main/
│ ├── assets/
│ │ ├── chess.src/ # Source files (JS + CSS + HTML template)
│ │ │ ├── game-logic.js # Chess rules, move generation, i18n, castling detection
│ │ │ ├── chess960.js # Chess960 SP-ID, Shredder-FEN, 960 castling rules
│ │ │ ├── pgn-standard.js # Standardized PGN encoder/decoder, NAG, [%csl]/[%cal]
│ │ │ ├── worker-pool.js # Web Worker pool
│ │ │ ├── ai-bridge.js # Engine communication, eval display, PGN export
│ │ │ ├── ui.js # Rendering, dialogs, interaction, review mode
│ │ │ ├── eco-data.js # ECO opening classification data
│ │ │ ├── tablebase.js # Syzygy tablebase + PGN import
│ │ │ ├── index.html.tpl # CSS template
│ │ │ └── build-chess.sh # Build script → chess.html
│ │ ├── chess.html # Built output
│ │ ├── stats.html # Statistics page
│ │ └── *.svg # License logos
│ ├── java/com/Regalia/ # Java source files (9 files)
│ ├── cpp/engine_jni.cpp # JNI bridge
│ └── AndroidManifest.xml
├── Manual/ # User manuals (Chinese & English, 4 versions)
├── NOTICE # Third-party component notices
├── LICENSE-AGPL v3 # AGPL v3 full text
├── LICENSE-GPL v3 # GPL v3 full text
├── LICENSE-Apache v2.0 # Apache v2.0 full text
├── README.md # Project readme
└── PRIVACY.md # Privacy policy
📥 Downloads
| File | Size | Description |
|---|---|---|
Regalia-v1.0.7-release.apk |
74 MB | Release APK, v1+v2+v3 triple signed |
Regalia-v1.0.7-manual-zh.html |
505 KB | Chinese user manual |
Regalia-v1.0.7-manual-en.html |
597 KB | English user manual |
Installation Requirements
- Architecture: arm64-v8a only
- OS: Android 5.0 (API 21)+
- Target SDK: 35 (Android 15)
- Compatibility: Xiaomi HyperOS 3 ✅
Build Verification
✅ APK signature v1 (JAR signing): true
✅ APK signature v2 (APK Signature Scheme v2): true
✅ APK signature v3 (APK Signature Scheme v3): true
✅ chess.html JS syntax validation passed
✅ tar package contains no engine files (98 files)
✅ Phase 18 feature tests all passed (28 PASS)
📜 License
Regalia is a dual-licensed combined work:
| Component | License | Source |
|---|---|---|
| Application as a whole | AGPL v3 | Original code |
| DroidFish-derived code | GPL v3 | game-logic.js, ai-bridge.js, ui.js, StockfishNative.java, engine_jni.cpp |
| Stockfish 18 | GPL v3 | Engine binary |
| Gradle | Apache v2.0 | Build tooling |
AI-GEN Declaration: All source files are marked
// AI-GEN: AI assisted, indicating the code was AI-assisted and has been reviewed for license compliance.
🙏 Acknowledgements
- DroidFish — Peter Österlund's Android chess app; this project's engine management, PGN parsing, and SAN notation logic are derived from it
- Stockfish — The world-class open-source chess engine
- lichess-org/chess-openings — ECO opening classification data (CC0)
- Kimi Audit Report — Provided 30+ suggestions; 5 were adopted in Phase 17, and 2 architectural suggestions were implemented in Phase 18
🔄 Full Changelog
The changelog is ordered newest-to-oldest. See:
- 📖 Chinese manual:
Regalia-v1.0.7-manual-zh.htmlAppendix A - 📖 English manual:
Regalia-v1.0.7-manual-en.htmlAppendix A - 📋 NOTICE file: License classification and per-phase structural summary
- 📋 README.md: Complete 52+ itemized update records
🎉 v1.0.7 release complete! Thank you for using Regalia.
AI-GEN