This commit is like Batman, It has no parents.

This commit is contained in:
2024-07-11 02:39:52 +02:00
commit 3f07d00f2a
13 changed files with 5673 additions and 0 deletions

292
src/main.rs Normal file
View File

@@ -0,0 +1,292 @@
#![allow(clippy::type_complexity)]
#![allow(clippy::too_many_arguments)]
mod utils;
use bevy::prelude::*;
use bevy::window::WindowResolution;
use helpers::grid::{Flag, GridPlugin, Revealed, Tile, TileClickEvent, TileOffset, TileType};
mod helpers;
#[derive(Deref, Resource)]
pub struct PlayerAlive(bool);
#[derive(Deref, Resource)]
pub struct Score(usize);
impl FromWorld for PlayerAlive {
fn from_world(world: &mut World) -> Self {
let alive = world.resource::<PlayerAlive>();
Self(**alive)
}
}
#[derive(Deref, Resource)]
pub struct FontHandle(Handle<Font>);
impl FromWorld for FontHandle {
fn from_world(world: &mut World) -> Self {
let asset_server = world.resource::<AssetServer>();
Self(asset_server.load("fonts/FiraSans-Bold.ttf"))
}
}
fn setup_camera(mut commands: Commands) {
// 0x0 = Center
let translation = Vec3 {
x: -(1280.0 / 2.0),
y: -(720.0 / 2.0),
z: 1.0,
};
commands.spawn((
Camera2dBundle {
projection: OrthographicProjection {
far: 1000.0,
near: -1000.0,
scaling_mode: bevy::render::camera::ScalingMode::Fixed {
width: 1280.0,
height: 720.0,
},
scale: 0.75,
..Default::default()
},
transform: Transform {
translation,
..default()
},
..default()
},
TileOffset {
x: 0,
y: 0,
translation,
},
));
}
fn tile_clicked(
mut commands: Commands,
mut tiles: Query<(Entity, &Tile, &mut TileType)>,
mut tile_flag: Query<&mut Flag>,
mut tile_revealed: Query<&mut Revealed>,
mut ev_click: EventReader<TileClickEvent>,
mut score: ResMut<Score>,
mut alive: ResMut<PlayerAlive>,
) {
if !**alive {
return;
}
for ev in ev_click.read() {
if reveal(
&mut commands,
ev.x,
ev.y,
ev.button,
&mut tiles,
&mut tile_flag,
&mut tile_revealed,
&mut score,
10,
) && ev.button == MouseButton::Left
{
*alive = PlayerAlive(false);
println!("Score: {}", **score);
}
}
}
fn reveal(
commands: &mut Commands,
x: isize,
y: isize,
button: MouseButton,
tiles: &mut Query<(Entity, &Tile, &mut TileType)>,
tile_flag: &mut Query<&mut Flag>,
tile_revealed: &mut Query<&mut Revealed>,
score: &mut ResMut<Score>,
n: isize,
) -> bool {
use std::collections::{HashSet, VecDeque};
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back((x, y));
while let Some((cx, cy)) = queue.pop_front() {
if (cx - x).abs() > n || (cy - y).abs() > n {
continue;
}
if visited.contains(&(cx, cy)) {
continue;
}
visited.insert((cx, cy));
let result = get_tile(cx, cy);
let mut found = false;
for (ent, tile, mut kind) in tiles.iter_mut() {
if tile.x == cx && tile.y == cy {
// tile found
*kind = result;
if button == MouseButton::Right && tile_revealed.get(ent).is_err() {
if tile_flag.get(ent).is_ok() {
if *kind == TileType::Mine {
**score = Score(***score - 1);
}
commands.entity(ent).remove::<Flag>();
} else {
if *kind == TileType::Mine {
**score = Score(***score + 1);
}
commands.entity(ent).insert(Flag);
}
} else if tile_flag.get(ent).is_err() {
commands.entity(ent).insert(Revealed);
}
found = true;
break;
}
}
if !found {
let tile_pos = Tile { x: cx, y: cy };
let mut ent = commands.spawn(tile_pos);
ent.insert(result);
if button == MouseButton::Left {
ent.insert(Revealed);
}
if button == MouseButton::Right {
if result == TileType::Mine {
**score = Score(***score + 1);
}
ent.insert(Flag);
}
if result == TileType::Empty && button == MouseButton::Left {
for i in cx - 1..=cx + 1 {
for j in cy - 1..=cy + 1 {
if i != cx || j != cy {
queue.push_back((i, j));
}
}
}
}
}
if result == TileType::Mine {
return true;
}
}
false
}
fn is_mine(x: isize, y: isize) -> bool {
let value = get_mine_value(x, y);
let x = x as f64;
let y = y as f64;
let distance = (x.powf(2.0) + y.powf(2.0)).sqrt();
value > (1.0 - (distance / 100.0)).min(0.7)
}
const SEED: u32 = 65536;
fn get_mine_value(x: isize, y: isize) -> f64 {
use noise::{NoiseFn, Perlin};
let x = x as f64;
let y = y as f64;
const SCALE: f64 = 2.0;
let result2 = Perlin::new(SEED).get([x / (SCALE * 2.0), y / (SCALE * 2.0), 3.8]);
let result1 = Perlin::new(SEED + 1).get([x / (SCALE / 2.0), y / (SCALE / 2.0), 1.5]);
let result1 = 1.0 - result1.powf(2.5);
let p = 4.0;
result1 * (1.0 - (((1.0 - result2) * 2.0).powf(p)) / 2.0)
}
fn get_tile(x: isize, y: isize) -> TileType {
if is_mine(x, y) {
return TileType::Mine;
}
let mut surrounding = 0;
for i in x - 1..x + 2 {
for j in y - 1..y + 2 {
if is_mine(i, j) {
surrounding += 1;
}
}
}
surrounding.into()
}
fn main() {
// use imageproc::drawing::Canvas;
// // prepare an image
// let width = 2048;
// let height = 2048;
// let mut canvas = image::DynamicImage::new(width, height, image::ColorType::Rgb8);
// for x in 0..width {
// for y in 0..height {
// // let c = (get_mine_value(
// // x as isize - (width as isize / 2_isize),
// // y as isize - (height as isize / 2_isize),
// // ) * 255.0) as u8;
// // let color = [c, c, c, 1];
// let color = if is_mine(
// x as isize - (width as isize / 2_isize),
// y as isize - (height as isize / 2_isize),
// ) {
// [255, 255, 255, 1]
// } else {
// [0, 0, 0, 1]
// };
// canvas.draw_pixel(
// x,
// (-(y as isize) + (height - 1) as isize) as u32,
// color.into(),
// );
// }
// }
// if let Err(e) = canvas.save("rawr.png") {
// eprintln!("Error saving image: {:?}", e);
// }
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: String::from("Infinite Minesweeper"),
resolution: WindowResolution::new(1280.0, 720.0),
..Default::default()
}),
..default()
}),
// bevy_inspector_egui::quick::WorldInspectorPlugin::new(),
))
.insert_resource(PlayerAlive(true))
.insert_resource(Score(0))
.init_resource::<FontHandle>()
.add_plugins(GridPlugin)
.add_systems(Startup, setup_camera)
.add_systems(Update, tile_clicked)
.run();
}