forked from eros/curltastic
fix
This commit is contained in:
parent
94c6949426
commit
5eefcd23bf
@ -71,7 +71,6 @@ impl Game {
|
||||
broom_y: f32,
|
||||
velocity: f32,
|
||||
curl: i8,
|
||||
friction: f32,
|
||||
) -> Result<Vec<StonePath>, String> {
|
||||
if self.turn_team != team {
|
||||
return Err("Not your turn".to_string());
|
||||
@ -81,9 +80,9 @@ impl Game {
|
||||
}
|
||||
|
||||
self.active_stones.clear();
|
||||
let trajectory =
|
||||
self.physics
|
||||
.throw(self.turn_team, broom_x, broom_y, velocity, curl, friction)?;
|
||||
let trajectory = self
|
||||
.physics
|
||||
.throw(self.turn_team, broom_x, broom_y, velocity, curl)?;
|
||||
self.active_stones = self.physics.current_stones();
|
||||
self.phase = GamePhase::Simulating;
|
||||
|
||||
@ -102,9 +101,8 @@ impl Game {
|
||||
broom_y: f32,
|
||||
velocity: f32,
|
||||
curl: i8,
|
||||
friction: f32,
|
||||
) -> Result<ThrowOutcome, String> {
|
||||
let trajectories = self.handle_throw(team, broom_x, broom_y, velocity, curl, friction)?;
|
||||
let trajectories = self.handle_throw(team, broom_x, broom_y, velocity, curl)?;
|
||||
self.finish_simulation();
|
||||
|
||||
let state_message = self.game_state_message();
|
||||
@ -259,7 +257,7 @@ impl Room {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::physics::DRAW_VELOCITY;
|
||||
use crate::protocol::DRAW_VELOCITY;
|
||||
|
||||
#[test]
|
||||
fn starts_in_waiting_phase() {
|
||||
@ -285,7 +283,7 @@ mod tests {
|
||||
game.start();
|
||||
let turn = game.turn_team;
|
||||
let wrong = turn.other();
|
||||
let result = game.handle_throw(wrong, 0.5, 38.7, DRAW_VELOCITY, 1, 1.0);
|
||||
let result = game.handle_throw(wrong, 0.5, 38.7, DRAW_VELOCITY, 1);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@ -294,7 +292,7 @@ mod tests {
|
||||
let mut game = Game::new();
|
||||
game.start();
|
||||
let turn = game.turn_team;
|
||||
let result = game.handle_throw(turn, 0.5, 38.7, DRAW_VELOCITY, 1, 1.0);
|
||||
let result = game.handle_throw(turn, 0.5, 38.7, DRAW_VELOCITY, 1);
|
||||
assert!(result.is_ok());
|
||||
let paths = result.unwrap();
|
||||
assert!(!paths.is_empty());
|
||||
@ -308,7 +306,7 @@ mod tests {
|
||||
game.start();
|
||||
let turn = game.turn_team;
|
||||
// (0.0, 30.0) is well outside HOUSE_RADIUS of HOUSE_CENTER
|
||||
let result = game.handle_throw(turn, 0.0, 30.0, DRAW_VELOCITY, 1, 1.0);
|
||||
let result = game.handle_throw(turn, 0.0, 30.0, DRAW_VELOCITY, 1);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"broom outside house should be allowed: {:?}",
|
||||
@ -322,7 +320,7 @@ mod tests {
|
||||
game.start();
|
||||
let turn = game.turn_team;
|
||||
let outcome = game
|
||||
.process_throw(turn, 0.0, HOUSE_CENTER.1, DRAW_VELOCITY, 0, 1.0)
|
||||
.process_throw(turn, 0.0, HOUSE_CENTER.1, DRAW_VELOCITY, 0)
|
||||
.unwrap();
|
||||
// ThrowOutcome must not carry end_scored
|
||||
assert!(!outcome.trajectories.is_empty());
|
||||
@ -366,7 +364,7 @@ mod tests {
|
||||
let mut game = Game::new();
|
||||
game.start();
|
||||
let turn = game.turn_team;
|
||||
game.handle_throw(turn, 0.0, HOUSE_CENTER.1, DRAW_VELOCITY, 0, 1.0)
|
||||
game.handle_throw(turn, 0.0, HOUSE_CENTER.1, DRAW_VELOCITY, 0)
|
||||
.unwrap();
|
||||
match turn {
|
||||
Team::Team1 => assert_eq!(game.stones_team1, STONES_PER_TEAM - 1),
|
||||
|
||||
@ -180,12 +180,11 @@ fn spawn_message_handler(
|
||||
broom_y,
|
||||
velocity,
|
||||
curl,
|
||||
friction,
|
||||
}) => {
|
||||
let mut room_guard = room.lock().await;
|
||||
match room_guard
|
||||
.game
|
||||
.process_throw(team, broom_x, broom_y, velocity, curl, friction)
|
||||
.process_throw(team, broom_x, broom_y, velocity, curl)
|
||||
{
|
||||
Ok(ThrowOutcome {
|
||||
trajectories,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -17,26 +17,45 @@ pub const SHEET_LENGTH: f32 = 45.0;
|
||||
pub const HOUSE_CENTER: (f32, f32) = (0.0, 38.5);
|
||||
pub const HOUSE_RADIUS: f32 = 6.0 * FEET_TO_METERS; // 12 ft diameter → 6 ft radius
|
||||
pub const HOG_LINE_Y: f32 = 21.0;
|
||||
pub const BACK_LINE_Y: f32 = 42.0;
|
||||
/// Backline touches the outer house ring (tee + 6 ft).
|
||||
pub const BACK_LINE_Y: f32 = HOUSE_CENTER.1 + HOUSE_RADIUS;
|
||||
pub const HACK_Y: f32 = 2.0;
|
||||
|
||||
// Stone physical properties
|
||||
pub const STONE_RADIUS: f32 = 0.15;
|
||||
pub const STONE_MASS: f32 = 20.0;
|
||||
pub const STONE_FRICTION: f32 = 0.015;
|
||||
/// Newton restitution for stone–stone contacts (Rapier, Average combine).
|
||||
/// Curling granite is nearly elastic on contact; low e makes takeouts feel like
|
||||
/// putty (both limp together). ~0.9 → both keep going along impact direction.
|
||||
/// Newton restitution for stone–stone contacts (Rapier, Max/Average combiners).
|
||||
pub const STONE_RESTITUTION: f32 = 0.9;
|
||||
/// Soft guard end of the throw slider. Weight 1 → MIN_SPEED.
|
||||
/// Calibrated with DRAW_VELOCITY so mid-slider (weight 5) lands near the tee.
|
||||
/// Shared with the frontend; binary sim does not clamp on it (clients send free velocity).
|
||||
|
||||
/// Stop offsets (feet relative to tee) for weights 1..=10 and hack.
|
||||
/// Positive = past tee toward backline (behind the tee).
|
||||
pub const WEIGHT_STOP_OFFSET_FT: [f32; 10] =
|
||||
[-11.0, -9.0, -7.0, -5.0, -3.0, -1.0, 0.0, 1.0, 3.0, 5.0];
|
||||
pub const HACK_STOP_OFFSET_FT: f32 = 12.0;
|
||||
|
||||
/// m/s for weights 1..=10 (calibrated via open-ice stop distance).
|
||||
pub const WEIGHT_SPEEDS: [f32; 10] = [
|
||||
2.2573, 2.2783, 2.2992, 2.3199, 2.3404, 2.3608, 2.3709, 2.3810, 2.4012, 2.4211,
|
||||
];
|
||||
pub const HACK_SPEED: f32 = 2.4899;
|
||||
/// Takeouts: open-ice stop at hack+N feet (N = 6,12,18,24).
|
||||
pub const BOARD_SPEED: f32 = 2.5492;
|
||||
pub const CONTROL_SPEED: f32 = 2.6408;
|
||||
pub const NORMAL_SPEED: f32 = 2.7448;
|
||||
pub const PEEL_SPEED: f32 = 2.8499;
|
||||
pub const BOARD_STOP_OFFSET_FT: f32 = HACK_STOP_OFFSET_FT + 6.0;
|
||||
pub const CONTROL_STOP_OFFSET_FT: f32 = HACK_STOP_OFFSET_FT + 15.0;
|
||||
pub const NORMAL_STOP_OFFSET_FT: f32 = HACK_STOP_OFFSET_FT + 25.0;
|
||||
pub const PEEL_STOP_OFFSET_FT: f32 = HACK_STOP_OFFSET_FT + 35.0;
|
||||
/// Tee-line weight (category 7).
|
||||
pub const DRAW_VELOCITY: f32 = WEIGHT_SPEEDS[6];
|
||||
/// UI soft end (weight 1).
|
||||
#[allow(dead_code)]
|
||||
pub const MIN_SPEED: f32 = 1.9;
|
||||
/// Heavy end of the throw slider. Weight 10 → MAX_SPEED.
|
||||
/// Keep span so weight 5 ≈ DRAW_VELOCITY (2.38).
|
||||
pub const MIN_SPEED: f32 = WEIGHT_SPEEDS[0];
|
||||
/// UI heavy end of draw weights (weight 10). Peel/hack sit above this.
|
||||
#[allow(dead_code)]
|
||||
pub const MAX_SPEED: f32 = 3.0;
|
||||
pub const MAX_SPEED: f32 = WEIGHT_SPEEDS[9];
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@ -89,17 +108,12 @@ pub enum ClientMessage {
|
||||
velocity: f32,
|
||||
#[serde(default = "default_curl")]
|
||||
curl: i8,
|
||||
#[serde(default = "default_friction")]
|
||||
friction: f32,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_curl() -> i8 {
|
||||
1
|
||||
}
|
||||
fn default_friction() -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EndScore {
|
||||
@ -185,7 +199,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn throw_message_uses_velocity_not_weight() {
|
||||
let json = r#"{"type":"throw","team":"team1","broom_x":0.5,"broom_y":38.5,"velocity":4.2,"curl":1,"friction":1.0}"#;
|
||||
let json = r#"{"type":"throw","team":"team1","broom_x":0.5,"broom_y":38.5,"velocity":4.2,"curl":1}"#;
|
||||
let msg: ClientMessage = serde_json::from_str(json).unwrap();
|
||||
match msg {
|
||||
ClientMessage::Throw {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user