chore: configure rustfmt, switch to LF line endings

This commit is contained in:
2025-07-23 20:15:54 -05:00
parent 2cc47d5904
commit 9f9ace0b16
13 changed files with 51 additions and 166 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto eol=lf

6
rustfmt.toml Normal file
View File

@@ -0,0 +1,6 @@
# Rustfmt configuration
edition = "2021"
max_width = 130
tab_spaces = 4
newline_style = "Unix"
use_small_heuristics = "Default"

View File

@@ -9,13 +9,7 @@ use crate::direction::Direction;
/// Trait for drawable atlas-based textures /// Trait for drawable atlas-based textures
pub trait FrameDrawn { pub trait FrameDrawn {
fn render( fn render(&self, canvas: &mut Canvas<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>);
&self,
canvas: &mut Canvas<Window>,
position: (i32, i32),
direction: Direction,
frame: Option<u32>,
);
} }
/// A texture atlas abstraction for static (non-animated) rendering. /// A texture atlas abstraction for static (non-animated) rendering.
@@ -28,13 +22,7 @@ pub struct AtlasTexture<'a> {
} }
impl<'a> AtlasTexture<'a> { impl<'a> AtlasTexture<'a> {
pub fn new( pub fn new(texture: Texture<'a>, frame_count: u32, frame_width: u32, frame_height: u32, offset: Option<(i32, i32)>) -> Self {
texture: Texture<'a>,
frame_count: u32,
frame_width: u32,
frame_height: u32,
offset: Option<(i32, i32)>,
) -> Self {
AtlasTexture { AtlasTexture {
raw_texture: texture, raw_texture: texture,
frame_count, frame_count,
@@ -62,13 +50,7 @@ impl<'a> AtlasTexture<'a> {
} }
impl<'a> FrameDrawn for AtlasTexture<'a> { impl<'a> FrameDrawn for AtlasTexture<'a> {
fn render( fn render(&self, canvas: &mut Canvas<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>) {
&self,
canvas: &mut Canvas<Window>,
position: (i32, i32),
direction: Direction,
frame: Option<u32>,
) {
let texture_source_frame_rect = self.get_frame_rect(frame.unwrap_or(0)); let texture_source_frame_rect = self.get_frame_rect(frame.unwrap_or(0));
let canvas_destination_rect = Rect::new( let canvas_destination_rect = Rect::new(
position.0 + self.offset.0, position.0 + self.offset.0,
@@ -157,13 +139,7 @@ impl<'a> AnimatedAtlasTexture<'a> {
} }
impl<'a> FrameDrawn for AnimatedAtlasTexture<'a> { impl<'a> FrameDrawn for AnimatedAtlasTexture<'a> {
fn render( fn render(&self, canvas: &mut Canvas<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>) {
&self,
canvas: &mut Canvas<Window>,
position: (i32, i32),
direction: Direction,
frame: Option<u32>,
) {
let frame = frame.unwrap_or_else(|| self.current_frame()); let frame = frame.unwrap_or_else(|| self.current_frame());
self.atlas.render(canvas, position, direction, Some(frame)); self.atlas.render(canvas, position, direction, Some(frame));
} }

View File

@@ -39,8 +39,7 @@ impl Audio {
.enumerate() .enumerate()
.map(|(i, asset)| { .map(|(i, asset)| {
let data = get_asset_bytes(*asset).expect("Failed to load sound asset"); let data = get_asset_bytes(*asset).expect("Failed to load sound asset");
let rwops = RWops::from_bytes(&data) let rwops = RWops::from_bytes(&data).unwrap_or_else(|_| panic!("Failed to create RWops for sound {}", i + 1));
.unwrap_or_else(|_| panic!("Failed to create RWops for sound {}", i + 1));
rwops rwops
.load_wav() .load_wav()
.unwrap_or_else(|_| panic!("Failed to load sound {} from asset API", i + 1)) .unwrap_or_else(|_| panic!("Failed to load sound {} from asset API", i + 1))
@@ -61,11 +60,7 @@ impl Audio {
if let Some(chunk) = self.sounds.get(self.next_sound_index) { if let Some(chunk) = self.sounds.get(self.next_sound_index) {
match mixer::Channel(0).play(chunk, 0) { match mixer::Channel(0).play(chunk, 0) {
Ok(channel) => { Ok(channel) => {
tracing::info!( tracing::info!("Playing sound #{} on channel {:?}", self.next_sound_index + 1, channel);
"Playing sound #{} on channel {:?}",
self.next_sound_index + 1,
channel
);
} }
Err(e) => { Err(e) => {
tracing::warn!("Could not play sound #{}: {}", self.next_sound_index + 1, e); tracing::warn!("Could not play sound #{}: {}", self.next_sound_index + 1, e);

View File

@@ -64,18 +64,10 @@ pub fn reconstruct_edibles<'a>(
let cell = (x, y); let cell = (x, y);
match tile { match tile {
Some(MapTile::Pellet) => { Some(MapTile::Pellet) => {
edibles.push(Edible::new( edibles.push(Edible::new(EdibleKind::Pellet, cell, Rc::clone(&pellet_sprite)));
EdibleKind::Pellet,
cell,
Rc::clone(&pellet_sprite),
));
} }
Some(MapTile::PowerPellet) => { Some(MapTile::PowerPellet) => {
edibles.push(Edible::new( edibles.push(Edible::new(EdibleKind::PowerPellet, cell, Rc::clone(&power_pellet_sprite)));
EdibleKind::PowerPellet,
cell,
Rc::clone(&power_pellet_sprite),
));
} }
// Fruits can be added here if you have fruit positions // Fruits can be added here if you have fruit positions
_ => {} _ => {}

View File

@@ -108,10 +108,7 @@ impl Moving for MovableEntity {
} }
fn next_cell(&self, direction: Option<Direction>) -> (i32, i32) { fn next_cell(&self, direction: Option<Direction>) -> (i32, i32) {
let (x, y) = direction.unwrap_or(self.direction).offset(); let (x, y) = direction.unwrap_or(self.direction).offset();
( (self.base.cell_position.0 as i32 + x, self.base.cell_position.1 as i32 + y)
self.base.cell_position.0 as i32 + x,
self.base.cell_position.1 as i32 + y,
)
} }
fn is_wall_ahead(&self, direction: Option<Direction>) -> bool { fn is_wall_ahead(&self, direction: Option<Direction>) -> bool {
let next_cell = self.next_cell(direction); let next_cell = self.next_cell(direction);
@@ -119,10 +116,10 @@ impl Moving for MovableEntity {
} }
fn handle_tunnel(&mut self) -> bool { fn handle_tunnel(&mut self) -> bool {
if !self.in_tunnel { if !self.in_tunnel {
let current_tile = self.map.borrow().get_tile(( let current_tile = self
self.base.cell_position.0 as i32, .map
self.base.cell_position.1 as i32, .borrow()
)); .get_tile((self.base.cell_position.0 as i32, self.base.cell_position.1 as i32));
if matches!(current_tile, Some(MapTile::Tunnel)) { if matches!(current_tile, Some(MapTile::Tunnel)) {
self.in_tunnel = true; self.in_tunnel = true;
} }
@@ -130,14 +127,12 @@ impl Moving for MovableEntity {
if self.in_tunnel { if self.in_tunnel {
if self.base.cell_position.0 == 0 { if self.base.cell_position.0 == 0 {
self.base.cell_position.0 = BOARD_WIDTH - 2; self.base.cell_position.0 = BOARD_WIDTH - 2;
self.base.pixel_position = self.base.pixel_position = Map::cell_to_pixel((self.base.cell_position.0, self.base.cell_position.1));
Map::cell_to_pixel((self.base.cell_position.0, self.base.cell_position.1));
self.in_tunnel = false; self.in_tunnel = false;
true true
} else if self.base.cell_position.0 == BOARD_WIDTH - 1 { } else if self.base.cell_position.0 == BOARD_WIDTH - 1 {
self.base.cell_position.0 = 1; self.base.cell_position.0 = 1;
self.base.pixel_position = self.base.pixel_position = Map::cell_to_pixel((self.base.cell_position.0, self.base.cell_position.1));
Map::cell_to_pixel((self.base.cell_position.0, self.base.cell_position.1));
self.in_tunnel = false; self.in_tunnel = false;
true true
} else { } else {

View File

@@ -64,11 +64,7 @@ impl<'a> Game<'a> {
let pacman_atlas = texture_creator let pacman_atlas = texture_creator
.load_texture_bytes(&pacman_bytes) .load_texture_bytes(&pacman_bytes)
.expect("Could not load pacman texture from asset API"); .expect("Could not load pacman texture from asset API");
let pacman = Rc::new(RefCell::new(Pacman::new( let pacman = Rc::new(RefCell::new(Pacman::new((1, 1), pacman_atlas, Rc::clone(&map))));
(1, 1),
pacman_atlas,
Rc::clone(&map),
)));
// Load ghost textures // Load ghost textures
let ghost_body_bytes = get_asset_bytes(Asset::GhostBody).expect("Failed to load asset"); let ghost_body_bytes = get_asset_bytes(Asset::GhostBody).expect("Failed to load asset");
@@ -113,12 +109,9 @@ impl<'a> Game<'a> {
// Load font from asset API // Load font from asset API
let font = { let font = {
let font_bytes = get_asset_bytes(Asset::FontKonami) let font_bytes = get_asset_bytes(Asset::FontKonami).expect("Failed to load asset").into_owned();
.expect("Failed to load asset")
.into_owned();
let font_bytes_static: &'static [u8] = Box::leak(font_bytes.into_boxed_slice()); let font_bytes_static: &'static [u8] = Box::leak(font_bytes.into_boxed_slice());
let font_rwops = let font_rwops = RWops::from_bytes(font_bytes_static).expect("Failed to create RWops for font");
RWops::from_bytes(font_bytes_static).expect("Failed to create RWops for font");
ttf_context ttf_context
.load_font_from_rwops(font_rwops, 24) .load_font_from_rwops(font_rwops, 24)
.expect("Could not load font from asset API") .expect("Could not load font from asset API")
@@ -306,18 +299,9 @@ impl<'a> Game<'a> {
// Draw the debug grid // Draw the debug grid
match self.debug_mode { match self.debug_mode {
DebugMode::Grid => { DebugMode::Grid => {
DebugRenderer::draw_debug_grid( DebugRenderer::draw_debug_grid(self.canvas, &self.map.borrow(), self.pacman.borrow().base.base.cell_position);
self.canvas, let next_cell = <Pacman as crate::entity::Moving>::next_cell(&*self.pacman.borrow(), None);
&self.map.borrow(), DebugRenderer::draw_next_cell(self.canvas, &self.map.borrow(), (next_cell.0 as u32, next_cell.1 as u32));
self.pacman.borrow().base.base.cell_position,
);
let next_cell =
<Pacman as crate::entity::Moving>::next_cell(&*self.pacman.borrow(), None);
DebugRenderer::draw_next_cell(
self.canvas,
&self.map.borrow(),
(next_cell.0 as u32, next_cell.1 as u32),
);
} }
DebugMode::ValidPositions => { DebugMode::ValidPositions => {
DebugRenderer::draw_valid_positions(self.canvas, &mut self.map.borrow_mut()); DebugRenderer::draw_valid_positions(self.canvas, &mut self.map.borrow_mut());
@@ -358,11 +342,7 @@ impl<'a> Game<'a> {
/// Renders text to the screen at the given position. /// Renders text to the screen at the given position.
fn render_text(&mut self, text: &str, position: (i32, i32), color: Color) { fn render_text(&mut self, text: &str, position: (i32, i32), color: Color) {
let surface = self let surface = self.font.render(text).blended(color).expect("Could not render text surface");
.font
.render(text)
.blended(color)
.expect("Could not render text surface");
let texture_creator = self.canvas.texture_creator(); let texture_creator = self.canvas.texture_creator();
let texture = texture_creator let texture = texture_creator

View File

@@ -121,22 +121,14 @@ impl Ghost<'_> {
let mut possible_moves = Vec::new(); let mut possible_moves = Vec::new();
// Check all four directions // Check all four directions
for dir in &[ for dir in &[Direction::Up, Direction::Down, Direction::Left, Direction::Right] {
Direction::Up,
Direction::Down,
Direction::Left,
Direction::Right,
] {
// Don't allow reversing direction // Don't allow reversing direction
if *dir == self.base.direction.opposite() { if *dir == self.base.direction.opposite() {
continue; continue;
} }
let next_cell = self.base.next_cell(Some(*dir)); let next_cell = self.base.next_cell(Some(*dir));
if !matches!( if !matches!(self.base.map.borrow().get_tile(next_cell), Some(MapTile::Wall)) {
self.base.map.borrow().get_tile(next_cell),
Some(MapTile::Wall)
) {
possible_moves.push(next_cell); possible_moves.push(next_cell);
} }
} }
@@ -185,12 +177,7 @@ impl Ghost<'_> {
successors.push(((1, p.1), 1)); successors.push(((1, p.1), 1));
} }
} }
for dir in &[ for dir in &[Direction::Up, Direction::Down, Direction::Left, Direction::Right] {
Direction::Up,
Direction::Down,
Direction::Left,
Direction::Right,
] {
let (dx, dy) = dir.offset(); let (dx, dy) = dir.offset();
let next_p = (p.0 as i32 + dx, p.1 as i32 + dy); let next_p = (p.0 as i32 + dx, p.1 as i32 + dy);
if let Some(tile) = map.get_tile(next_p) { if let Some(tile) = map.get_tile(next_p) {
@@ -209,9 +196,8 @@ impl Ghost<'_> {
/// Changes the ghost's mode and handles direction reversal /// Changes the ghost's mode and handles direction reversal
pub fn set_mode(&mut self, new_mode: GhostMode) { pub fn set_mode(&mut self, new_mode: GhostMode) {
// Don't reverse if going to/from frightened or if in house // Don't reverse if going to/from frightened or if in house
let should_reverse = self.mode != GhostMode::House let should_reverse =
&& new_mode != GhostMode::Frightened self.mode != GhostMode::House && new_mode != GhostMode::Frightened && self.mode != GhostMode::Frightened;
&& self.mode != GhostMode::Frightened;
self.mode = new_mode; self.mode = new_mode;
@@ -224,8 +210,7 @@ impl Ghost<'_> {
}; };
if should_reverse { if should_reverse {
self.base self.base.set_direction_if_valid(self.base.direction.opposite());
.set_direction_if_valid(self.base.direction.opposite());
} }
} }
@@ -241,9 +226,7 @@ impl Ghost<'_> {
if !self.base.handle_tunnel() { if !self.base.handle_tunnel() {
// Pathfinding logic (only if not in tunnel) // Pathfinding logic (only if not in tunnel)
let target_tile = self.get_target_tile(); let target_tile = self.get_target_tile();
if let Some((path, _)) = if let Some((path, _)) = self.get_path_to_target((target_tile.0 as u32, target_tile.1 as u32)) {
self.get_path_to_target((target_tile.0 as u32, target_tile.1 as u32))
{
if path.len() > 1 { if path.len() > 1 {
let next_move = path[1]; let next_move = path[1];
let (x, y) = self.base.base.cell_position; let (x, y) = self.base.base.cell_position;
@@ -318,7 +301,6 @@ impl<'a> Renderable for Ghost<'a> {
Direction::Down => 3, Direction::Down => 3,
} }
}; };
self.eyes_sprite self.eyes_sprite.render(canvas, pos, Direction::Right, Some(eye_frame));
.render(canvas, pos, Direction::Right, Some(eye_frame));
} }
} }

View File

@@ -23,14 +23,7 @@ impl<'a> Blinky<'a> {
pacman: Rc<RefCell<Pacman<'a>>>, pacman: Rc<RefCell<Pacman<'a>>>,
) -> Blinky<'a> { ) -> Blinky<'a> {
Blinky { Blinky {
ghost: Ghost::new( ghost: Ghost::new(GhostType::Blinky, starting_position, body_texture, eyes_texture, map, pacman),
GhostType::Blinky,
starting_position,
body_texture,
eyes_texture,
map,
pacman,
),
} }
} }

View File

@@ -94,14 +94,8 @@ mod tests {
#[test] #[test]
fn test_commutative_property() { fn test_commutative_property() {
// The function should work the same regardless of parameter order // The function should work the same regardless of parameter order
assert_eq!( assert_eq!(is_adjacent((1, 2), (2, 2), false), is_adjacent((2, 2), (1, 2), false));
is_adjacent((1, 2), (2, 2), false),
is_adjacent((2, 2), (1, 2), false)
);
assert_eq!( assert_eq!(is_adjacent((1, 2), (2, 3), true), is_adjacent((2, 3), (1, 2), true));
is_adjacent((1, 2), (2, 3), true),
is_adjacent((2, 3), (1, 2), true)
);
} }
} }

View File

@@ -53,6 +53,7 @@ unsafe fn attach_console() {
} }
mod animation; mod animation;
mod asset;
mod audio; mod audio;
mod constants; mod constants;
mod debug; mod debug;
@@ -66,7 +67,6 @@ mod helper;
mod map; mod map;
mod modulation; mod modulation;
mod pacman; mod pacman;
mod asset;
/// The main entry point of the application. /// The main entry point of the application.
/// ///
@@ -99,26 +99,16 @@ pub fn main() {
.build() .build()
.expect("Could not initialize window"); .expect("Could not initialize window");
let mut canvas = window let mut canvas = window.into_canvas().build().expect("Could not build canvas");
.into_canvas()
.build()
.expect("Could not build canvas");
canvas canvas
.set_logical_size(WINDOW_WIDTH, WINDOW_HEIGHT) .set_logical_size(WINDOW_WIDTH, WINDOW_HEIGHT)
.expect("Could not set logical size"); .expect("Could not set logical size");
let texture_creator = canvas.texture_creator(); let texture_creator = canvas.texture_creator();
let mut game = Game::new( let mut game = Game::new(&mut canvas, &texture_creator, &ttf_context, &audio_subsystem);
&mut canvas,
&texture_creator,
&ttf_context,
&audio_subsystem,
);
let mut event_pump = sdl_context let mut event_pump = sdl_context.event_pump().expect("Could not get SDL EventPump");
.event_pump()
.expect("Could not get SDL EventPump");
// Initial draw and tick // Initial draw and tick
game.draw(); game.draw();
@@ -169,11 +159,7 @@ pub fn main() {
.. ..
} => { } => {
paused = !paused; paused = !paused;
event!( event!(tracing::Level::INFO, "{}", if paused { "Paused" } else { "Unpaused" });
tracing::Level::INFO,
"{}",
if paused { "Paused" } else { "Unpaused" }
);
} }
Event::KeyDown { keycode, .. } => { Event::KeyDown { keycode, .. } => {
game.keyboard_event(keycode.unwrap()); game.keyboard_event(keycode.unwrap());

View File

@@ -75,9 +75,7 @@ impl Map {
'o' => MapTile::PowerPellet, 'o' => MapTile::PowerPellet,
' ' => MapTile::Empty, ' ' => MapTile::Empty,
'T' => MapTile::Tunnel, 'T' => MapTile::Tunnel,
c @ '0' | c @ '1' | c @ '2' | c @ '3' | c @ '4' => { c @ '0' | c @ '1' | c @ '2' | c @ '3' | c @ '4' => MapTile::StartingPosition(c.to_digit(10).unwrap() as u8),
MapTile::StartingPosition(c.to_digit(10).unwrap() as u8)
}
'=' => MapTile::Empty, '=' => MapTile::Empty,
_ => panic!("Unknown character in board: {character}"), _ => panic!("Unknown character in board: {character}"),
}; };
@@ -94,12 +92,7 @@ impl Map {
/// Resets the map to its original state. /// Resets the map to its original state.
pub fn reset(&mut self) { pub fn reset(&mut self) {
// Restore the map to its original state // Restore the map to its original state
for (x, col) in self for (x, col) in self.current.iter_mut().enumerate().take(BOARD_WIDTH as usize) {
.current
.iter_mut()
.enumerate()
.take(BOARD_WIDTH as usize)
{
for (y, cell) in col.iter_mut().enumerate().take(BOARD_HEIGHT as usize) { for (y, cell) in col.iter_mut().enumerate().take(BOARD_HEIGHT as usize) {
*cell = self.default[x][y]; *cell = self.default[x][y];
} }
@@ -146,10 +139,7 @@ impl Map {
/// ///
/// * `cell` - The cell coordinates, in grid coordinates. /// * `cell` - The cell coordinates, in grid coordinates.
pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) { pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) {
( ((cell.0 * CELL_SIZE) as i32, ((cell.1 + BOARD_OFFSET.1) * CELL_SIZE) as i32)
(cell.0 * CELL_SIZE) as i32,
((cell.1 + BOARD_OFFSET.1) * CELL_SIZE) as i32,
)
} }
/// Returns a reference to a cached vector of all valid playable positions in the maze. /// Returns a reference to a cached vector of all valid playable positions in the maze.
@@ -198,8 +188,7 @@ impl Map {
] { ] {
let neighbor = pos + offset; let neighbor = pos + offset;
if neighbor.x < BOARD_WIDTH && neighbor.y < BOARD_HEIGHT { if neighbor.x < BOARD_WIDTH && neighbor.y < BOARD_HEIGHT {
let neighbor_tile = let neighbor_tile = self.current[neighbor.x as usize][neighbor.y as usize];
self.current[neighbor.x as usize][neighbor.y as usize];
if matches!(neighbor_tile, Empty | Pellet | PowerPellet) { if matches!(neighbor_tile, Empty | Pellet | PowerPellet) {
queue.push_back(neighbor); queue.push_back(neighbor);
} }

View File

@@ -58,11 +58,7 @@ impl<'a> Moving for Pacman<'a> {
impl Pacman<'_> { impl Pacman<'_> {
/// Creates a new `Pacman` instance. /// Creates a new `Pacman` instance.
pub fn new<'a>( pub fn new<'a>(starting_position: (u32, u32), atlas: Texture<'a>, map: Rc<RefCell<Map>>) -> Pacman<'a> {
starting_position: (u32, u32),
atlas: Texture<'a>,
map: Rc<RefCell<Map>>,
) -> Pacman<'a> {
let pixel_position = Map::cell_to_pixel(starting_position); let pixel_position = Map::cell_to_pixel(starting_position);
Pacman { Pacman {
base: MovableEntity::new( base: MovableEntity::new(