Skip to content

Commit 511d17d

Browse files
Merge pull request #1 from 1902325620-del/agent/morris-ai-v2.1.0-alpha.3
Release 2.1.0-alpha.3 with stronger Morris placement AI
2 parents 757c733 + ad26e0c commit 511d17d

40 files changed

Lines changed: 6526 additions & 137 deletions

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ jobs:
2020
with:
2121
node-version: 22.13.0
2222
cache: pnpm
23+
- name: Install Rust 1.97.0 with Wasm target
24+
run: rustup toolchain install 1.97.0 --profile minimal --target wasm32-unknown-unknown
2325
- run: pnpm install --frozen-lockfile
26+
- name: Test native Morris engine
27+
run: cargo test --manifest-path morris-engine/Cargo.toml
28+
- run: pnpm run check:wasm
2429
- run: pnpm run lint
2530
- run: pnpm exec tsc --noEmit
2631
- run: pnpm test

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,6 @@ next-env.d.ts
4949
/launcher/web/
5050
/launcher/bin/
5151
/launcher/obj/
52+
53+
# generated Rust/Wasm build products
54+
/morris-engine/target/

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,49 @@
22

33
本项目采用语义化版本号。日期使用 `YYYY-MM-DD` 格式。
44

5+
## [2.1.0-alpha.3] - 2026-07-13
6+
7+
### 九子棋落子评估修正
8+
9+
- 通过只读运行时观察确认,游戏对手并没有大型开局棋谱;其核心是低深度迭代加深 Negamax/Alpha-Beta、置换表和一个很小的局面评估器。本项目只复现可观察的算法行为,不复制、注入或分发游戏代码。
10+
- 修复本地引擎落子阶段忽略真实活动空间的问题。摆子评分现在以总子数差、已落棋子的空邻接边差和已成磨差为稳定基线,权重分别为 `500/100/80`;TypeScript 回退路径同步采用相同基线。
11+
- 保留强制封堵、成磨取子和不同落点双威胁的有限战术延伸,但不再把同一落点上的两条线误算成两个可执行威胁。
12+
- 落子期只计算一个首选候选,把预算集中到主变化;在尚未搜到全部落子前按完整双方回合推进偶数层,避免短时限下因奇偶深度产生大幅摇摆。
13+
- 已复现局面在 2 秒、8 秒与 20 秒基准中稳定推荐 `@b4`;三档仍未穷尽剩余 19 层目标,因此这是针对主要算法缺陷的测试版修正,不宣称已经求得必胜着。
14+
- 游戏内小范围验收中,程序无论先手或后手均战胜内置对手;该结果用于确认针对性改进有效,仍不表述为九子棋必胜或完美求解。
15+
16+
## [2.1.0-alpha.2] - 2026-07-13
17+
18+
### 九子棋诊断棋谱
19+
20+
- 测试版新增完整九子棋棋谱复制功能,记录先手、强度、逐回合落点、取子、当前棋盘和待摆棋子。
21+
- 每次我方搜索同时记录实际使用的 WebAssembly 或 TypeScript 后端、完成深度、节点数、评分、候选主变化,以及是否搜到完整落子终点,便于定位手机端静默回退或开局搜索未收敛问题。
22+
- 明确当前版本没有预计算开局库或完美对局数据库;APK 体积主要来自界面和 Android 运行组件,不能代表棋力。后续开局增强将以可复现棋谱和独立基准为准,不再仅凭局面权重试错。
23+
24+
## [2.1.0-alpha.1] - 2026-07-13
25+
26+
### 莫里斯九子棋独立引擎测试版
27+
28+
- 移除实测未能改善胜率的《刺客信条 IV:黑旗》对手模型与针对性对策路径;新版九子棋引擎不读取、复制或依赖游戏 AI 文件。
29+
- 新增以 MIT 协议开源的独立 Rust/WebAssembly 九子棋核心,与迂棋、西洋跳棋规则和搜索代码完全分离;WebAssembly 不可用或校验失败时会永久回退到原 TypeScript 引擎完成当前运行会话。
30+
- 落子阶段使用迭代加深 PVS/Alpha-Beta 搜索、16 种棋盘对称置换表,以及成磨、强制封堵、双重威胁和连接点走法排序。
31+
- 搜索目标覆盖剩余落子,并在完成落子后继续验证 4 ply 移动阶段;该目标仍受用户所选思考时间限制,结果会明确记录是否完整完成。
32+
- 开局通常有 18 次落子尚未完成,2 秒思考时间无法完整穷举全部落子及后续变化。本版本是增强测试版,不是九子棋完美解,也不保证每一局必胜。
33+
- Rust 核心不引入第三方 crate;引擎、界面和回退逻辑均在本机离线运行,不联网、不调用 AI 服务且不消耗 Token。
34+
35+
## [2.0.1-rc.1] - 2026-07-13
36+
37+
### 莫里斯九子棋 AI 增强
38+
39+
- 修复三档强度过早撞到固定深度上限的问题;快速、强力与复仇档现在会由各自的思考时间主导迭代加深,不再只计算几十到几百毫秒便提前结束。
40+
- 将评估改为分阶段模型:摆子阶段加入真实可达的成磨行动、多点威胁、先手节奏与连接点控制;走子和飞子阶段重新平衡子力、封锁与机动性。
41+
- 修复三枚棋子进入飞行阶段后机动分骤增、可能把“被吃到只剩三子”误判为有利的评分问题。
42+
- 搜索加入成磨吃子的有限战术延伸、PVS、历史/杀手走法排序、16 种棋盘对称置换表与静态评估缓存,并规范化置换表中的距杀分数。
43+
- 搜索现在会结合真实对局历史识别即将造成的三次重复;和棋只带 `-1` 的极轻微求胜偏好,既会尝试更好的非重复路线,也不会为了求变而选择明显更差的局面。
44+
- 根节点只对可能进入前三名的候选执行完整重搜,将更多思考预算留给真正有竞争力的棋路。
45+
- 新增后手关键防守、飞行阶段评分与敌我换色对称回归测试;已确认规则和 SELF/OPPONENT 映射不存在偏袒先手或后手的硬编码。
46+
- 在已复现的后手关键局面中,旧快速档在约 31ms/5 层时误选 `@f6`;新版在同深度及完整 2 秒搜索中均改为深层参考一致的 `@d6`,完整快速搜索可达到 10 层。
47+
548
## [2.0.0] - 2026-07-12
649

750
### 正式发布

README.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,24 @@
2727

2828
- **迂棋 9×5**:必吃、撞吃、拖吃、连续吃子与可选停止。
2929
- **英式西洋跳棋 8×8**:有吃必吃、连续跳吃、单格王棋与升王。游戏使用的不是 10×10 国际跳棋规则。
30-
- **莫里斯九子棋**:摆子、成磨取子、相邻走子、三子飞行与终局判定。
30+
- **莫里斯九子棋**:摆子、成磨取子、相邻走子、三子飞行与终局判定;推荐棋路由独立 Rust/WebAssembly 引擎计算,异常时回退到 TypeScript 引擎
3131

3232
### 主要功能
3333

34-
- 三种棋均使用本地迭代加深 Alpha-Beta 搜索,并提供前三候选棋路
34+
- 迂棋与西洋跳棋使用各自的本地搜索;九子棋使用独立的迭代加深 PVS/Alpha-Beta 核心。三种棋均可提供最多三个候选棋路
3535
- 简体中文与英文界面,顶部可快速切换棋类。
3636
- 默认快速搜索,也可选择强力或复仇强度。
3737
- 支持整回合撤销、任意局面编辑、先手切换和棋子颜色自动互换。
3838
- 西洋跳棋可快速取消或切换所选棋子,并明确提示强制吃子与连续跳吃限制。
3939
- Android 适配状态栏、摄像头开孔和底部导航安全区,并提供底部快捷确认与终局提示。
4040
- Windows 便携版只在 `127.0.0.1` 启动本机页面,不对外提供服务。
4141

42+
### 九子棋引擎说明
43+
44+
`2.1.0-alpha.3` 的只读行为审计确认,《黑旗》的九子棋对手并没有可检测的大型开局棋谱,而是使用低深度 Negamax/Alpha-Beta、置换表,以及总子数、空邻接边和已成磨三项评分。项目只依据可观察行为重新实现通用算法,不复制、注入、打包或依赖 Ubisoft 的代码与数据。
45+
46+
当前独立 Rust/WebAssembly 引擎在落子期采用同量纲的稳定评分基线,并额外使用 16 种棋盘对称置换表、战术走法排序、强制封堵与有限战术延伸。尚未搜索到全部落子时只保留完整双方回合的偶数层结果,避免短时限下的奇偶层摇摆;同时只计算一个首选落子,把预算集中到主变化。在时间允许时,搜索目标仍会覆盖所有剩余落子并继续验证 4 ply 移动阶段。开局共有 18 次落子,常用的 2 秒预算无法穷举全部变化,因此这是针对已确认缺陷的增强测试版,不是完美解,也不保证必胜。WebAssembly 无法加载或校验失败时,程序会回退到采用相同落子基线的 TypeScript 引擎;两种路径都完全离线,不联网且不消耗 Token。
47+
4248
### 开发说明
4349

4450
本项目主要由 GPT-5.6 AI 在项目发起者的需求设计、实机测试、规则反馈和最终决策指导下开发,包括规则引擎、搜索算法、Web 界面、Windows 便携版与 Android 版本。AI 生成代码已经过自动化测试和实际操作验证,但仍欢迎玩家复核棋规与改进搜索质量。
@@ -54,6 +60,14 @@ pnpm install
5460
pnpm dev
5561
```
5662

63+
若要从源码重新编译九子棋 WebAssembly,还需要通过 rustup 安装项目锁定的 Rust 1.97.0,并添加 `wasm32-unknown-unknown` 目标:
64+
65+
```bash
66+
rustup target add wasm32-unknown-unknown
67+
pnpm run build:wasm
68+
pnpm run check:wasm
69+
```
70+
5771
验证与构建:
5872

5973
```bash
@@ -99,18 +113,24 @@ The builds are not signed with a commercial code-signing certificate, so Windows
99113

100114
- **Fanorona 9x5**: mandatory captures, approach and withdrawal captures, capture chains, and optional stopping.
101115
- **English Draughts 8x8**: mandatory captures, multiple jumps, single-step kings, and promotion. This is not 10x10 International Draughts.
102-
- **Nine Men's Morris**: placement, mills and removals, adjacent movement, flying with three pieces, and terminal-state detection.
116+
- **Nine Men's Morris**: placement, mills and removals, adjacent movement, flying with three pieces, and terminal-state detection; recommendations use an independent Rust/WebAssembly engine with a TypeScript fallback.
103117

104118
### Highlights
105119

106-
- Local iterative-deepening Alpha-Beta search with three ranked candidates for every game.
120+
- Separate local search engines for Fanorona and draughts, plus an independent iterative-deepening PVS/Alpha-Beta core for Morris. Each game can return up to three ranked candidates.
107121
- Complete Simplified Chinese and English interface with a top game switcher.
108122
- Quick search by default, with Strong and Revenge presets available.
109123
- Full-turn undo, position editing, first-player switching, and automatic piece-color swapping.
110124
- Fast draughts piece selection with clear feedback for mandatory captures and unfinished multiple jumps.
111125
- Android safe-area handling for status bars, display cutouts, and navigation bars, plus quick confirmation and game-over prompts.
112126
- The Windows build only serves the embedded app on `127.0.0.1` and never exposes it to the network.
113127

128+
### About the Morris engine
129+
130+
The read-only behavioral audit behind `2.1.0-alpha.3` found no detectable large opening book in the Black Flag Morris opponent. It uses a shallow Negamax/Alpha-Beta search, a transposition table, and a compact score based on total material, empty adjacent edges, and completed mills. This project independently reimplements only observable, general algorithmic behavior; it does not copy, inject, bundle, or depend on Ubisoft code or data.
131+
132+
The independent Rust/WebAssembly engine now uses a placement baseline on the same scale, plus 16-way board-symmetry caching, tactical move ordering, forced blocks, and bounded tactical extensions. Until search reaches the end of placement, it retains balanced even-ply results and calculates only one principal placement candidate, avoiding horizon oscillation and MultiPV budget waste. When time permits, the target still includes every remaining placement followed by four movement plies. The opening has 18 placements, so the normal two-second budget cannot exhaust every continuation. This is an enhanced test build aimed at a confirmed evaluation defect, not a perfect solution or a guaranteed win. If WebAssembly fails to load or validate, the app falls back to a TypeScript engine using the same placement baseline. Both paths are fully offline, make no network requests, and consume no tokens.
133+
114134
### Development
115135

116136
This project was developed primarily by GPT-5.6 AI under the project owner's requirements, device testing, rule feedback, and final decisions. The AI contributed the rules engines, search algorithms, Web interface, Windows portable build, and Android app. Generated code has been covered by automated tests and hands-on verification, while rule reviews and search improvements remain welcome.
@@ -126,6 +146,14 @@ pnpm install
126146
pnpm dev
127147
```
128148

149+
Rebuilding the Morris WebAssembly module also requires rustup, the Rust 1.97.0 toolchain pinned by this repository, and the `wasm32-unknown-unknown` target:
150+
151+
```bash
152+
rustup target add wasm32-unknown-unknown
153+
pnpm run build:wasm
154+
pnpm run check:wasm
155+
```
156+
129157
Tests and builds:
130158

131159
```bash

android/FanoronaAndroid.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<Nullable>enable</Nullable>
77
<ApplicationId>com.fanorona.tactician</ApplicationId>
88
<ApplicationTitle>棋局参谋</ApplicationTitle>
9-
<ApplicationVersion>9</ApplicationVersion>
10-
<ApplicationDisplayVersion>2.0.0</ApplicationDisplayVersion>
9+
<ApplicationVersion>14</ApplicationVersion>
10+
<ApplicationDisplayVersion>2.1.0-alpha.3</ApplicationDisplayVersion>
1111
<SupportedOSPlatformVersion>24.0</SupportedOSPlatformVersion>
1212
<AndroidPackageFormat>apk</AndroidPackageFormat>
1313
<AndroidUseAapt2>true</AndroidUseAapt2>

android/MainActivity.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ private static WebResourceResponse EmptyResponse(string mimeType) =>
134134
".json" or ".webmanifest" => "application/manifest+json",
135135
".svg" => "image/svg+xml",
136136
".png" => "image/png",
137+
".wasm" => "application/wasm",
137138
".txt" => "text/plain",
138139
_ => "application/octet-stream",
139140
};

android/finalize.mjs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { copyFile, mkdir, readdir } from "node:fs/promises";
1+
import { copyFile, mkdir, readFile, readdir } from "node:fs/promises";
22
import { dirname, resolve } from "node:path";
33
import { fileURLToPath } from "node:url";
44

@@ -23,6 +23,18 @@ async function findSignedApk(directory) {
2323

2424
const apk = await findSignedApk(searchRoot);
2525
if (!apk) throw new Error("Signed Android APK was not produced");
26+
const { version } = JSON.parse(
27+
await readFile(resolve(projectDir, "package.json"), "utf8"),
28+
);
29+
if (typeof version !== "string" || !/^[0-9A-Za-z.-]+$/.test(version)) {
30+
throw new Error("package.json contains an invalid release version");
31+
}
2632
await mkdir(outputsDir, { recursive: true });
27-
await copyFile(apk, resolve(outputsDir, "fanorona-android.apk"));
28-
await copyFile(apk, resolve(outputsDir, "board-tactician-android.apk"));
33+
await Promise.all([
34+
copyFile(apk, resolve(outputsDir, "fanorona-android.apk")),
35+
copyFile(apk, resolve(outputsDir, "board-tactician-android.apk")),
36+
copyFile(
37+
apk,
38+
resolve(outputsDir, `board-tactician-android-v${version}.apk`),
39+
),
40+
]);

0 commit comments

Comments
 (0)