山河拼图

参考用户提供的日本地图下落合并视频开发的小红书离线小工具。正式交付包含省级版、地级版;县级素材已备好,未进入游戏发布包。

项目独立存放于 ProjectRednote/shanhe-puzzle/。以下命令请在 shanhe-puzzle 目录执行。仓库仅跟踪源码、测试和构建脚本、依赖清单、本文档,以及运行和构建校验必需的地图数据。

立即预览和打包

npm ci
npm run dev
# http://localhost:4173
npm test
npm run build
npm run test:browser
npm run test:results
npm run test:interactions
npm run test:container
npm run test:swipes

浏览器测试目前默认使用 macOS 已安装的 Google Chrome,路径见 tests/browser.mjs。构建要求 Python 3,以及官方 minitool-zip-builder 1.8.0 Skill。克隆后需将 Skill 解压到本地 .codex/skills/minitool-zip-builder/,该目录应直接包含 SKILL.md、references/ 和 scripts/。本地工具、依赖、构建输出和报告均已忽略,不推送到代码库。所有运行依赖与地图随最终 ZIP 提供,无联网运行逻辑。

上传产物:artifacts/shanhe-puzzle-xhs.zip。根目录直接包含 index.html,不是项目源码压缩包。源代码在 src/,静态构建在 dist/。

必须在创服平台模拟器、Android 真机扫码、iOS 真机扫码验证同一 ZIP。 本地浏览器仅用于开发回归和视觉检查,不能替代小红书容器验收。Chrome 61 / Android 8.1 兼容性、CSS 兼容性及真机性能均未实测;原生发笔记、相册授权和保存需要真机验证。

本机预览服务监听 127.0.0.1:4173,可以在这台Mac的系统浏览器打开 http://localhost:4173/;与Codex内置浏览器使用同一个服务。当前已有服务时无需重复启动。

版本范围

  • 省级版:全国 34 个省级行政区,各出现一次,合并为一个整体即通关。
  • 地级版:选择省或自治区,提供 27 个关卡、333 个地级行政区,包含地级市、自治州、地区、盟。
  • 排除县、区、县级市,以及济源、仙桃等省直辖县级单位。数据源会把部分县级单位标为 city,构建时进一步用行政区代码排除。
  • 北京、天津、上海、重庆、香港、澳门不设地级关卡;数据源暂缺台湾省地级边界,未生成对应地级关卡。
  • 本机县级原始边界位于 data/raw/,共 335 组、2840 个县级单元;预处理素材位于 data/county-materials/,另附独立素材 ZIP。这些开发素材全部保留本地,不进入 Git 或发布包。

玩法与得分

进入页选择省级 / 地级版、地区、四档下落速度和三档弹性,点击“开启这局旅程”后开局。在容器内快速轻划并松手,给容器一次惯性晃动;划过位置附近的地块反应更强。每次轻划只在松手时触发一次,地块获得带少量方向、力度差异的冲量后自由运动,手指只能决定大致方向。慢拖、按住、取消触摸或暂停不会持续施力,也不会抓取、牵引或瞬移单块碎片。底部只有一个无文字的圆形暂停图标,统一查看本局旅程、继续、结束或重开;电脑保留方向键辅助。弹跳、倾斜和速度有上限。

只有真实相邻关系、正确地理位置接近时才自动吸合。全部落下后仍可晃动,直到通关、稳定堆过红线超过 2.4 秒,或主动结束。底部圆形暂停图标打开旅程菜单,可继续、结束旅程、重新开局。结束旅程会保留成绩,即使零分或未通关也会生成图片;通关和判负自动生成。

省级海岛使用明确标注的“跨海连接”;地级版中因海域、未纳入本关的省直辖县等产生的间隔,使用明确标注的“隔区连接”。这两种连接都是游戏规则,不能当作陆地邻接关系。入口地图保留相对地理位置。

档位 出片间隔 重力参数 得分倍率
佛系看山 8.5 秒 0.32 ×1
说走就走 6 秒 0.48 ×1.5
一路狂飙 4 秒 0.7 ×2
山河起飞 2.5 秒 1 ×3

每次合并分数 = (100 + 40 × (合并后块数 − 2)) × 连合系数 × 速度倍率 × 特殊连接系数,四舍五入到整数。8.5 秒内再次合并视为连合,系数每次增加 0.25,从 1 起、最高 2;跨海和隔区连接额外乘 0.8。通关再奖 1000 × 速度倍率。轻划不限次数、不扣分。弹性不改变得分倍率。

弹性三档为“轻轻弹 / 蹦蹦跳 / 弹力全开”,碰撞恢复系数分别为 0.65 / 0.82 / 0.96,同时调整空气阻力、横向冲量和上抛上限。现在最低档对应上一版最高档;默认推荐“蹦蹦跳”,便于腾挪,“弹力全开”需要更好地控制节奏。省级、地级碎片均在上一版基础上线性再缩小 10%;地级仍比相同地图基准额外小 30%;组合过大时仍会全局等比缩放。

最佳纪录按“关卡 + 下落速度 + 弹性”分别保存,旧版纪录对应最低弹性。客户端 9.46.0+ 优先使用官方 Storage;含同步启动信息、异步回退、编译序号剥离及旧纪录迁移。旧环境回退到带异常处理的浏览器存储。

成绩页展示可分享的 750×1400 图片:本局得分、结束时实际容器画面、关卡、速度和弹性、用时、最大拼合 / 最佳连合,以及得分随分钟变化的曲线。曲线从零开始,记录实际得分时刻和结束时间,包含通关奖励,暂停不计时。不会用完整地图冒充未完成的结局。

“发小红书”在玩家点击后调用 writeTempFile + postNote,携带图片和成绩文案打开图文发布页,玩家可编辑、取消、确认发布;不会自动发布或声称审核成功。“保存成绩图”调用 writeTempFile + saveImageToPhotosAlbum。无 SDK、接口缺失、授权拒绝、取消或调用失败都有提示,不丢失成绩图;临时路径即用即弃。

当前官方文档支持分享已有视频,但未提供公开录屏 API;屏幕共享被禁用。因此本包不含自动录屏,可用手机系统录屏后自行分享。未使用未公开原生 bridge 或网页屏幕采集。

构建使用已随源码提供的 src/maps.js;research/china.json 是构建时地图完整性核对必需的源快照。其余 research/ 文件和整个 data/ 目录均不入库。下载与预处理脚本仅用于另行更新地图;重生成前需要准备 research/areas.json 等原始输入。

代码与数据

  • src/core.js:相邻关系、地理位置与角度吸合判断、计分、纪录清洗。
  • src/game.js:Matter.js 刚体、出片、组合、胜负、速度、弹性、容器滑动与上晃、得分时间点和尺寸变化。
  • src/app.js:入口 / 游戏 / 成绩页切换、Canvas 绘制、容器手势与键盘输入、暂停与结束、原生 Storage、发笔记和相册导出。
  • src/result.js:真实结局快照与得分曲线的成绩图绘制。
  • src/index.html、src/style.css:单入口、响应式布局、安全区和 Chrome 61 样式基线。
  • src/maps.js:构建期简化的省级 / 地级真实轮廓与预计算连接图。
  • scripts/download-maps.py、scripts/download-provinces.py:仅开发期下载公开地图;后者有台湾省 404 的如实记录。
  • scripts/prepare-maps.py:简化轮廓、区分行政级别、预计算邻接,输出正式数据和独立县级素材。
  • scripts/check.mjs、scripts/build.mjs、scripts/package.py:ES2017 / 离线静态检查、官方 Skill 体积审计、ZIP 结构及完整性验证。
  • tests/:真实地图可通关性、错误边拒合、朝向一致性、计分、警戒线、整框惯性及入口 / 开局浏览器回归。

地图来源:阿里云 DataV.GeoAtlas,2026-10-16 下载。数据可能落后于最新行政区划,已做游戏用轮廓简化,不能用于导航、测绘或行政区划查询。原始数据提供方条款继续适用,公开可下载不等于获得额外授权。视觉保留岛屿轮廓;物理碰撞对极小区域、退化或相交轮廓采用简化代理。

物理引擎 Matter.js 0.20.0、凹多边形分解 poly-decomp 0.3.0 随包本地提供,许可证见发布包 licenses.json。不用 AI、外部 SDK、远程字体、WASM、Worker、网络请求、剪贴板或文件下载能力。

检查记录

artifacts/check-report.json 是本地语法及资源预检,artifacts/official-audit.txt 是官方 Skill 1.8.0 的目录与 ZIP 审计结果,artifacts/browser-report.json 是本地浏览器开发回归,artifacts/package-report.json 提供包大小和 SHA-256。详细交付检查及待验证项目见 artifacts/VALIDATION.md。

容器晃动与碎片比例更新

棋盘改为较窄的开口实体容器,侧壁和底边清晰可见,红色警戒线位于顶部 78px。以 320×435 逻辑尺寸为例,全国开局缩放约 0.4995,相对最初约 0.215 的版本仍明显更大,本轮比上一版的0.555再缩小10%。保持各省之间真实比例,微小地区保持真实相对比例,同样通过框体晃动参与拼接。

晃动采用阻尼弹簧模型:容器位移和施加给所有碎片的惯性来自同一状态,并有 14px 的视觉位移限制。在容器内滑动可朝任意方向施加冲量,力度随滑速增加,按与手指的距离平滑衰减;所有组合都受影响,近处更强。移动时只采集手势,在松手时识别一次轻划;使用最近约180ms轨迹,位移至少18px、平均速度至少0.16px/ms,慢拖和按住不触发。触发间隔至少240ms,系统取消或暂停清理尚未触发的手势。键盘左右键仍支持长按。无需加速度计或设备运动 API。

较大组合超出容器可用空间时,全体碎片按相同比例缩小,保持地理相对比例、分数与队列,继续容纳完整地图。难度和拥堵由关卡形状、晃动、速度及弹性共同影响;本地模拟只用于排查物理问题,不能推断真机性能或玩家通关难度。

碰撞与晃动手感

碰撞采用160ms轻微压缩回弹、淡轮廓与180ms小接触点,明显弱于合并时的1600ms反馈。渲染形变不修改真实碰撞几何。左右晃动缓存300ms颠起请求,每个物理步在实际接触时处理,避免150ms输入采样错过55ms接触窗口;每组仍有500ms颠起间隔,过期请求不在空中反复加速。暂停/离开页面清理待处理请求。上晃对框内全部组合施加一次向上冲量,420ms 冷却限制快速连点。颠起目标与上抛速度随弹性档位变化,但各档均有上限。最大倾角约 ±9.2°,水平和下落速度也受限,避免无限加速或飞出框。

tests/shake-preview.mjs 用真实左右操作和虚拟时钟采集碰撞反馈、速度与截图;tests/results.mjs 检查上晃、暂停主动结束、曲线和图片生成、模拟 SDK 参数和失败降级、自然判负及小型关卡真实通关的结算流程。均为本地开发回归,不能替代平台模拟器或真机验收。

artifacts/bounce-probe.json记录320×590逻辑尺寸下的实际物理峰值:单块正方形、一次上晃,四种速度分别测得轻弹33~37px、正常97~110px、强弹236~270px。真实地图组合、容器尺寸和接触情况会改变结果。

省级/地级有滑块过渡及水平滑动手势;弹性选项压缩回弹,普通按钮轻按,目的地和弹窗渐入。减少动态效果设置会关闭装饰动画与碰撞形变,保留游戏物理。npm run test:interactions覆盖滑动切换、推荐默认档、按钮动画和真实碰撞反馈。

手机布局与地图标签修正

顶部在状态栏安全区之外额外预留56px平台操作区。玩法说明移到入口页脚,游戏的旅程设置和暂停/结束移到底部,成绩页返回移入底部操作组;弹窗也避开平台顶部区域。

手机入口保留地图预览,并按设置面板剩余高度分配地图区域;省级展示全国、地级展示所选省区。短屏收紧间距,横屏地图与双栏设置并排,避免地图、面板和页脚互相挤压。

入口和游戏页均按可见视口及安全区排版:在320×568、375×667、390×844以及740×390横屏检查一屏显示;小屏会缩减装饰地图和冗余文案,保留所有主要选择与开局按钮。省区选择采用27项网格,不再使用原生长下拉框,弹窗注明无地级数据的范围。

四档名称为“佛系看山 / 说走就走 / 一路狂飙 / 山河起飞”,速度与倍率参数保持原值。底部仅保留无文字的圆形暂停图标,合并全部旅程操作,容器顶部说明标签已移除,成绩页同样一屏显示。游戏地图先绘制全部形状,再统一绘制标签;名称采用独立布局、边距约束、避让,以及小地区标签底色 / 引线,防止名称贴底被裁或被其他省份覆盖。

构建补回源数据的小离岛外环及独立海域线层,34省级代码、名称及外环数量均与保存的原始数据对比。台湾省14个源外环、香港9个、澳门2个、海南133个全部保留。详见artifacts/MAP_AUDIT.md,这不是标准地图审图结论。

npm run test:container在现代浏览器模拟平台顶部按钮与四组手机尺寸/安全区,逐页点击并检查布局,不替代小红书真机验证。

tests/swipes.mjs 使用真实触摸事件验证单次轻划松手触发、移动中不施力、慢拖和按住过滤、松手后的惯性运动、系统取消、滑动中暂停和恢复、圆形无文字菜单;物理单测另检查附近力度更强、远处仍动、快速更有力和三档边界限制。

S
Description
No description provided
Readme 2.8 MiB
Languages
JavaScript 94.6%
Python 2.6%
CSS 2.3%
HTML 0.5%