[no description]
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8644542 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,239 @@ +#![feature(try_blocks)] +use std::time::Duration; + +use bevy::{ + DefaultPlugins, + app::App, + camera::ScalingMode, + ecs::{event::Trigger, observer::ObservedBy, resource::IsResource}, + input::{common_conditions::input_just_pressed, keyboard::KeyboardInput}, + prelude::*, + window::{Window, WindowPlugin}, +}; +use bevy_ecs_tilemap::{helpers::square_grid::neighbors::Neighbors, prelude::*}; +use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin}; +use rand::{rng, seq::IteratorRandom}; + +use crate::picking::TilemapPickingPlugin; + +const W: usize = 20; +const H: usize = 10; +const BOMB_COUNT: usize = 30; +fn main() -> AppExit { + App::new() + .add_plugins( + DefaultPlugins + .set(WindowPlugin { + primary_window: Some(Window { + title: "Minesweeper!".into(), + ..default() + }), + ..default() + }) + .set(ImagePlugin::default_linear()), + ) + .insert_resource(Grid(ge())) + .add_plugins(TilemapPlugin) + // .add_plugins(EguiPlugin::default()) + // .add_plugins(WorldInspectorPlugin::new()) + .add_plugins(TilemapPickingPlugin) + .add_systems(Startup, startup) + // .add_systems(Update, movement) + .add_observer(|_: On<End>, q: Query<&mut TileTextureIndex, With<Bomb>>| { + for mut lem in q { + if lem.0 == 1 { + return; + } + if lem.0 != 12 { + lem.0 = 11; + } + } + }) + .add_observer( + |_: On<End>, q: Query<&mut TileTextureIndex, (Without<Bomb>, With<Flag>)>| { + for mut lem in q { + lem.0 = 13; + } + }, + ) + .add_observer( + |_: On<End>, q: Query<Entity, (With<TilePos>, With<ObservedBy>)>, mut c: Commands| { + // println!("{q:?}"); + for lem in q { + c.entity(lem).remove::<ObservedBy>(); + } + }, + ) + .add_systems(Update, reload.run_if(input_just_pressed(KeyCode::KeyR))) + .run() +} +#[derive(Resource)] +struct Grid([[u8; W]; W]); +#[derive(EntityEvent, Debug, Clone, Reflect)] +struct Zero(Entity); +#[derive(Event, Debug, Clone, Reflect)] +struct End; +#[derive(Component)] +struct Bomb; +#[derive(Component)] +struct Flag; +#[derive(Component)] +struct Murderable; + +macro_rules! all { + () => { + (0..W).flat_map(|x| (0..H).map(move |y| (x, y))) + }; +} + +fn reveal<E: EntityEvent>() +-> impl Fn(On<E>, Query<(&TilePos, &mut TileTextureIndex)>, Res<Grid>, Query<&TileStorage>, Commands) +{ + move |ev, mut tile, ge, store, mut cmds| { + let Ok((&tp @ TilePos { x, y }, mut sp)) = tile.get_mut(ev.event_target()) else { + return; + }; + *sp = TileTextureIndex(match ge.0[y as usize][x as usize] { + 0 if (sp.0 != 10 && sp.0 == 0) => { + let map_size = TilemapSize { + x: W as u32, + y: H as u32, + }; + for lem in Neighbors::get_square_neighboring_positions(&tp, &map_size, false) + .entities(store.single().unwrap()) + .iter() + { + cmds.trigger(Zero(*lem)); + } + 10 + } + n @ 1..=8 => 1 + n as u32, + _ => return, + }); + } +} +fn clicked() +-> impl Fn(On<Pointer<Press>>, Query<(&TilePos, &mut TileTextureIndex)>, Res<Grid>, Commands) { + move |ev, mut tile, ge, mut cmds| { + let Ok((&TilePos { x, y }, mut sp)) = tile.get_mut(ev.event_target()) else { + return; + }; + let real = ge.0[y as usize][x as usize]; + sp.0 = match ev.event().button { + PointerButton::Primary if sp.0 == 0 => match real { + 255 => { + cmds.trigger(End); + 12 + } + 0 => { + cmds.trigger(Zero(ev.event_target())); + return; + } + n @ 1..=8 => 1 + n as u32, + _ => 10, + }, + PointerButton::Secondary if sp.0 < 2 => { + if sp.0 == 0 { + cmds.entity(ev.event_target()).insert(Flag); + } else { + cmds.entity(ev.event_target()).remove::<Flag>(); + } + (!(sp.0 != 0)) as u32 + } + _ => return, + }; + } +} +fn reload( + mut commands: Commands, + mut gr: ResMut<Grid>, + asset_server: Res<AssetServer>, + q: Query<Entity, With<Murderable>>, +) { + gr.0 = ge(); + for lem in q { + commands.entity(lem).despawn(); + } + startup(commands, gr.into(), asset_server); +} +fn startup(mut commands: Commands, ge: Res<Grid>, asset_server: Res<AssetServer>) { + // commands.spawn(Camera2d); + commands.spawn(( + Camera2d, + Projection::Orthographic(OrthographicProjection { + scaling_mode: ScalingMode::AutoMin { + min_width: (W as f32 + 4.) * 256., + min_height: (H as f32 + 4.) * 256., + }, + ..OrthographicProjection::default_2d() + }), + Murderable, + )); + let texture_handle: Handle<Image> = asset_server.load("tiles.png"); + + let map_size = TilemapSize { + x: W as u32, + y: H as u32, + }; + + let mut tile_storage = TileStorage::empty(map_size); + let tilemap_entity = commands.spawn_empty().insert(Murderable).id(); + let tilemap_id = TilemapId(tilemap_entity); + + for (x, y) in all!() { + let position = TilePos { + x: x as u32, + y: y as u32, + }; + let mut e = commands.spawn(( + TileBundle { + texture_index: TileTextureIndex(0), + position, + tilemap_id, + ..default() + }, + Murderable, + )); + if ge.0[y][x] == 255 { + e.insert(Bomb); + } + let tile_entity = e.observe(clicked()).observe(reveal::<Zero>()).id(); + tile_storage.set(&position, tile_entity); + } + + let tile_size = TilemapTileSize { x: 256.0, y: 256.0 }; + let grid_size = tile_size.into(); + + commands.entity(tilemap_entity).insert(( + TilemapBundle { + grid_size, + size: map_size, + storage: tile_storage, + texture: TilemapTexture::Single(texture_handle), + tile_size, + map_type: TilemapType::Square, + anchor: TilemapAnchor::Center, + + ..Default::default() + }, + Murderable, + )); +} + +fn ge() -> [[u8; W]; W] { + let mut g = [[0u8; W]; W]; + + for (x, y) in all!().sample(&mut rng(), BOMB_COUNT) { + g[y][x] = 255; + let lems = aoc::util::nb((x, y)); + lems.into_iter().for_each(|(x, y)| { + try { + let lem = g.get_mut(y)?.get_mut(x)?; + *lem = lem.saturating_add(1); + }; + }); + } + + g +} +mod picking; |