Prune stale docs and update .hermes content

Delete a large set of outdated documentation (many files under docs/ and .hermes/plans/, including audits, design, prd, technical, planning, assets, and todos). Update and consolidate .hermes content: refresh shared-memory pages (decision-log, development-workflow, document-map, pitfalls, project-overview, team-conventions) and several skills/references under .hermes/skills. Also modify AGENTS.md, README.md, UI_CODING_STANDARD.md, docs/README.md and .encoding-check-ignore. Purpose: clean up stale planning/audit material and keep current hermes documentation and related top-level docs in sync.
This commit is contained in:
2026-05-15 06:24:07 +08:00
parent 2eded08bc7
commit 3cb3efb4d0
708 changed files with 4033 additions and 142328 deletions

View File

@@ -51,6 +51,8 @@ const MATCH3D_SIZE_TIER_RULES: [Match3DSizeTierRule; 5] = [
radius_scale: 0.76,
},
];
const MATCH3D_CONTAINER_MOUTH_SPAWN_RATIO: f32 = 0.78;
const MATCH3D_GOLDEN_ANGLE: f32 = 2.399_963_1;
pub fn compile_result_draft(config: &Match3DCreatorConfig) -> Match3DResultDraft {
let game_name = format!("{}抓大鹅", config.theme_text);
@@ -336,6 +338,7 @@ fn build_initial_items(
let item_type_count = resolve_item_type_count(clear_count, difficulty, item_type_count_override);
let selected_visual_keys = select_visual_keys(&mut rng, theme_text, item_type_count);
let size_tier_plan = resolve_size_tier_plan(item_type_count);
let total_item_count = clear_count * MATCH3D_ITEMS_PER_CLEAR;
let mut items = Vec::with_capacity((clear_count * MATCH3D_ITEMS_PER_CLEAR) as usize);
for clear_index in 0..clear_count {
@@ -345,8 +348,14 @@ fn build_initial_items(
let radius = resolve_item_radius_variant(base_radius, size_tier_plan[visual_index]);
for copy_index in 0..MATCH3D_ITEMS_PER_CLEAR {
let (x, y) = random_point_in_circle(&mut rng, max_spawn_offset(radius));
let instance_index = clear_index * MATCH3D_ITEMS_PER_CLEAR + copy_index;
let (x, y) = spawn_point_in_container_mouth(
instance_index,
total_item_count,
radius,
rng.next_unit_signed(),
rng.next_unit_signed(),
);
items.push(Match3DItemSnapshot {
item_instance_id: format!("match3d-item-{instance_index:04}"),
item_type_id: item_type_id.clone(),
@@ -474,15 +483,29 @@ fn max_spawn_offset(radius: f32) -> f32 {
(MATCH3D_BOARD_RADIUS - MATCH3D_BOARD_SAFE_MARGIN - radius).max(0.0)
}
fn random_point_in_circle(rng: &mut DeterministicRng, max_radius: f32) -> (f32, f32) {
for _ in 0..24 {
let x = rng.next_unit_signed() * max_radius;
let y = rng.next_unit_signed() * max_radius;
if x * x + y * y <= max_radius * max_radius {
return (MATCH3D_BOARD_CENTER + x, MATCH3D_BOARD_CENTER + y);
}
fn spawn_point_in_container_mouth(
item_index: u32,
total_item_count: u32,
radius: f32,
jitter_x: f32,
jitter_y: f32,
) -> (f32, f32) {
let safe_radius = max_spawn_offset(radius);
let mouth_radius = safe_radius * MATCH3D_CONTAINER_MOUTH_SPAWN_RATIO;
let total = total_item_count.max(1) as f32;
let index = item_index as f32;
let distance = ((index + 0.5) / total).sqrt() * mouth_radius;
let angle = index * MATCH3D_GOLDEN_ANGLE;
let jitter_radius = (mouth_radius * 0.035).min(0.012);
let mut dx = angle.cos() * distance + jitter_x * jitter_radius;
let mut dy = angle.sin() * distance + jitter_y * jitter_radius;
let current_distance = (dx * dx + dy * dy).sqrt();
if current_distance > safe_radius && current_distance > 0.0 {
let ratio = safe_radius / current_distance;
dx *= ratio;
dy *= ratio;
}
(MATCH3D_BOARD_CENTER, MATCH3D_BOARD_CENTER)
(MATCH3D_BOARD_CENTER + dx, MATCH3D_BOARD_CENTER + dy)
}
fn fully_covers(
@@ -943,6 +966,58 @@ mod tests {
}
}
#[test]
fn initial_positions_are_centered_in_container_mouth() {
let run = start_run_with_seed_at(
"run-centered-spawn".to_string(),
"user-1".to_string(),
"profile-1".to_string(),
&build_creator_config("玩具", None, 21, 8).expect("config should be valid"),
12,
1_000,
)
.expect("run should start");
let board_items = run
.items
.iter()
.filter(|item| item.state == Match3DItemState::InBoard)
.collect::<Vec<_>>();
let item_count = board_items.len() as f32;
let mean_x = board_items.iter().map(|item| item.x).sum::<f32>() / item_count;
let mean_y = board_items.iter().map(|item| item.y).sum::<f32>() / item_count;
let max_distance = board_items
.iter()
.map(|item| {
let dx = item.x - MATCH3D_BOARD_CENTER;
let dy = item.y - MATCH3D_BOARD_CENTER;
(dx * dx + dy * dy).sqrt()
})
.fold(0.0_f32, f32::max);
let far_item_count = board_items
.iter()
.filter(|item| {
let dx = item.x - MATCH3D_BOARD_CENTER;
let dy = item.y - MATCH3D_BOARD_CENTER;
(dx * dx + dy * dy).sqrt() > 0.32
})
.count();
let mut quadrants = BTreeMap::<String, u32>::new();
for item in board_items {
let quadrant = format!(
"{}-{}",
if item.x >= MATCH3D_BOARD_CENTER { "r" } else { "l" },
if item.y >= MATCH3D_BOARD_CENTER { "b" } else { "t" },
);
*quadrants.entry(quadrant).or_default() += 1;
}
assert!((mean_x - MATCH3D_BOARD_CENTER).abs() < 0.035);
assert!((mean_y - MATCH3D_BOARD_CENTER).abs() < 0.035);
assert!(max_distance < 0.4);
assert!(far_item_count > 0);
assert_eq!(quadrants.len(), 4);
}
#[test]
fn twenty_five_or_less_does_not_repeat_visual_keys() {
let run = start_run_with_seed_at_and_item_type_count(