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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user