use bevy::app::AppExit; use bevy::prelude::*; use bevy::render::camera::Camera; use std::fs::File; use std::io::{BufRead, BufReader}; fn main() { App::build() .add_resource(WindowDescriptor { title: "Advent of Code".to_string(), width: 1920, height: 1080, ..Default::default() }) .add_resource(ClearColor(Color::rgb(0., 0., 0.))) .add_startup_system(setup_camera.system()) .add_startup_stage("game_entities") .add_startup_system_to_stage("game_entities", read_input_file.system()) .add_system(move_tobogganist.system()) .add_system(collide_with_trees.system()) .add_system(clear_finished_tobogganists.system()) .add_system(end_of_slope.system()) .add_system(translate_positions.system()) .add_system(tobogganist_cam.system()) .add_plugins(DefaultPlugins) .run(); } fn setup_camera(mut commands: Commands, mut materials: ResMut>) { commands.spawn(Camera2dComponents::default()); commands.insert_resource(Materials { tobogganist_material: materials.add(Color::rgb(0., 0., 255.).into()), tree_material: materials.add(Color::rgb(0., 255., 0.).into()), }); } struct Materials { tobogganist_material: Handle, tree_material: Handle, } struct Tobogganist; struct Tree; #[derive(PartialEq, Eq)] struct Position { x: usize, y: usize, } struct Velocity { x: usize, y: usize, } struct MapSize { width: usize, height: usize, } struct TreeHitCount(usize); struct Score(usize); fn read_input_file(mut commands: Commands, materials: Res) { commands .spawn(SpriteComponents { sprite: Sprite::new(Vec2::new(10., 10.)), material: materials.tobogganist_material.clone(), ..Default::default() }) .with(Tobogganist) .with(Position { x: 0, y: 0 }) .with(Velocity { x: 1, y: 1 }) .with(TreeHitCount(0)); commands .spawn(SpriteComponents { sprite: Sprite::new(Vec2::new(10., 10.)), material: materials.tobogganist_material.clone(), ..Default::default() }) .with(Tobogganist) .with(Position { x: 0, y: 0 }) .with(Velocity { x: 3, y: 1 }) .with(TreeHitCount(0)); commands .spawn(SpriteComponents { sprite: Sprite::new(Vec2::new(10., 10.)), material: materials.tobogganist_material.clone(), ..Default::default() }) .with(Tobogganist) .with(Position { x: 0, y: 0 }) .with(Velocity { x: 5, y: 1 }) .with(TreeHitCount(0)); commands .spawn(SpriteComponents { sprite: Sprite::new(Vec2::new(10., 10.)), material: materials.tobogganist_material.clone(), ..Default::default() }) .with(Tobogganist) .with(Position { x: 0, y: 0 }) .with(Velocity { x: 7, y: 1 }) .with(TreeHitCount(0)); commands .spawn(SpriteComponents { sprite: Sprite::new(Vec2::new(10., 10.)), material: materials.tobogganist_material.clone(), ..Default::default() }) .with(Tobogganist) .with(Position { x: 0, y: 0 }) .with(Velocity { x: 1, y: 2 }) .with(TreeHitCount(0)); let f = File::open("./inputs/day_3.txt").expect("Failed to read file"); // TODO: Use the asset loading system to load this rather? let mut width = 0; let mut height = 0; for (y, line) in BufReader::new(f).lines().enumerate() { let line = line.expect("Error reading file"); let line = line.trim(); width = width.max(line.len()); for (x, c) in line.chars().enumerate() { if c == '#' { commands .spawn(SpriteComponents { sprite: Sprite::new(Vec2::new(10., 10.)), material: materials.tree_material.clone(), ..Default::default() }) .with(Tree) .with(Position { x, y }) .with(TreeHitCount(0)); } } height = y + 1; } commands.insert_resource(MapSize { width, height }); commands.insert_resource(Score(1)); } fn translate_positions(mut transform: Mut, position: &Position) { transform.translation = Vec3::new(position.x as f32 * 10., position.y as f32 * -10., 0.); } fn tobogganist_cam( tobogganist: Query>, mut camera: Query>, ) { for mut camera_transform in camera.iter_mut() { if let Some(tobogganist_transform) = tobogganist.iter().next() { camera_transform.translation = Vec3::new(0., tobogganist_transform.translation.y(), 0.); } } } fn move_tobogganist(map_size: Res, mut position: Mut, velocity: &Velocity) { position.x += velocity.x; position.x %= map_size.width; position.y += velocity.y; } fn collide_with_trees( mut tobogganists: Query>, trees: Query>, ) { for (tobogganist_position, mut tree_hit_count) in tobogganists.iter_mut() { for tree_position in trees.iter() { if *tree_position == *tobogganist_position { tree_hit_count.0 += 1; } } } } fn clear_finished_tobogganists( mut commands: Commands, map_size: Res, mut score: ResMut, tobogganists: Query>, ) { for (entity, position, velocity, tree_hit_count) in tobogganists.iter() { if position.y >= map_size.height { println!( "A tobogganist finished heading at incline {} x {}, after hitting {} trees", velocity.x, velocity.y, tree_hit_count.0 ); score.0 *= tree_hit_count.0; commands.despawn(entity); } } } fn end_of_slope( score: Res, mut exit_events: ResMut>, tobogganists: Query<&Tobogganist>, ) { if tobogganists.iter().count() == 0 { println!("All tobogganists finished. Total score: {}", score.0); exit_events.send(AppExit); } }