fix
This commit is contained in:
parent
94c6949426
commit
5eefcd23bf
@ -71,7 +71,6 @@ impl Game {
|
|||||||
broom_y: f32,
|
broom_y: f32,
|
||||||
velocity: f32,
|
velocity: f32,
|
||||||
curl: i8,
|
curl: i8,
|
||||||
friction: f32,
|
|
||||||
) -> Result<Vec<StonePath>, String> {
|
) -> Result<Vec<StonePath>, String> {
|
||||||
if self.turn_team != team {
|
if self.turn_team != team {
|
||||||
return Err("Not your turn".to_string());
|
return Err("Not your turn".to_string());
|
||||||
@ -81,9 +80,9 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.active_stones.clear();
|
self.active_stones.clear();
|
||||||
let trajectory =
|
let trajectory = self
|
||||||
self.physics
|
.physics
|
||||||
.throw(self.turn_team, broom_x, broom_y, velocity, curl, friction)?;
|
.throw(self.turn_team, broom_x, broom_y, velocity, curl)?;
|
||||||
self.active_stones = self.physics.current_stones();
|
self.active_stones = self.physics.current_stones();
|
||||||
self.phase = GamePhase::Simulating;
|
self.phase = GamePhase::Simulating;
|
||||||
|
|
||||||
@ -102,9 +101,8 @@ impl Game {
|
|||||||
broom_y: f32,
|
broom_y: f32,
|
||||||
velocity: f32,
|
velocity: f32,
|
||||||
curl: i8,
|
curl: i8,
|
||||||
friction: f32,
|
|
||||||
) -> Result<ThrowOutcome, String> {
|
) -> 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();
|
self.finish_simulation();
|
||||||
|
|
||||||
let state_message = self.game_state_message();
|
let state_message = self.game_state_message();
|
||||||
@ -259,7 +257,7 @@ impl Room {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::physics::DRAW_VELOCITY;
|
use crate::protocol::DRAW_VELOCITY;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn starts_in_waiting_phase() {
|
fn starts_in_waiting_phase() {
|
||||||
@ -285,7 +283,7 @@ mod tests {
|
|||||||
game.start();
|
game.start();
|
||||||
let turn = game.turn_team;
|
let turn = game.turn_team;
|
||||||
let wrong = turn.other();
|
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());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +292,7 @@ mod tests {
|
|||||||
let mut game = Game::new();
|
let mut game = Game::new();
|
||||||
game.start();
|
game.start();
|
||||||
let turn = game.turn_team;
|
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());
|
assert!(result.is_ok());
|
||||||
let paths = result.unwrap();
|
let paths = result.unwrap();
|
||||||
assert!(!paths.is_empty());
|
assert!(!paths.is_empty());
|
||||||
@ -308,7 +306,7 @@ mod tests {
|
|||||||
game.start();
|
game.start();
|
||||||
let turn = game.turn_team;
|
let turn = game.turn_team;
|
||||||
// (0.0, 30.0) is well outside HOUSE_RADIUS of HOUSE_CENTER
|
// (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!(
|
assert!(
|
||||||
result.is_ok(),
|
result.is_ok(),
|
||||||
"broom outside house should be allowed: {:?}",
|
"broom outside house should be allowed: {:?}",
|
||||||
@ -322,7 +320,7 @@ mod tests {
|
|||||||
game.start();
|
game.start();
|
||||||
let turn = game.turn_team;
|
let turn = game.turn_team;
|
||||||
let outcome = game
|
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();
|
.unwrap();
|
||||||
// ThrowOutcome must not carry end_scored
|
// ThrowOutcome must not carry end_scored
|
||||||
assert!(!outcome.trajectories.is_empty());
|
assert!(!outcome.trajectories.is_empty());
|
||||||
@ -366,7 +364,7 @@ mod tests {
|
|||||||
let mut game = Game::new();
|
let mut game = Game::new();
|
||||||
game.start();
|
game.start();
|
||||||
let turn = game.turn_team;
|
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();
|
.unwrap();
|
||||||
match turn {
|
match turn {
|
||||||
Team::Team1 => assert_eq!(game.stones_team1, STONES_PER_TEAM - 1),
|
Team::Team1 => assert_eq!(game.stones_team1, STONES_PER_TEAM - 1),
|
||||||
|
|||||||
@ -180,12 +180,11 @@ fn spawn_message_handler(
|
|||||||
broom_y,
|
broom_y,
|
||||||
velocity,
|
velocity,
|
||||||
curl,
|
curl,
|
||||||
friction,
|
|
||||||
}) => {
|
}) => {
|
||||||
let mut room_guard = room.lock().await;
|
let mut room_guard = room.lock().await;
|
||||||
match room_guard
|
match room_guard
|
||||||
.game
|
.game
|
||||||
.process_throw(team, broom_x, broom_y, velocity, curl, friction)
|
.process_throw(team, broom_x, broom_y, velocity, curl)
|
||||||
{
|
{
|
||||||
Ok(ThrowOutcome {
|
Ok(ThrowOutcome {
|
||||||
trajectories,
|
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_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 HOUSE_RADIUS: f32 = 6.0 * FEET_TO_METERS; // 12 ft diameter → 6 ft radius
|
||||||
pub const HOG_LINE_Y: f32 = 21.0;
|
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;
|
pub const HACK_Y: f32 = 2.0;
|
||||||
|
|
||||||
// Stone physical properties
|
// Stone physical properties
|
||||||
pub const STONE_RADIUS: f32 = 0.15;
|
pub const STONE_RADIUS: f32 = 0.15;
|
||||||
pub const STONE_MASS: f32 = 20.0;
|
pub const STONE_MASS: f32 = 20.0;
|
||||||
pub const STONE_FRICTION: f32 = 0.015;
|
pub const STONE_FRICTION: f32 = 0.015;
|
||||||
/// Newton restitution for stone–stone contacts (Rapier, Average combine).
|
/// Newton restitution for stone–stone contacts (Rapier, Max/Average combiners).
|
||||||
/// 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.
|
|
||||||
pub const STONE_RESTITUTION: f32 = 0.9;
|
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.
|
/// Stop offsets (feet relative to tee) for weights 1..=10 and hack.
|
||||||
/// Shared with the frontend; binary sim does not clamp on it (clients send free velocity).
|
/// 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)]
|
#[allow(dead_code)]
|
||||||
pub const MIN_SPEED: f32 = 1.9;
|
pub const MIN_SPEED: f32 = WEIGHT_SPEEDS[0];
|
||||||
/// Heavy end of the throw slider. Weight 10 → MAX_SPEED.
|
/// UI heavy end of draw weights (weight 10). Peel/hack sit above this.
|
||||||
/// Keep span so weight 5 ≈ DRAW_VELOCITY (2.38).
|
|
||||||
#[allow(dead_code)]
|
#[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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Default)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
@ -89,17 +108,12 @@ pub enum ClientMessage {
|
|||||||
velocity: f32,
|
velocity: f32,
|
||||||
#[serde(default = "default_curl")]
|
#[serde(default = "default_curl")]
|
||||||
curl: i8,
|
curl: i8,
|
||||||
#[serde(default = "default_friction")]
|
|
||||||
friction: f32,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_curl() -> i8 {
|
fn default_curl() -> i8 {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
fn default_friction() -> f32 {
|
|
||||||
1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct EndScore {
|
pub struct EndScore {
|
||||||
@ -185,7 +199,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn throw_message_uses_velocity_not_weight() {
|
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();
|
let msg: ClientMessage = serde_json::from_str(json).unwrap();
|
||||||
match msg {
|
match msg {
|
||||||
ClientMessage::Throw {
|
ClientMessage::Throw {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user