From c2f14434367ad44efe88cc94588fd506a52576a7 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 29 Aug 2026 09:57:39 +0200 Subject: [PATCH] fmt: just fmt (rust only) --- tdkpin-rs/src/app.rs | 131 ++++++++----- tdkpin-rs/src/assets.rs | 3 +- tdkpin-rs/src/borland_random.rs | 9 +- tdkpin-rs/src/flipper_physics.rs | 99 +++++++--- tdkpin-rs/src/game.rs | 314 ++++++++++++++---------------- tdkpin-rs/src/original_physics.rs | 69 +++---- tdkpin-rs/src/persistence.rs | 18 +- tdkpin-rs/src/real48.rs | 20 +- tdkpin-rs/src/simulation.rs | 43 +++- tdkpin-rs/src/table.rs | 8 +- 10 files changed, 386 insertions(+), 328 deletions(-) diff --git a/tdkpin-rs/src/app.rs b/tdkpin-rs/src/app.rs index 233294a..22b640d 100644 --- a/tdkpin-rs/src/app.rs +++ b/tdkpin-rs/src/app.rs @@ -1,24 +1,67 @@ +use std::path::Path; + +use macroquad::prelude::*; + use crate::{ assets::Assets, game::{Controls, Event, Game, Nudge}, persistence::{ - HighScore, Language, Persistence, SavedData, insert_high_score, qualifies_high_score, + HighScore, + Language, + Persistence, + SavedData, + insert_high_score, + qualifies_high_score, }, }; -use macroquad::prelude::*; -use std::path::Path; const WIDTH: f32 = 640.0; const HEIGHT: f32 = 460.0; const DETAIL_TIMER_SECONDS: [f32; 5] = [0.050, 0.040, 0.030, 0.020, 0.010]; const ADD_PLAYER_KEYS: [KeyCode; 3] = [KeyCode::KpAdd, KeyCode::RightBracket, KeyCode::Equal]; const TARGET_POSITIONS: [[(f32, f32); 5]; 6] = [ - [(9.0, 41.0), (23.0, 11.0), (56.0, 14.0), (63.0, 47.0), (35.0, 63.0)], - [(11.0, 45.0), (18.0, 13.0), (52.0, 11.0), (63.0, 42.0), (39.0, 64.0)], - [(13.0, 50.0), (15.0, 17.0), (46.0, 9.0), (64.0, 37.0), (44.0, 62.0)], - [(17.0, 55.0), (11.0, 23.0), (39.0, 7.0), (64.0, 30.0), (50.0, 59.0)], - [(21.0, 60.0), (9.0, 28.0), (35.0, 7.0), (62.0, 25.0), (54.0, 56.0)], - [(8.0, 35.0), (26.0, 8.0), (59.0, 18.0), (59.0, 50.0), (27.0, 61.0)], + [ + (9.0, 41.0), + (23.0, 11.0), + (56.0, 14.0), + (63.0, 47.0), + (35.0, 63.0), + ], + [ + (11.0, 45.0), + (18.0, 13.0), + (52.0, 11.0), + (63.0, 42.0), + (39.0, 64.0), + ], + [ + (13.0, 50.0), + (15.0, 17.0), + (46.0, 9.0), + (64.0, 37.0), + (44.0, 62.0), + ], + [ + (17.0, 55.0), + (11.0, 23.0), + (39.0, 7.0), + (64.0, 30.0), + (50.0, 59.0), + ], + [ + (21.0, 60.0), + (9.0, 28.0), + (35.0, 7.0), + (62.0, 25.0), + (54.0, 56.0), + ], + [ + (8.0, 35.0), + (26.0, 8.0), + (59.0, 18.0), + (59.0, 50.0), + (27.0, 61.0), + ], ]; const BUMPER_VALUE_REGIONS: [(i32, i32, i32, i32); 5] = [ (103, 304, 121, 323), @@ -116,11 +159,7 @@ impl AttractAnimation { fn target_active(self, index: usize) -> bool { let index = u32::try_from(index).unwrap_or(0); - let first_activation = if index == 0 { - 32 - } else { - index * 2 - }; + let first_activation = if index == 0 { 32 } else { index * 2 }; self.tick >= first_activation && self.tick.wrapping_sub(index * 2) % 32 < 16 } @@ -137,11 +176,7 @@ impl AttractAnimation { fn record_strip_active(self, index: usize) -> bool { let index = u32::try_from(index).unwrap_or(0); - let first_activation = if index == 0 { - 40 - } else { - index * 4 - }; + let first_activation = if index == 0 { 40 } else { index * 4 }; self.tick >= first_activation && self.tick.wrapping_sub(index * 4) % 40 < 20 } @@ -151,11 +186,7 @@ impl AttractAnimation { fn item_index(self) -> Option { let phase = (self.tick % 144) / 8; - let item = if phase < 10 { - phase - } else { - 18 - phase - }; + let item = if phase < 10 { phase } else { 18 - phase }; (item != 0).then(|| usize::try_from(item - 1).unwrap_or(0)) } @@ -495,7 +526,8 @@ impl App { self.name.pop(); } let click = is_mouse_button_pressed(MouseButton::Left).then(Self::logical_mouse); - let clicked_ok = click.is_some_and(|point| Rect::new(255.0, 256.0, 60.0, 23.0).contains(point)); + let clicked_ok = + click.is_some_and(|point| Rect::new(255.0, 256.0, 60.0, 23.0).contains(point)); let clicked_cancel = click.is_some_and(|point| Rect::new(324.0, 256.0, 60.0, 23.0).contains(point)); if is_key_pressed(KeyCode::Escape) || clicked_cancel { @@ -574,11 +606,8 @@ impl App { } if self.attract.magnetic_records_active() { - for (x, y, width, height) in [ - (151, 389, 13, 49), - (11, 344, 13, 49), - (291, 346, 13, 49), - ] { + for (x, y, width, height) in [(151, 389, 13, 49), (11, 344, 13, 49), (291, 346, 13, 49)] + { self.draw_active_table_region(x, y, width, height); } } @@ -605,12 +634,7 @@ impl App { } if let Some(item) = self.attract.item_index() { - draw_texture( - &self.assets.diamond[item], - 123.0, - 300.0, - WHITE, - ); + draw_texture(&self.assets.diamond[item], 123.0, 300.0, WHITE); } self.draw_intro_marquee(); } @@ -929,7 +953,15 @@ impl App { } else if (66..=128).contains(&frame) { draw_texture_region(&self.assets.wheel, 79.0, 33.0, 91.0, 90.0, 180.0, 90.0); if frame < 83 { - draw_texture_region(wide_a, 79.0, ((82 - frame) * 2) as f32, 91.0, 90.0, 0.0, 0.0); + draw_texture_region( + wide_a, + 79.0, + ((82 - frame) * 2) as f32, + 91.0, + 90.0, + 0.0, + 0.0, + ); } else { let height = (128 - frame) * 2; draw_texture_region( @@ -1122,14 +1154,7 @@ impl App { draw_centered("SETTINGS", 122.0, 30, BLACK); let rows = [ format!("GRAPHICS DETAIL / SPEED: {}", self.saved.settings.speed), - format!( - "SOUND: {}", - if self.sounds_enabled { - "ON" - } else { - "OFF" - } - ), + format!("SOUND: {}", if self.sounds_enabled { "ON" } else { "OFF" }), format!("LANGUAGE: {}", self.saved.settings.language.label()), ]; for (index, row) in rows.iter().enumerate() { @@ -1181,7 +1206,13 @@ impl App { draw_rectangle(x, y, 270.0, 124.0, Color::from_rgba(192, 192, 192, 255)); draw_rectangle_lines(x, y, 270.0, 124.0, 2.0, WHITE); draw_rectangle_lines(x + 2.0, y + 2.0, 266.0, 120.0, 2.0, DARKGRAY); - draw_rectangle(x + 4.0, y + 4.0, 262.0, 20.0, Color::from_rgba(0, 0, 128, 255)); + draw_rectangle( + x + 4.0, + y + 4.0, + 262.0, + 20.0, + Color::from_rgba(0, 0, 128, 255), + ); draw_text( "Congratulations! This is a Top Ten Score!", x + 9.0, @@ -1319,13 +1350,7 @@ mod tests { #[test] fn attract_timer_uses_the_selected_detail_callback() { - for (detail, interval) in [ - (1, 0.050), - (2, 0.040), - (3, 0.030), - (4, 0.020), - (5, 0.010), - ] { + for (detail, interval) in [(1, 0.050), (2, 0.040), (3, 0.030), (4, 0.020), (5, 0.010)] { let mut animation = AttractAnimation::default(); animation.update(interval - 0.001, detail); assert_eq!(animation.tick, 0); diff --git a/tdkpin-rs/src/assets.rs b/tdkpin-rs/src/assets.rs index eed37a6..e20bc4e 100644 --- a/tdkpin-rs/src/assets.rs +++ b/tdkpin-rs/src/assets.rs @@ -33,7 +33,8 @@ impl Assets { let inactive_table = texture(include_bytes!("../assets/original/images/dat_00998.png")); let loading = texture(include_bytes!("../assets/original/images/dat_00995.png")); let loading_progress = texture(include_bytes!("../assets/original/images/dat_00994.png")); - let highscore_background = texture(include_bytes!("../assets/original/images/dat_00993.png")); + let highscore_background = + texture(include_bytes!("../assets/original/images/dat_00993.png")); let help = [ texture(include_bytes!("../assets/original/images/dat_01001.png")), texture(include_bytes!("../assets/original/images/dat_01002.png")), diff --git a/tdkpin-rs/src/borland_random.rs b/tdkpin-rs/src/borland_random.rs index 2f761a6..d98c723 100644 --- a/tdkpin-rs/src/borland_random.rs +++ b/tdkpin-rs/src/borland_random.rs @@ -77,15 +77,16 @@ mod tests { fn zero_bounds_still_advance_the_seed() { let mut random = BorlandRandom::new(7); assert_eq!(random.below(0), 0); - assert_eq!(random.seed(), 7_u32.wrapping_mul(MULTIPLIER).wrapping_add(1)); + assert_eq!( + random.seed(), + 7_u32.wrapping_mul(MULTIPLIER).wrapping_add(1) + ); } #[test] fn unit_interval_is_the_exact_unsigned_seed_fraction() { let mut random = BorlandRandom::new(0xfedc_ba98); - let expected_seed = 0xfedc_ba98_u32 - .wrapping_mul(MULTIPLIER) - .wrapping_add(1); + let expected_seed = 0xfedc_ba98_u32.wrapping_mul(MULTIPLIER).wrapping_add(1); assert_eq!( random.unit_interval().to_bits(), (f64::from(expected_seed) / TWO_TO_32).to_bits() diff --git a/tdkpin-rs/src/flipper_physics.rs b/tdkpin-rs/src/flipper_physics.rs index bcdd3c7..01f1db6 100644 --- a/tdkpin-rs/src/flipper_physics.rs +++ b/tdkpin-rs/src/flipper_physics.rs @@ -5,8 +5,7 @@ use crate::{original_physics::MilliVec, real48::Real48}; const SEARCH_RADIUS: i32 = 54_000; const ONE: Real48 = Real48::from_bytes([0x81, 0, 0, 0, 0, 0]); const TWO: Real48 = Real48::from_bytes([0x82, 0, 0, 0, 0, 0]); -const TWO_FIFTHS: Real48 = - Real48::from_bytes([0x7f, 0xcd, 0xcc, 0xcc, 0xcc, 0x4c]); +const TWO_FIFTHS: Real48 = Real48::from_bytes([0x7f, 0xcd, 0xcc, 0xcc, 0xcc, 0x4c]); const THOUSAND: Real48 = Real48::from_bytes([0x8a, 0, 0, 0, 0, 0x7a]); #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -171,14 +170,11 @@ fn penetration(geometry: Geometry, delta: i32, ball: MilliVec) -> i32 { .wrapping_add(43_000) .wrapping_sub(geometry.pivot.y), ); - geometry - .pivot - .y - .wrapping_sub( - Real48::from_i32(numerator) - .divide(Real48::from_i32(edge_dx)) - .round_i32(), - ) + geometry.pivot.y.wrapping_sub( + Real48::from_i32(numerator) + .divide(Real48::from_i32(edge_dx)) + .round_i32(), + ) } else { geometry.positive_edge.y.wrapping_add(43_000) } @@ -299,8 +295,14 @@ mod tests { let velocities = [ MilliVec { x: 0, y: 2_000 }, MilliVec { x: 1_000, y: 2_000 }, - MilliVec { x: -1_000, y: 2_000 }, - MilliVec { x: 2_700, y: -2_700 }, + MilliVec { + x: -1_000, + y: 2_000, + }, + MilliVec { + x: 2_700, + y: -2_700, + }, ]; let mut hash = 14_695_981_039_346_656_037_u64; let mut hits = 0_u32; @@ -315,10 +317,10 @@ mod tests { Real48::from_bytes([0x80, 0, 0, 0, 0, 0]), 3_800, ); - let (hit, velocity, movement) = response.map_or( - (0_u32, input_velocity, MilliVec::default()), - |response| (1, response.velocity, response.movement), - ); + let (hit, velocity, movement) = response + .map_or((0_u32, input_velocity, MilliVec::default()), |response| { + (1, response.velocity, response.movement) + }); hits += hit; for value in [ i32_bits(x), @@ -342,40 +344,73 @@ mod tests { fn four_direction_vectors_match_the_reconstructed_c_harness() { let cases = [ ( - MilliVec { x: 104_000, y: 384_000 }, + MilliVec { + x: 104_000, + y: 384_000, + }, MilliVec { x: 1_000, y: 2_000 }, -1, FlipperSide::Left, Real48::from_bytes([0x80, 0, 0, 0, 0, 0]), MilliVec { x: 1_266, y: 1_511 }, - MilliVec { x: -6_703, y: -8_000 }, + MilliVec { + x: -6_703, + y: -8_000, + }, ), ( - MilliVec { x: 209_000, y: 419_000 }, - MilliVec { x: -1_000, y: 2_000 }, + MilliVec { + x: 209_000, + y: 419_000, + }, + MilliVec { + x: -1_000, + y: 2_000, + }, 1, FlipperSide::Right, Real48::from_bytes([0x80, 0, 0, 0, 0, 0x40]), MilliVec { x: -737, y: 2_263 }, - MilliVec { x: -5_578, y: 17_127 }, + MilliVec { + x: -5_578, + y: 17_127, + }, ), ( - MilliVec { x: 104_000, y: 421_000 }, + MilliVec { + x: 104_000, + y: 421_000, + }, MilliVec { x: 1_000, y: 2_000 }, 1, FlipperSide::Left, Real48::from_bytes([0x7f, 0, 0, 0, 0, 0]), MilliVec { x: 622, y: 2_320 }, - MilliVec { x: 5_414, y: 20_193 }, + MilliVec { + x: 5_414, + y: 20_193, + }, ), ( - MilliVec { x: 209_000, y: 385_000 }, - MilliVec { x: -1_000, y: 2_000 }, + MilliVec { + x: 209_000, + y: 385_000, + }, + MilliVec { + x: -1_000, + y: 2_000, + }, -1, FlipperSide::Right, Real48::ZERO, - MilliVec { x: -1_280, y: 1_560 }, - MilliVec { x: 7_385, y: -9_000 }, + MilliVec { + x: -1_280, + y: 1_560, + }, + MilliVec { + x: 7_385, + y: -9_000, + }, ), ]; for (ball, velocity, delta, side, response, expected_velocity, expected_movement) in cases { @@ -398,7 +433,10 @@ mod tests { fn boundary_vectors_match_the_live_original_binary() { for (ball, velocity, delta, side, expected_velocity, expected_movement) in [ ( - MilliVec { x: 100_000, y: 370_000 }, + MilliVec { + x: 100_000, + y: 370_000, + }, MilliVec { x: 1_000, y: 2_000 }, -1, FlipperSide::Left, @@ -406,7 +444,10 @@ mod tests { MilliVec::default(), ), ( - MilliVec { x: 209_000, y: 419_000 }, + MilliVec { + x: 209_000, + y: 419_000, + }, MilliVec { x: 1_000, y: 2_000 }, 1, FlipperSide::Right, diff --git a/tdkpin-rs/src/game.rs b/tdkpin-rs/src/game.rs index 3ad49fa..1c2d1a4 100644 --- a/tdkpin-rs/src/game.rs +++ b/tdkpin-rs/src/game.rs @@ -1,20 +1,34 @@ +use macroquad::prelude::{Rect, Vec2, vec2}; + use crate::{ borland_random::BorlandRandom, flipper_physics::{FlipperSide, moving_flipper_response}, geometry::Segment, original_physics::{ - CollisionMaterial, CollisionResponse, GRAVITY_MILLI_PER_STEP, - MAXIMUM_SPEED_MILLI_PER_STEP, MilliVec, StaticCollisionCandidate, - ball_collision_response, capture_collision_candidate, circle_collision_candidate_at, - line_collision_candidate_at, milli_distance, path_intersects_circle, + CollisionMaterial, + CollisionResponse, + GRAVITY_MILLI_PER_STEP, + MAXIMUM_SPEED_MILLI_PER_STEP, + MilliVec, + StaticCollisionCandidate, + ball_collision_response, + capture_collision_candidate, + circle_collision_candidate_at, + line_collision_candidate_at, + milli_distance, + path_intersects_circle, }, real48::Real48, table::{ - BUMPERS, EFFECT_SENSOR, LOCK_HOLES, PASSIVE_CIRCLES, TARGET_SENSORS, WALLS, + BUMPERS, + EFFECT_SENSOR, + LOCK_HOLES, + PASSIVE_CIRCLES, SPECIAL_HOLE_SENSOR, + TARGET_SENSORS, + WALLS, }, }; -use macroquad::prelude::{Rect, Vec2, vec2}; const LEFT_FLIPPER_RAISED_TIP: Vec2 = Vec2::new(133.0, 377.0); const RIGHT_FLIPPER_RAISED_TIP: Vec2 = Vec2::new(181.0, 376.0); @@ -483,7 +497,8 @@ impl Game { let variation = self.random.below(3_800) / 40; launch_velocity = -MAXIMUM_SPEED_MILLI_PER_STEP + i32::from(variation); } else if launch_velocity > -2_280 { - launch_velocity = launch_velocity.wrapping_sub(i32::from(self.random.below(3_800) / 40)); + launch_velocity = + launch_velocity.wrapping_sub(i32::from(self.random.below(3_800) / 40)); } self.ball.in_launcher = false; self.ball.velocity = MilliVec { @@ -516,12 +531,10 @@ impl Game { } else if self.tilted { self.flipper_release_latch[1] = true; } - self.flipper_inputs.left_raised = controls.left_flipper - && !self.tilted - && !self.flipper_release_latch[0]; - self.flipper_inputs.right_raised = controls.right_flipper - && !self.tilted - && !self.flipper_release_latch[1]; + self.flipper_inputs.left_raised = + controls.left_flipper && !self.tilted && !self.flipper_release_latch[0]; + self.flipper_inputs.right_raised = + controls.right_flipper && !self.tilted && !self.flipper_release_latch[1]; if self.ball.in_launcher && !self.tilted { if controls.launch_down { @@ -576,10 +589,7 @@ impl Game { self.score_mode = ScoreMode::Multiball; } for _ in 0..substeps { - if self.fixed_update(events) - || self.finished - || self.claw.ball_suspended - { + if self.fixed_update(events) || self.finished || self.claw.ball_suspended { break; } } @@ -609,22 +619,18 @@ impl Game { } } - self.pending_flipper_edges[0] = match ( - self.flipper_inputs.left_raised, - self.flippers.left_raised, - ) { - (true, false) => -1, - (false, true) => 1, - _ => 0, - }; - self.pending_flipper_edges[1] = match ( - self.flipper_inputs.right_raised, - self.flippers.right_raised, - ) { - (true, false) => -1, - (false, true) => 1, - _ => 0, - }; + self.pending_flipper_edges[0] = + match (self.flipper_inputs.left_raised, self.flippers.left_raised) { + (true, false) => -1, + (false, true) => 1, + _ => 0, + }; + self.pending_flipper_edges[1] = + match (self.flipper_inputs.right_raised, self.flippers.right_raised) { + (true, false) => -1, + (false, true) => 1, + _ => 0, + }; self.flippers = self.flipper_inputs; for edge in self.pending_flipper_edges { if edge != 0 { @@ -706,7 +712,11 @@ impl Game { match nudge { Nudge::Left | Nudge::Right => { let amount = (50 - i32::from(self.random.below(20))) * SCALAR; - let signed = if nudge == Nudge::Left { -amount } else { amount }; + let signed = if nudge == Nudge::Left { + -amount + } else { + amount + }; velocity.x = velocity.x.wrapping_add(signed); } Nudge::Center => { @@ -726,12 +736,10 @@ impl Game { } if let Some(secondary) = &mut self.secondary_ball { let slot_impulse = match nudge { - Nudge::Left | Nudge::Right => { - MilliVec { - x: if nudge == Nudge::Left { -600 } else { 600 }, - y: 0, - } - } + Nudge::Left | Nudge::Right => MilliVec { + x: if nudge == Nudge::Left { -600 } else { 600 }, + y: 0, + }, Nudge::Center => { let horizontal = (i32::from(self.random.below(21)) - 10) * SCALAR; let vertical = -(i32::from(self.random.below(100)) + 50) * SCALAR; @@ -1070,13 +1078,7 @@ impl Game { best = Some((id, false, candidate)); } ball.velocity = velocity.to_velocity_per_second(); - ( - best, - SensorScanResult { - action, - }, - predicted, - ) + (best, SensorScanResult { action }, predicted) } fn find_static_collision_candidate_in_range( @@ -1317,12 +1319,7 @@ impl Game { collided || scan.action == BallAction::Suspend } - fn apply_bumper_rule( - &mut self, - object_id: u8, - auxiliary_fired: bool, - events: &mut Vec, - ) { + fn apply_bumper_rule(&mut self, object_id: u8, auxiliary_fired: bool, events: &mut Vec) { if self.tilted || !BUMPERS.iter().any(|bumper| bumper.id == object_id) { return; } @@ -1504,11 +1501,7 @@ impl Game { if !self.tilted && (TARGET_SENSORS.into_iter().any(|sensor| { self.object_active[usize::from(sensor.id)] - && trigger_broadphase_contains( - predicted_position, - sensor.center, - sensor.radius, - ) + && trigger_broadphase_contains(predicted_position, sensor.center, sensor.radius) }) || self.object_active[usize::from(EFFECT_SENSOR.id)] && trigger_broadphase_contains( predicted_position, @@ -1549,56 +1542,32 @@ impl Game { } } - if let Some(completed_action) = - self.check_lock_holes( - ball, - ball_number, - ball_count, - old_position, - predicted_position, - &mut capture_candidate, - events, - ) - { - action = completed_action; - } - self.check_target_sensors( - ball, - old_position, - movement_velocity, - 140..=147, - events, - ); - if let Some(completed_action) = - self.check_special_hole( - ball, - ball_number, - ball_count, - old_position, - predicted_position, - &mut capture_candidate, - events, - ) - { - action = completed_action; - } - self.check_effect_sensor( + if let Some(completed_action) = self.check_lock_holes( ball, ball_number, + ball_count, old_position, - movement_velocity, + predicted_position, + &mut capture_candidate, events, - ); - self.check_target_sensors( - ball, - old_position, - movement_velocity, - 150..=152, - events, - ); - SensorScanResult { - action, + ) { + action = completed_action; } + self.check_target_sensors(ball, old_position, movement_velocity, 140..=147, events); + if let Some(completed_action) = self.check_special_hole( + ball, + ball_number, + ball_count, + old_position, + predicted_position, + &mut capture_candidate, + events, + ) { + action = completed_action; + } + self.check_effect_sensor(ball, ball_number, old_position, movement_velocity, events); + self.check_target_sensors(ball, old_position, movement_velocity, 150..=152, events); + SensorScanResult { action } } #[allow(clippy::cast_possible_truncation, clippy::too_many_arguments)] @@ -1858,7 +1827,8 @@ impl Game { self.players[self.current_player].secondary_score = 0; } 7 if self.record_contacts[usize::from(SPECIAL_HOLE_SENSOR.id)] != 0 - && self.record_contacts[usize::from(SPECIAL_HOLE_SENSOR.id)] != ball_number + && self.record_contacts[usize::from(SPECIAL_HOLE_SENSOR.id)] + != ball_number && self.secondary_ball.is_none() && matches!( self.multiball_state, @@ -1999,10 +1969,7 @@ impl Game { .round_i32() .wrapping_neg(); let predicted = old_position.add(*velocity); - if predicted.x < min_x - || predicted.x > max_x - || predicted.y < min_y - || predicted.y > max_y + if predicted.x < min_x || predicted.x > max_x || predicted.y < min_y || predicted.y > max_y { self.object_active[object_id] = false; } @@ -2225,7 +2192,10 @@ impl Game { fn special_respawn_pending(&self) -> bool { self.secondary_ball.is_none() && self.score_mode == ScoreMode::Normal - && matches!(self.multiball_state, MultiballState::Ready | MultiballState::Active) + && matches!( + self.multiball_state, + MultiballState::Ready | MultiballState::Active + ) } fn save_current_rule_state(&mut self) { @@ -2336,16 +2306,14 @@ fn claw_release(frame: u8) -> (Vec2, Vec2) { fn apply_flipper_response_to_ball(ball: &mut Ball, delta: i32, side: FlipperSide) { let position = MilliVec::from_position(ball.position); let velocity = MilliVec::from_velocity_per_second(ball.velocity); - if let Some(response) = - moving_flipper_response( - position, - velocity, - delta, - side, - Real48::from_bytes([0x80, 0, 0, 0, 0, 0]), - MAXIMUM_SPEED_MILLI_PER_STEP, - ) - { + if let Some(response) = moving_flipper_response( + position, + velocity, + delta, + side, + Real48::from_bytes([0x80, 0, 0, 0, 0, 0]), + MAXIMUM_SPEED_MILLI_PER_STEP, + ) { ball.position = position.add(response.movement).to_position(); ball.velocity = response.velocity.to_velocity_per_second(); } @@ -2379,11 +2347,7 @@ mod tests { game.ball.in_launcher = false; game.ball.position = center; game.ball.velocity = Vec2::ZERO; - game.check_sensor_objects( - MilliVec::from_position(center), - MilliVec::default(), - events, - ); + game.check_sensor_objects(MilliVec::from_position(center), MilliVec::default(), events); } } @@ -2397,7 +2361,8 @@ mod tests { ) -> CaptureStep { let mut ball = game.ball; let mut capture_candidate = None; - let predicted_position = previous_position.add(MilliVec::from_velocity_per_second(ball.velocity)); + let predicted_position = + previous_position.add(MilliVec::from_velocity_per_second(ball.velocity)); let result = game.capture_record_step( &mut ball, 1, @@ -2456,8 +2421,8 @@ mod tests { game.object_active[usize::from(wall.id)] = true; game.ball.in_launcher = false; game.ball.position = midpoint - normal; - game.ball.velocity = MilliVec::from_velocity_per_second(normal * 200.0) - .to_velocity_per_second(); + game.ball.velocity = + MilliVec::from_velocity_per_second(normal * 200.0).to_velocity_per_second(); game.fixed_update(&mut Vec::new()); if game.last_collision_id != Some(wall.id) { missed_walls.push(wall.id); @@ -2478,7 +2443,10 @@ mod tests { } } - assert!(missed_walls.is_empty(), "missed production wall records: {missed_walls:?}"); + assert!( + missed_walls.is_empty(), + "missed production wall records: {missed_walls:?}" + ); assert!( missed_circles.is_empty(), "missed production circle records: {missed_circles:?}" @@ -2869,9 +2837,11 @@ mod tests { assert_eq!(special.multiball_state, MultiballState::Unavailable); assert_eq!(special.record_contacts[148], 0); assert!(!special_events.contains(&Event::Drain)); - assert!(!special_events - .iter() - .any(|event| event.sound_resource().is_some())); + assert!( + !special_events + .iter() + .any(|event| event.sound_resource().is_some()) + ); } #[test] @@ -3179,10 +3149,7 @@ mod tests { ); assert_eq!( MilliVec::from_velocity_per_second(game.ball.velocity), - MilliVec { - x: 0, - y: 3_055, - } + MilliVec { x: 0, y: 3_055 } ); assert_eq!(game.special_hole_gate, SpecialHoleGate::Suppressed); @@ -3255,10 +3222,7 @@ mod tests { ); assert_eq!( MilliVec::from_velocity_per_second(game.ball.velocity), - MilliVec { - x: -302, - y: -1_809, - } + MilliVec { x: -302, y: -1_809 } ); assert_eq!(game.multiball_state, MultiballState::Unavailable); assert_eq!(game.special_hole_gate, SpecialHoleGate::Suppressed); @@ -3297,9 +3261,7 @@ mod tests { spin: Real48::ZERO, capture_age: 0, }; - let old_position = MilliVec::from_position( - sensor.center - vec2(sensor.radius + 1.0, 0.0), - ); + let old_position = MilliVec::from_position(sensor.center - vec2(sensor.radius + 1.0, 0.0)); let current_position = MilliVec::from_position(sensor.center); let movement = MilliVec { x: current_position.x.wrapping_sub(old_position.x), @@ -3391,7 +3353,10 @@ mod tests { BallAction::Keep ); - assert_eq!(game.secondary_ball.map(|ball| ball.position), Some(secondary.position)); + assert_eq!( + game.secondary_ball.map(|ball| ball.position), + Some(secondary.position) + ); assert_eq!(game.multiball_state, MultiballState::Active); assert_eq!(game.target_effect, 0); } @@ -3511,7 +3476,6 @@ mod tests { assert!(game.secondary_ball.is_none()); assert_eq!(game.player().balls, 3); assert_eq!(game.score_mode, ScoreMode::Normal); - } #[test] @@ -3605,10 +3569,7 @@ mod tests { assert!(game.fixed_update(&mut events)); assert_eq!(game.last_collision_id, Some(57)); - assert_eq!( - game.ball.spin.bytes(), - [0x83, 0x5d, 0x8f, 0xc2, 0xf5, 0xa8] - ); + assert_eq!(game.ball.spin.bytes(), [0x83, 0x5d, 0x8f, 0xc2, 0xf5, 0xa8]); assert_eq!( MilliVec::from_position(game.ball.position), MilliVec { @@ -3645,10 +3606,7 @@ mod tests { assert!(game.fixed_update(&mut events)); assert_eq!(game.last_collision_id, Some(72)); - assert_eq!( - game.ball.spin.bytes(), - [0x83, 0x86, 0xeb, 0x51, 0xb8, 0x2e] - ); + assert_eq!(game.ball.spin.bytes(), [0x83, 0x86, 0xeb, 0x51, 0xb8, 0x2e]); assert_eq!( MilliVec::from_position(game.ball.position), MilliVec { @@ -3658,7 +3616,10 @@ mod tests { ); assert_eq!( MilliVec::from_velocity_per_second(game.ball.velocity), - MilliVec { x: -2_095, y: 1_669 } + MilliVec { + x: -2_095, + y: 1_669 + } ); } @@ -3928,8 +3889,7 @@ mod tests { #[test] fn raised_flipper_tips_use_their_asymmetric_swept_bounds() { - for (object_id, center) in [(67_u8, vec2(133.0, 377.0)), (82, vec2(181.0, 376.0))] - { + for (object_id, center) in [(67_u8, vec2(133.0, 377.0)), (82, vec2(181.0, 376.0))] { let mut game = Game::new(1); game.object_active.fill(false); game.object_active[usize::from(object_id)] = true; @@ -4012,7 +3972,13 @@ mod tests { let velocity_after_edge = game.ball.velocity; assert_eq!(game.ball.position, vec2(138.124, 355.0)); let raw_velocity = MilliVec::from_velocity_per_second(velocity_after_edge); - assert_eq!(raw_velocity, MilliVec { x: 2_209, y: -5_891 }); + assert_eq!( + raw_velocity, + MilliVec { + x: 2_209, + y: -5_891 + } + ); assert_eq!(game.pending_flipper_edges, [0, 0]); game.apply_flipper_kicks(); @@ -4102,20 +4068,20 @@ mod tests { #[test] fn tilt_counter_decays_once_per_selected_detail_callback() { - for (detail, interval) in [ - (1, 0.050), - (2, 0.040), - (3, 0.030), - (4, 0.020), - (5, 0.010), - ] { + for (detail, interval) in [(1, 0.050), (2, 0.040), (3, 0.030), (4, 0.020), (5, 0.010)] { let mut game = Game::new(1); game.tilt_counter = 2; game.update(interval - 0.001, detail, Controls::default()); - assert_eq!(game.tilt_counter, 2, "detail {detail} decayed before its callback"); + assert_eq!( + game.tilt_counter, 2, + "detail {detail} decayed before its callback" + ); game.update(0.001_1, detail, Controls::default()); - assert_eq!(game.tilt_counter, 1, "detail {detail} did not decay at its callback"); + assert_eq!( + game.tilt_counter, 1, + "detail {detail} did not decay at its callback" + ); } } @@ -4462,11 +4428,7 @@ mod tests { 5 ); - complete_stationary_type_three_capture( - &mut game, - SPECIAL_HOLE_SENSOR.center, - &mut events, - ); + complete_stationary_type_three_capture(&mut game, SPECIAL_HOLE_SENSOR.center, &mut events); assert!(game.wheel_holes.iter().all(|filled| *filled)); assert_eq!(game.multiball_state, MultiballState::Ready); assert_eq!(game.record_contacts[148], 2); @@ -4696,7 +4658,11 @@ mod tests { assert_eq!(game.panel_frame, None); assert_eq!(game.wheel_holes, [false; 5]); - assert!(game.record_contacts[129..=133].iter().all(|contact| *contact == 0)); + assert!( + game.record_contacts[129..=133] + .iter() + .all(|contact| *contact == 0) + ); assert_eq!( events, [ @@ -4758,7 +4724,11 @@ mod tests { game.record_countdowns[51] = 5; game.timer_tick(0.030, 0, &mut Vec::new()); - assert_eq!(game.record_countdown(51), 5, "launcher idle pauses countdowns"); + assert_eq!( + game.record_countdown(51), + 5, + "launcher idle pauses countdowns" + ); game.ball.in_launcher = false; for expected in (0..5).rev() { diff --git a/tdkpin-rs/src/original_physics.rs b/tdkpin-rs/src/original_physics.rs index e787743..5df9047 100644 --- a/tdkpin-rs/src/original_physics.rs +++ b/tdkpin-rs/src/original_physics.rs @@ -73,11 +73,7 @@ impl CollisionMaterial { } } - pub const fn circle( - normal_rebound: f64, - tangent_coupling: f64, - normal_kick: f64, - ) -> Self { + pub const fn circle(normal_rebound: f64, tangent_coupling: f64, normal_kick: f64) -> Self { Self { normal_rebound, tangent_coupling, @@ -173,8 +169,7 @@ impl MilliVec { // `maximum / speed` is mathematically equivalent, but can differ by // one millipixel because each Real48 operation is rounded // independently. - let excess = Real48::from_i32(speed.wrapping_sub(maximum)) - .divide(Real48::from_i32(speed)); + let excess = Real48::from_i32(speed.wrapping_sub(maximum)).divide(Real48::from_i32(speed)); self.x = self .x .wrapping_sub(Real48::from_i32(self.x).multiply(excess).round_i32()); @@ -262,11 +257,7 @@ fn tangent_velocity(velocity: MilliVec, normal_x: Real48, normal_y: Real48, leng .round_i32() } -fn cross_at_endpoint( - point: MilliVec, - current: MilliVec, - predicted: MilliVec, -) -> i32 { +fn cross_at_endpoint(point: MilliVec, current: MilliVec, predicted: MilliVec) -> i32 { predicted .x .wrapping_sub(point.x) @@ -383,9 +374,7 @@ pub fn line_collision_candidate_at( let vertical_units = normal_y.divide(THOUSAND); let distance = Real48::from_i32(old_position.x.wrapping_sub(start.x)) .multiply(vertical_units) - .subtract( - Real48::from_i32(old_position.y.wrapping_sub(start.y)).multiply(horizontal_units), - ) + .subtract(Real48::from_i32(old_position.y.wrapping_sub(start.y)).multiply(horizontal_units)) .divide(length_units); let distance = if distance.compare(ZERO).is_lt() { ZERO.subtract(distance) @@ -525,10 +514,7 @@ pub fn capture_collision_candidate( let radius_milli = (radius * 1_000.0).round() as i32; let delta = MilliVec { x: center.x.wrapping_sub(old_position.x), - y: center - .y - .wrapping_sub(old_position.y) - .wrapping_sub(2_000), + y: center.y.wrapping_sub(old_position.y).wrapping_sub(2_000), }; let surface_distance = milli_distance(delta).wrapping_sub(radius_milli); let normal_x = Real48::from_i32(center.y.wrapping_sub(old_position.y)); @@ -541,9 +527,7 @@ pub fn capture_collision_candidate( return None; } let normal_velocity = normal_velocity(velocity, normal_x, normal_y, length); - if normal_velocity >= 0 - || normal_velocity.wrapping_abs() < surface_distance.wrapping_abs() - { + if normal_velocity >= 0 || normal_velocity.wrapping_abs() < surface_distance.wrapping_abs() { return None; } Some(StaticCollisionCandidate { @@ -681,8 +665,14 @@ mod tests { #[test] fn float_views_roundtrip_every_gameplay_velocity_millipixel() { for value in -10_000..=10_000 { - let milli = MilliVec { x: value, y: -value }; - assert_eq!(MilliVec::from_velocity_per_second(milli.to_velocity_per_second()), milli); + let milli = MilliVec { + x: value, + y: -value, + }; + assert_eq!( + MilliVec::from_velocity_per_second(milli.to_velocity_per_second()), + milli + ); } } @@ -697,8 +687,7 @@ mod tests { if speed <= maximum { continue; } - let excess = Real48::from_i32(speed - maximum) - .divide(Real48::from_i32(speed)); + let excess = Real48::from_i32(speed - maximum).divide(Real48::from_i32(speed)); let expected = MilliVec { x: x.wrapping_sub(Real48::from_i32(x).multiply(excess).round_i32()), y: y.wrapping_sub(Real48::from_i32(y).multiply(excess).round_i32()), @@ -716,7 +705,10 @@ mod tests { break; } } - assert!(mismatches.is_empty(), "speed-clamp mismatches: {mismatches:?}"); + assert!( + mismatches.is_empty(), + "speed-clamp mismatches: {mismatches:?}" + ); } #[test] @@ -783,7 +775,9 @@ mod tests { assert_eq!(candidate.surface_distance, -800); assert_ne!( - candidate.resolve(MilliVec { x: 1_000, y: 0 }, Real48::ZERO).velocity, + candidate + .resolve(MilliVec { x: 1_000, y: 0 }, Real48::ZERO) + .velocity, MilliVec { x: 1_000, y: 0 } ); } @@ -887,16 +881,15 @@ mod tests { fn surface_distance_orders_candidate_contacts() { let old = MilliVec::default(); let velocity = MilliVec { x: 10_000, y: 0 }; - let near = - line_collision_response( - old, - velocity, - vec2(2.0, 1.0), - vec2(2.0, -1.0), - CollisionMaterial::line(0.6, 0.1), - Real48::ZERO, - ) - .expect("near rail should be crossed"); + let near = line_collision_response( + old, + velocity, + vec2(2.0, 1.0), + vec2(2.0, -1.0), + CollisionMaterial::line(0.6, 0.1), + Real48::ZERO, + ) + .expect("near rail should be crossed"); let far = line_collision_response( old, velocity, diff --git a/tdkpin-rs/src/persistence.rs b/tdkpin-rs/src/persistence.rs index 2b50567..82810f5 100644 --- a/tdkpin-rs/src/persistence.rs +++ b/tdkpin-rs/src/persistence.rs @@ -1,6 +1,7 @@ +use std::{fs, io, path::PathBuf}; + use directories::ProjectDirs; use serde::{Deserialize, Serialize}; -use std::{fs, io, path::PathBuf}; const ORIGINAL_HIGHSCORES: &[u8] = include_bytes!("../assets/original/HISCORES.DAT"); const ORIGINAL_INI: &str = include_str!("../../original/TDKPIN.INI"); @@ -154,7 +155,10 @@ pub fn parse_original_settings(text: &str) -> Settings { _ => Language::English, }; } else if key.trim().eq_ignore_ascii_case("Speed") { - settings.speed = u8::try_from(value).ok().filter(|speed| (1..=5).contains(speed)).unwrap_or(3); + settings.speed = u8::try_from(value) + .ok() + .filter(|speed| (1..=5).contains(speed)) + .unwrap_or(3); } else if key.trim().eq_ignore_ascii_case("Sound") { settings.sounds = value != 0; } @@ -249,14 +253,16 @@ mod tests { let settings = parse_original_settings(ORIGINAL_INI); assert_eq!(settings.speed, 3); assert_eq!(settings.language, Language::German); - assert!(settings.sounds, "the original reads singular Sound, not Sounds"); + assert!( + settings.sounds, + "the original reads singular Sound, not Sounds" + ); } #[test] fn original_ini_values_are_case_insensitive_and_clamped() { - let settings = parse_original_settings( - "[settings]\nlanguage=5\nSPEED=9\nSound=0\nSounds=1\n", - ); + let settings = + parse_original_settings("[settings]\nlanguage=5\nSPEED=9\nSound=0\nSounds=1\n"); assert_eq!(settings.speed, 3); assert_eq!(settings.language, Language::Spanish); assert!(!settings.sounds); diff --git a/tdkpin-rs/src/real48.rs b/tdkpin-rs/src/real48.rs index 7946a4e..ede7227 100644 --- a/tdkpin-rs/src/real48.rs +++ b/tdkpin-rs/src/real48.rs @@ -167,8 +167,7 @@ impl Real48 { } let mut numerator = self.significand(); let denominator = right.significand(); - let mut exponent = - i16::from(self.exponent()) - i16::from(right.exponent()) + EXPONENT_BIAS; + let mut exponent = i16::from(self.exponent()) - i16::from(right.exponent()) + EXPONENT_BIAS; if numerator < denominator { numerator <<= 1; exponent -= 1; @@ -254,16 +253,8 @@ impl Real48 { assert!(highest_bit < 32, "Real48 integer overflow"); let significand = self.significand(); let shift = i16::try_from(FRACTION_BITS).unwrap_or(39) - highest_bit; - let mut magnitude = if shift >= 64 { - 0 - } else { - significand >> shift - }; - if round - && shift > 0 - && shift <= 40 - && (significand >> (shift - 1)) & 1 != 0 - { + let mut magnitude = if shift >= 64 { 0 } else { significand >> shift }; + if round && shift > 0 && shift <= 40 && (significand >> (shift - 1)) & 1 != 0 { magnitude += 1; } let limit = if self.negative() { @@ -344,7 +335,10 @@ mod tests { assert_eq!(HALF.round_i32(), 1); assert_eq!(Real48::from_bytes([0x80, 0, 0, 0, 0, 0x80]).round_i32(), -1); assert_eq!(ONE.compare(TWO), Ordering::Less); - assert_eq!(Real48::from_i32(-2).compare(Real48::from_i32(-1)), Ordering::Less); + assert_eq!( + Real48::from_i32(-2).compare(Real48::from_i32(-1)), + Ordering::Less + ); assert_eq!(TWO.sqrt().bytes(), [0x81, 0xfa, 0x33, 0xf3, 0x04, 0x35]); assert_eq!( ONE_AND_HALF.sqrt().bytes(), diff --git a/tdkpin-rs/src/simulation.rs b/tdkpin-rs/src/simulation.rs index 0def3db..f072057 100644 --- a/tdkpin-rs/src/simulation.rs +++ b/tdkpin-rs/src/simulation.rs @@ -1,11 +1,13 @@ -use crate::game::{ClawSpriteBank, Controls, Event, Game}; -use serde::Serialize; use std::{ fs, path::{Path, PathBuf}, str::FromStr, }; +use serde::Serialize; + +use crate::game::{ClawSpriteBank, Controls, Event, Game}; + pub const SIMULATION_HZ: u32 = 120; const SIMULATION_HZ_F64: f64 = 120.0; const SIMULATION_DT: f32 = 1.0 / 120.0; @@ -376,9 +378,7 @@ fn controls_for(scenario: Scenario, step: u64) -> Controls { | Scenario::Claw1 | Scenario::Claw6 | Scenario::Claw7 - | Scenario::Claw18 => { - Controls::default() - } + | Scenario::Claw18 => Controls::default(), } } @@ -507,9 +507,24 @@ mod tests { .iter() .flat_map(|snapshot| snapshot.events.iter().map(String::as_str)) .collect::>(); - assert_eq!(events.iter().filter(|event| **event == "StopSound").count(), 3); - assert_eq!(events.iter().filter(|event| **event == "Sound(2013)").count(), 4); - assert_eq!(events.iter().filter(|event| **event == "Sound(2012)").count(), 2); + assert_eq!( + events.iter().filter(|event| **event == "StopSound").count(), + 3 + ); + assert_eq!( + events + .iter() + .filter(|event| **event == "Sound(2013)") + .count(), + 4 + ); + assert_eq!( + events + .iter() + .filter(|event| **event == "Sound(2012)") + .count(), + 2 + ); } #[test] @@ -521,7 +536,10 @@ mod tests { simulation.advance_to(22); assert_eq!(simulation.game.target_rotation_state, Some(6)); - assert_eq!(simulation.game.wheel_holes, [false, true, false, false, true]); + assert_eq!( + simulation.game.wheel_holes, + [false, true, false, false, true] + ); simulation.advance_to(26); assert_eq!(simulation.game.target_rotation_state, None); } @@ -561,7 +579,12 @@ mod tests { .filter(|event| **event == "ClawRelease") .count(); assert!(captures >= 1, "{} must enter the claw", scenario.name()); - assert_eq!(releases, 1, "{} must release its seeded capture", scenario.name()); + assert_eq!( + releases, + 1, + "{} must release its seeded capture", + scenario.name() + ); assert!(captures >= releases); assert!(simulation.game.ball.position.is_finite()); assert!(simulation.game.ball.velocity.is_finite()); diff --git a/tdkpin-rs/src/table.rs b/tdkpin-rs/src/table.rs index e576e80..5c7640d 100644 --- a/tdkpin-rs/src/table.rs +++ b/tdkpin-rs/src/table.rs @@ -9,9 +9,10 @@ //! each object's bounds by `param_26`. They must not be enlarged again by the //! radius of the rendered ball. -use crate::geometry::Segment; use macroquad::prelude::Vec2; +use crate::geometry::Segment; + #[derive(Clone, Copy, Debug)] pub struct TableSegment { pub id: u8, @@ -425,7 +426,10 @@ mod tests { .iter() .find(|wall| wall.id == id) .expect("relative line record must be present"); - assert_eq!((wall.segment.start, wall.segment.end), (expected_start, expected_end)); + assert_eq!( + (wall.segment.start, wall.segment.end), + (expected_start, expected_end) + ); } for (id, expected_center) in [ (54, Vec2::new(53.0, 292.0)),