Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use std::fmt::{self, Display, Formatter}; | ||
use std::ops::{Add, Sub}; | ||
use bevy::prelude::Component; | ||
|
||
use bevy_inspector_egui::{prelude::*, reflect_inspector}; | ||
|
||
// adopted https://github.com/jakobhellermann/bevy-inspector-egui/blob/main/docs/MIGRATION_GUIDE_0.15_0.16.md | ||
#[cfg_attr(feature = "debug", derive(InspectorOptions))] | ||
#[derive(Clone, Copy)] | ||
// todo | ||
pub struct Coordinates { | ||
pub x: u16, | ||
pub y: u16 | ||
} | ||
|
||
impl Add for Coordinates { | ||
type Output = Self; | ||
|
||
fn add(self, rhs: Self) -> Self::Output { | ||
Self { | ||
x: self.x + rhs.x, | ||
y: self.y + rhs.y | ||
} | ||
} | ||
} | ||
|
||
impl Add<(i8,i8)> for Coordinates { | ||
type Output = Self; | ||
|
||
fn add(self, (rhs_x, rhs_y): (i8,i8)) -> Self::Output { | ||
let x = ((self.x as i16) + rhs_x as i16) as u16; | ||
let y = ((self.y as i16) + rhs_y as i16) as u16; | ||
Self {x, y} | ||
} | ||
} | ||
|
||
impl Sub for Coordinates { | ||
type Output = Self; | ||
|
||
fn sub(self, rhs: Self) -> Self::Output { | ||
Self{ | ||
x: self.x.saturating_sub(rhs.x), | ||
y: self.y.saturating_sub(rhs.y) | ||
} | ||
} | ||
} | ||
|
||
impl Display for Coordinates { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
write!(f, "({},{})", self.x, self.y) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub use coordinates::Coordinates; | ||
mod coordinates; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub(crate) mod tile; | ||
pub(crate) mod tile_map; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use std::fmt::format; | ||
|
||
#[cfg(feature = "debug")] | ||
use colored::Colorize; | ||
|
||
#[derive(Debug,Clone, Copy, PartialEq, Eq)] | ||
pub enum Tile { | ||
Bomb, | ||
BombNeighbour(u8), | ||
Empty, | ||
} | ||
|
||
impl Tile { | ||
pub const fn is_bomb(&self) -> bool { | ||
matches!(self, Self::Bomb) | ||
} | ||
|
||
pub fn console_output(&self) -> String { | ||
format!( | ||
"{}", | ||
match self { | ||
Tile::Bomb => "*".bright_red(), | ||
Tile::BombNeighbour(bombs_count) => match bombs_count { | ||
1 => "1".cyan(), | ||
2 => "2".green(), | ||
3 => "3".yellow(), | ||
_ => bombs_count.to_string().red() | ||
}, | ||
Tile::Empty => " ".black(), | ||
} | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
use crate::components::Coordinates; | ||
use crate::resources::tile::Tile; | ||
|
||
use std::ops::{Deref, DerefMut}; | ||
|
||
use rand::{thread_rng, Rng}; | ||
|
||
// Delta coordinates for all 8 square neighbors | ||
// [column, row] | ||
const SQUARE_COORDINATES: [(i8, i8); 8] = [ | ||
(-1, -1), // Bottom left | ||
(0, -1), // Bottom | ||
(1, -1), // Bottom right | ||
(-1, 0), // Left | ||
(1, 0), // Right | ||
(-1, 1), // Top Left | ||
(0, 1), // Top | ||
(1, 1), // Top Right | ||
]; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct TileMap { | ||
bomb_count: u16, | ||
height: u16, | ||
width: u16, | ||
map: Vec<Vec<Tile>>, | ||
} | ||
|
||
impl TileMap { | ||
pub fn empty(width: u16, height: u16) -> Self { | ||
let map = (0..height) | ||
.into_iter() | ||
.map(|_| (0..width).into_iter().map(|_| Tile::Empty).collect()) | ||
.collect(); | ||
Self { | ||
bomb_count: 9, | ||
height, | ||
width, | ||
map, | ||
} | ||
} | ||
|
||
#[cfg(feature = "debug")] | ||
pub fn console_output(&self) -> String { | ||
let mut buffer = format!( | ||
"Map: ({},{}) with {} bombs:\n", | ||
self.width, self.height, self.bomb_count | ||
); | ||
|
||
let table_separator: String = (0..=(self.width + 1)).into_iter().map(|_| '-').collect(); | ||
buffer = format!("{}{}\n", buffer, table_separator); | ||
|
||
for line in self.iter().rev() { | ||
buffer = format!("{}|", buffer); | ||
for tile in line.iter() { | ||
buffer = format!("{}{}", buffer, tile.console_output()) | ||
} | ||
buffer = format!("{}|\n", buffer); | ||
} | ||
|
||
format!("{}{}", buffer, table_separator) | ||
} | ||
|
||
pub fn safe_square_at(&self, coordinates: Coordinates) -> impl Iterator<Item = Coordinates> { | ||
SQUARE_COORDINATES | ||
.iter() | ||
.copied() | ||
.map(move |tuple| coordinates + tuple) | ||
} | ||
|
||
pub fn is_bomb_at(&self, coordinates: Coordinates) -> bool { | ||
if coordinates.x >= self.width || coordinates.y >= self.height { | ||
return false; | ||
} | ||
|
||
self.map[coordinates.y as usize][coordinates.x as usize].is_bomb() | ||
} | ||
|
||
pub fn bomb_count_at(&self, coordinates: Coordinates) -> u8 { | ||
if self.is_bomb_at(coordinates) { | ||
return 0; | ||
} | ||
|
||
let res = self | ||
.safe_square_at(coordinates) | ||
.filter(|coord| self.is_bomb_at(*coord)) | ||
.count(); | ||
|
||
res as u8 | ||
} | ||
|
||
pub fn width(&self) -> u16 { | ||
self.width | ||
} | ||
|
||
pub fn height(&self) -> u16 { | ||
self.height | ||
} | ||
|
||
pub fn bomb_count(&self) -> u16 { | ||
self.bomb_count | ||
} | ||
|
||
pub fn set_bombs(&mut self, bomb_count: u16) { | ||
self.bomb_count = bomb_count; | ||
let mut remaining_bombs = bomb_count; | ||
let mut rng = thread_rng(); | ||
|
||
while remaining_bombs > 0 { | ||
let row = rng.gen_range(0..self.height) as usize; | ||
let column = rng.gen_range(0..self.width) as usize; | ||
if let Tile::Empty = self[row][column] { | ||
self[row][column] = Tile::Bomb; | ||
remaining_bombs -= 1; | ||
} | ||
} | ||
|
||
for row in 0..self.height { | ||
for col in 0..self.width { | ||
let coords = Coordinates { y: row, x: col }; | ||
|
||
if self.is_bomb_at(coords) { | ||
continue; | ||
}; | ||
|
||
let bomb_count = self.bomb_count_at(coords); | ||
if bomb_count == 0 { | ||
continue; | ||
} | ||
|
||
let tile = &mut self[row as usize][col as usize]; | ||
*tile = Tile::BombNeighbour(bomb_count); | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Deref for TileMap { | ||
type Target = Vec<Vec<Tile>>; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.map | ||
} | ||
} | ||
|
||
impl DerefMut for TileMap { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
&mut self.map | ||
} | ||
} |