From f71985c0c34b49cb4f7e3b33fbb03646da02ca47 Mon Sep 17 00:00:00 2001 From: Yaossg Date: Sat, 17 Oct 2026 00:04:47 +0800 Subject: [PATCH] Add verified heart pattern example --- README.md | 2 +- bench/README.md | 24 +++++++++++++ bench/patterns/heart-5x5.txt | 5 +++ bench/results/heart-5x5-example.json | 52 ++++++++++++++++++++++++++++ bench/test_cli.py | 15 ++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 bench/patterns/heart-5x5.txt create mode 100644 bench/results/heart-5x5-example.json diff --git a/README.md b/README.md index 9c3f6e8..f92d065 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ make bench --position-batch 128 --devices all --repeat 3 ``` -自定义图案用 `--pattern` 或 `--pattern-file`,支持史莱姆、非史莱姆和忽略格;见 [6×6 苦力怕示例](bench/README.md#自定义图案6x6-苦力怕)。 +自定义图案用 `--pattern` 或 `--pattern-file`,支持史莱姆、非史莱姆和忽略格;见 [已验证的 5×5 爱心](bench/README.md#已验证实例5x5-爱心)及 [6×6 苦力怕示例](bench/README.md#自定义图案6x6-苦力怕)。 H100 编译:`make -C bench -B CUDA_ARCH=90`。需要 CUDA Toolkit 和 C++17 编译器。普通分支不包含拒绝重抽补扫,不能将它的结果声明为严格完整目录;参数和计时口径见 [CLI 使用说明](bench/README.md)。 diff --git a/bench/README.md b/bench/README.md index 12d4f34..c2083ee 100644 --- a/bench/README.md +++ b/bench/README.md @@ -16,6 +16,30 @@ mkdir -p artifacts 默认编译 `sm_86` 并附带 PTX,适合本机 RTX 3060 Laptop。换架构时用 `-B` 强制重建;仅改变 Make 变量不会使已有文件自动过期。 +## 已验证实例:5×5 爱心 + +种子 **`22428888508636`**,左上角区块 **`(-5,-147)`**(方块原点 `(-80,-2352)`)。每格一个区块,向右为 `+X`、向下为 `+Z`;16 格 `#` 必须是史莱姆,9 格 `.` 必须不是: + +```text +.#.#. +##### +##### +.###. +..#.. +``` + +```sh +./bench/bin/slime-bench --radius 256 \ + --pattern-file bench/patterns/heart-5x5.txt \ + --position-batch 256 --max-results 1000 + +# 单点复查,收集全部普通分支匹配:应得到 1,181 个种子 +./bench/bin/slime-bench --x -5 --z -147 --radius 0 \ + --pattern-file bench/patterns/heart-5x5.txt --max-results 2000 +``` + +RTX 3060 Laptop 实测:131,585 个锚点,普通分支共找到 1,456 个种子/位置匹配,扫描约 2.439 秒(含设备初始化的端到端约 2.620 秒)。保存的 1,000 个搜索样本,以及单点复查的全部 1,181 个种子,都通过独立 Python 实现的 Java 整数回绕及 `Random.nextInt(10)` 精确逐格验证。已知种子及计时见 [results/heart-5x5-example.json](results/heart-5x5-example.json)。GPU 回归检查单点计数、已知种子及全部样本,搜索计数不包含拒绝重抽补扫。 + ## 自定义图案:6×6 苦力怕 `--pattern` 接受 `/` 分隔的行,或用 `--pattern-file` 读取每行一排的文本文件。`#` 必须是史莱姆区块,`.` 必须不是,`?` 不限制。支持非正方形,宽、高分别为 1~64,至少含一个 `#`;不能与 `--size` 同时指定。不自动旋转或镜像。 diff --git a/bench/patterns/heart-5x5.txt b/bench/patterns/heart-5x5.txt new file mode 100644 index 0000000..7a13f33 --- /dev/null +++ b/bench/patterns/heart-5x5.txt @@ -0,0 +1,5 @@ +.#.#. +##### +##### +.###. +..#.. diff --git a/bench/results/heart-5x5-example.json b/bench/results/heart-5x5-example.json new file mode 100644 index 0000000..e1732b3 --- /dev/null +++ b/bench/results/heart-5x5-example.json @@ -0,0 +1,52 @@ +{ + "pattern": [ + ".#.#.", + "#####", + "#####", + ".###.", + "..#.." + ], + "seed48": 22428888508636, + "chunk_x": -5, + "chunk_z": -147, + "block_origin": [ + -80, + -2352 + ], + "verified_rows": [ + ".#.#.", + "#####", + "#####", + ".###.", + "..#.." + ], + "verification": { + "method": "independent Python implementation of Java int overflow and Random.nextInt(10) rejection loop", + "verified_search_sample_count": 1000, + "verified_single_anchor_count": 1181, + "slime_cells": 16, + "non_slime_cells": 9 + }, + "search": { + "center": [ + 0, + 0 + ], + "radius_chunks": 256, + "anchors": 131585, + "matches": 1456, + "scan_wall_ms": 2438.534811, + "elapsed_ms": 2619.994137, + "gpu": "NVIDIA GeForce RTX 3060 Laptop GPU", + "rejection_tail_included": false + }, + "single_anchor": { + "matches": 1181, + "root_jobs": 405 + }, + "source_sha256": { + "bench/slime_bench.cu": "3c70cbe2e5babc3e737199fb5e62b78ea9c5daadc0020af77292c2d59516259e", + "include/slime/solver_core.hpp": "feb1200518716b4d796fb60f60a83b6b798155bbd4e795531793181a5c7f9eba", + "include/slime/upper_interval.hpp": "af3fddf7705295d91ffd6e2190f542004a3ecff847fce9d4c9b13125bdbdc2d5" + } +} diff --git a/bench/test_cli.py b/bench/test_cli.py index 9700370..cbe6d5c 100644 --- a/bench/test_cli.py +++ b/bench/test_cli.py @@ -40,8 +40,23 @@ def main(): with tempfile.TemporaryDirectory() as tmp: path=Path(tmp)/'pattern.txt';path.write_bytes(b'?##.\r\n#..?\r\n..#?\r\n') assert run('--radius',0,'--pattern-file',path,'--dry-run')['pattern']==pattern.split('/') + heart_fixture=json.loads((HERE/'results/heart-5x5-example.json').read_text()) + assert (HERE/'patterns/heart-5x5.txt').read_text().splitlines()==heart_fixture['pattern'] + assert all(slime(heart_fixture['seed48'],heart_fixture['chunk_x']+dx,heart_fixture['chunk_z']+dz)==(ch=='#') + for dz,row in enumerate(heart_fixture['pattern']) for dx,ch in enumerate(row)) summary={'cli_scope_and_errors':True,'pattern_inputs':True} if args.gpu: + heart_poc=run('--x',heart_fixture['chunk_x'],'--z',heart_fixture['chunk_z'],'--radius',0, + '--pattern-file',HERE/'patterns/heart-5x5.txt','--max-results',2000) + heart_hits=heart_poc['rounds'][0] + assert heart_hits['matches']==heart_fixture['single_anchor']['matches'] + assert heart_hits['root_jobs']==heart_fixture['single_anchor']['root_jobs'] + assert not heart_hits['sample_truncated'] and len(heart_hits['sample'])==heart_hits['matches'] + assert len(set(map(tuple,heart_hits['sample'])))==heart_hits['matches'] + assert [heart_fixture['seed48'],heart_fixture['chunk_x'],heart_fixture['chunk_z']] in heart_hits['sample'] + for seed,x,z in heart_hits['sample']: + assert all(slime(seed,x+dx,z+dz)==(ch=='#') for dz,row in enumerate(heart_fixture['pattern']) for dx,ch in enumerate(row)) + summary['heart_pattern_verified_matches']=heart_hits['matches'] # A mixed-mask regression fixture; this is not the target creeper pattern. fixture=json.loads((HERE/'testdata/mixed-mask-regression.json').read_text()) face=run('--x',fixture['chunk_x'],'--z',fixture['chunk_z'],'--radius',0,