Regalia v1.0.6
What's Changed
Full Changelog: v1.0.5...v1.0.6
Regalia v1.0.6 — Release Notes
Version:
v1.0.6(versionCode 106)
Engine: Stockfish 18arm64-v8a-dotprod
Platform: Android 5.0+ (API 21) · ARM64 · Xiaomi HyperOS 3 compatible
License: AGPL v3 (application) + GPL v3 (DroidFish-derived code & Stockfish engine)
📥 Download
| File | Description |
|---|---|
Regalia-v1.0.6-release.apk |
Release APK (v1 + v2 + v3 signed) |
Regalia-v1.0.6-manual-zh.html |
Chinese user manual |
Regalia-v1.0.6-manual-en.html |
English user manual |
⚠️ Enable "Install from unknown sources" in system settings before installing.
🆕 What's New in v1.0.6
v1.0.6 is a feature-enhancement release with 10 major new features and 4 rounds of bug fixes (including critical Chess960 castling fixes).
♟️ Chess960 Improvements
ECO Opening Recognition Suppression
Chess960 (Fischer Random Chess) has no fixed opening theory. When Chess960 mode is active, the in-game ECO Opening info panel and Opening Recommendation bar are now fully suppressed — no more meaningless ECO lookups for non-standard start positions.
// game-logic.js — _startGameImpl()
_ecoEnabled = !(typeof dlgChess960 !== 'undefined' && dlgChess960);
// ui.js — ECO panels gated by gameVariant check
if (_ecoEnabled && !(gameVariant === 'chess960')) { /* show ECO panel */ }Chess960 Castling Detection Overhaul
Replaced the legacy Math.abs(to.col - from.col) === 2 pattern (which only works for standard chess) with an explicit castle flag on castling moves plus a unified _castleSide() helper:
| Detection Method | Standard Chess | Chess960 (1-col king move) |
|---|---|---|
Legacy === 2 |
✅ | ❌ |
mv.castle flag |
✅ | ✅ |
| Fallback (dest col + distance ≥ 2) | ✅ | ✅ (2+ col moves) |
Files updated: pseudoMoves(), makeMv(), makeMvInPlace(), moveAlg(), animateMove(), _uciToSimple(), tablebase.js applySANMove(), stats.html executeMove()
🏰 King-then-Rook Castling Gesture
A new castling operation method — essential for Chess960 where some positions can only be castled this way.
How it works:
- Select a king that can castle
- The castling-capable rook(s) are visually marked with a golden dashed ring (with pulse animation)
- Click the marked rook → castling executes instantly
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ ♖ │ │ │ ♔ │ │ │ │ ♖ │ ← Standard: king e1, rooks a1/h1
└───┴───┴───┴───┴───┴───┴───┴───┘
└──┐
▼ Select king → rook gets golden ring
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ ♖ │ │ │ ╔═╗ │ │ │ ╔═╗ │ ♖ │ ← Click the ringed rook
└───┴───┴───┴───┴═╤═╩───┴───┴═╤═┘
└─ king→g1, rook→f1
Why it matters: In Chess960, when the rook's source square is the king's destination (e.g. rook on g1, king castles kingside to g1), the traditional "click the king's destination" gesture is ambiguous. The king-then-rook gesture resolves this.
🔧 Implementation Details
_computeCastlingRookSetForSelection(selPos, moves)— pure helper, no global state mutation_updateChangedSquares()— lightweight update path now syncs the marker (was only in full render)castle-ringCSS — golden dashed border +castlePulsekeyframe animation_getCastlingRookForClick()— click handler detects rook clicks and triggers castling
📊 Stats Page Per-Move Selection
Click any move or the initial FEN in the stats page's PGN Text panel to instantly view:
- The board position after that move (or the initial position)
- All statistics computed up to that move only — not including the effect of later moves
- Material balance, phase, pawn structure, eval trend, move quality distribution
When nothing is selected, the final complete statistics are shown.
🔄 PGN [SetUp]/[FEN] Round-Trip Preservation
Before: Importing a PGN with [SetUp "1"] and [FEN "..."] headers, then exporting, would drop both headers.
After: importPGN() now correctly assigns result.startFEN to _setupFEN, so _buildPGNString() regenerates both headers on export.
[SetUp "1"]
[FEN "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"]
🎨 Unified Gray-Out Styling
When a toggle or stepper is grayed out (disabled), only the control itself is dimmed. Surrounding labels, descriptions, and explanations remain at full opacity.
| Element | Before | After |
|---|---|---|
| Toggle switch | Dimmed | Dimmed |
| Section heading | Dimmed | Full opacity |
| Description text | Dimmed | Full opacity |
| Explanation paragraph | Dimmed | Full opacity |
| Gray-out reason | Absent | New (red, same color as Engine Config warning) |
New gray-out explanations:
- "Chess960 is unavailable while AI Opening Book is enabled"
- "AI Opening Book is unavailable while Chess960 is enabled"
📱 Portrait-Optimized New Game Dialog
The New Game Settings dialog now has dedicated portrait-mode CSS while preserving the landscape layout:
| Property | Landscape | Portrait (<900px) | Narrow Portrait (<480px) |
|---|---|---|---|
| Max width | 500px | 460px | 96% |
| Padding | 24px | 18px 16px | 14px 12px |
| Time control input | 80px | 80px | 70px |
| ECO search row | Inline | Wrap | Stack vertically |
📜 Scroll-Position Preservation
Fixed a bug where scrollable windows would suddenly jump back to the top after a state change triggered a re-render.
Affected containers:
.dlg— dialogs (New Game, Engine Config, Import, About).panel— right-side info panel (landscape).review-moves— review mode move list.op-list— opening selectorstats.html#content— full document scroll
Mechanism: _savedContainerScrolls array captures scrollTop before app.innerHTML = h and restores it after (clamped to new scrollHeight).
⚙️ Engine Evaluation FEN Sanitization
Added _sanitizeFenForEngine() — strips inconsistent castling rights before sending to Stockfish.
Problem: The FEN 4k3/8/8/8/8/8/8/r1K4R w q - 1 3 has black queenside castling right (q) but no black rook on a8. Stockfish would enter a degraded state — no info lines, no bestmove, hanging until the 30s safety timer fired, then retrying indefinitely.
Solution:
function _sanitizeFenForEngine(fen) {
// Parse board, validate each castling right against actual piece placement
// K → white king on e1 AND white rook on h1
// Q → white king on e1 AND white rook on a1
// k → black king on e8 AND black rook on h8
// q → black king on e8 AND black rook on a8
// Shredder-FEN (Chess960) is passed through unchanged
}Applied to all engineEval, engineEvalDeep, engineHint, engineGo, engineGoTimed, and startPonder calls.
🏅 SL Mode Skill-Level Display
When the AI opponent is in SL (Skill Level) mode, the actual skill level value is now shown:
| Location | Before | After |
|---|---|---|
| AI opponent bar | SL |
SL20 |
| Top toolbar button | SL |
SL20 |
PGN [Black ""] tag |
AI Opponent SL |
AI Opponent SL20 |
New Java bridge method: getEngineSkillLevel() returns the current engineSkillLevel int.
🐛 Bug Fixes
Round 1 — Initial v1.0.6 Release
- White screen on launch — A backtick in a comment inside the
_WORKER_SOURCEtemplate literal (worker-pool.js) prematurely terminated the string, causing aSyntaxErrorthat blocked the entire script. Fixed by removing the backtick. executeMove()lostcastleflag — Themvobject was built as{from, to, piece, promotion}without copyingcastlefromlegalMvs, breaking Chess960 castling detection. Fixed by looking up the matching legal move._computeAndCacheVisualAnnotations()replay path — Samecastleflag loss during move replay. Fixed by restoring frommoveRecords.isCastling.
Round 2 — Castling Root Cause
executeMove()castle flag propagation — Comprehensive fix ensuringcastleflag flows fromlegalMvs→mv→makeMv()/moveAlg()/_castleSide()._castleSide()fallback logic — Improved to use destination column + distance (≥2 cols) instead of just=== 2, covering 2+ col Chess960 castling._uciToSimple()Chess960 detection — Simplified to distance-based (≥2 cols) to avoid 1-col ambiguity with normal king moves._sanitizeFenForEngine()Shredder detection — Simplified from double-regex to single/^[KQkq-]+$/._finishAnim()cleanup — Now clears_lastAnimMvto avoid stale references.
Round 3 — Chess960 Rook Capture & Analyze-All Off-by-One
-
Chess960 own rook captured during castling — In Chess960, when the rook's source square equals the king's destination (e.g. rook on g1, king castles kingside to g1),
makeMv()/makeMvInPlace()moved the king first (overwriting the rook), then tried to move the rook — but it was already gone.Fix: Detect castling before moving the king, save the rook piece reference, move the king, then place the saved rook at its destination. Also fixed
stats.html executeMove()(review mode replay).Test verification
Before: King e1, Rook g1 (Chess960) Kingside castling: king → g1, rook → f1 makeMv() result: g1 (col 6): ♔ (king) ✅ f1 (col 5): ♖ (rook) ✅ e1 (col 4): empty ✅ -
"Analysis complete" never displayed —
_reviewEvalCacheis keyed byreviewStep(1-based), so cache size =moveRecords.length. But the check compared_reviewEvalCache.size >= reviewStates.length, andreviewStates.length = moveRecords.length + 1(includes initial position at index 0). This off-by-one meant the comparison was alwaysN >= N+1→ false.Fix: Three coordinated changes:
- Analyze button:
_totalSteps = moveRecords.length reviewAnalyzeAll(): iterate1..moveRecords.length_reviewAnalyzeAdvance(): terminate whennextStep > moveRecords.length
- Analyze button:
Round 4 — Castling Rook Marker Display
- Marker didn't appear on king selection —
_updateChangedSquares()(the lightweight update path) didn't includecastle-ringmarker logic — it was only inrenderInternal(). - Marker got stuck after appearing — Same root cause: the lightweight path didn't remove the marker when selection changed.
- Fix: New pure helper
_computeCastlingRookSetForSelection()computes the set without mutating globals._updateChangedSquares()now computes both current and previous sets, includes both in the update list, and correctly adds/removes thecastle-rookclass +castle-ringdiv.
⚡ Performance Optimizations
- Dead code removal —
_getCastlingRookSquares()removed (40 lines). It was redundant afterrenderInternalswitched to_computeCastlingRookSetForSelectiondirectly, and contained a wasteful double-computation ofchess960CastlingRookMove(). _updateChangedSquares()castling-set computation — EliminatedlegalMoves()recomputation for the previous selection by deriving castling rooks from the already-tracked_prevLegalSet(Set ofrow*8+colkeys). If the previous piece was a king and_prevLegalSetcontains col 6 or col 2, castling was legal — no need to re-run the full move generator.- Zobrist hash verification — Confirmed correct for the Chess960 edge case where rook source === king destination (XOR commutativity ensures correctness regardless of operation order).
🏗️ Technical Architecture
Regalia v1.0.6
├── Java Layer (Android Native)
│ ├── MainActivity.java — WebView host, immersive mode, lifecycle
│ ├── StabilizationHelper.java — Sensor-fusion board anti-shake (v1.0.5)
│ ├── StockfishNative.java — Engine process, UCI, PGN cache, getEngineSkillLevel() (v1.0.6)
│ ├── StatsActivity.java — Statistics page fullscreen WebView
│ ├── EngineService.java — Foreground service (engine stability)
│ ├── ChessApp.java — Crash protection
│ ├── TlsSecurityHelper.java — TLS 1.2+ enforcement
│ └── RootDetector.java — Informational root detection
├── JS Layer (WebView)
│ ├── game-logic.js — Chess rules, _castleSide(), makeMv rook-save fix
│ ├── chess960.js — SP-ID, Shredder-FEN, 960 castling helpers
│ ├── pgn-standard.js — Standardized PGN codec, NAG, [%csl]/[%cal]
│ ├── worker-pool.js — Web Worker pool (PGN/stats/control-map)
│ ├── ai-bridge.js — Engine comms, _sanitizeFenForEngine(), SL display
│ ├── tablebase.js — Syzygy tablebase + PGN import (FEN round-trip fix)
│ ├── ui.js — Rendering, castling gesture, scroll preservation
│ ├── eco-data.js — ECO opening data
│ └── index.html.tpl — CSS (portrait dialog, castle-ring animation)
├── C++ Layer
│ └── engine_jni.cpp — JNI chmod/renice (from DroidFish)
└── Engine
└── Stockfish 18 arm64-v8a-dotprod (NEON + dotprod)
📦 Deliverables
| Artifact | Size | Description |
|---|---|---|
Regalia-v1.0.6-release.apk |
~77 MB | Signed release APK (v1+v2+v3) |
Regalia-v1.0.6-manual-zh.html |
~382 KB | Chinese manual (with v1.0.6 diagrams) |
Regalia-v1.0.6-manual-en.html |
~466 KB | English manual (synced) |
🔄 Upgrading from v1.0.5
v1.0.6 is fully compatible with v1.0.5's PGN archives and evaluation caches. Install directly over the existing app — no uninstall needed.
📂 PGN cache archives and evaluation caches are preserved across the upgrade.
📜 License
| Component | License |
|---|---|
| Original application code (UI, WebView, services, build scripts) | AGPL v3 |
| DroidFish-derived code (engine management, game logic, PGN parsing, UI patterns) | GPL v3 |
| Stockfish 18 engine binary | GPL v3 |
| ECO opening data | CC0 (data) / AGPL v3 (code) |
| App icon | AI-generated / AGPL v3 |
GPL v3 Section 13 permits this combination of licenses. AGPL v3's network interaction provisions (Section 13) apply to the entire combined work.
🙏 Acknowledgments
- Stockfish — World-class chess engine (Stockfish 18)
- DroidFish — Engine management, game logic, PGN parsing, UI patterns
- Lichess Tablebase API — 7-piece endgame tablebase queries
- lichess-org/chess-openings — ECO opening classification data (CC0)
📌 Known Limitations
- Syzygy Tablebase — Requires network connection; automatically disabled offline.
- Chess960 castling — Fully supported in v1.0.6 (previous off-by-one and rook-capture bugs fixed).
Regalia uses "Regalia" solely as a project name. No trademark rights are claimed. Anyone is free to fork and rename their own version.
AI-generated