1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
//! The `map` module contains structures of the dungeon game map
//! including the current `Floor`, and map `Tile`.
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use crate::wfc::Wfc;
use std::{
cell::RefCell,
hash::{DefaultHasher, Hash, Hasher},
};
use crate::{const_pos, pos::Pos};
/// `MAP_SIZE` is the size of the size of the dungeon grid.
pub const MAP_SIZE: u16 = 100;
/// `MAP_SIZE` as a usize
pub const MAP_SIZE_USIZE: usize = MAP_SIZE as usize;
/// The number of tiles in the dungeon grid
pub const TILE_COUNT: usize = MAP_SIZE_USIZE * MAP_SIZE_USIZE;
/// The `Tile` enum represents what is (or is not) at
/// any given spot in the dungeon grid.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)]
pub enum Tile {
/// `Wall` represents an impassible wall
Wall,
/// `Air` represents empty walkable space
Air,
/// `Stairs` represents stairs to another floor
Stairs,
}
impl Tile {
/// Returns a list of all possible tiles
pub fn values() -> impl Iterator<Item = Self> {
Self::iter()
}
}
impl Default for Tile {
fn default() -> Self {
Self::Air
}
}
/// The `Floor` type represents the current playing
/// grid of the dungeon. It contains the tiles of the
/// grid, and the starting position of the player.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Floor {
/// The dungeon grid
tiles: Box<[Tile; TILE_COUNT]>,
/// The position the player starts at
player_start: Pos,
/// The seed used when generating the dungeon grid
seed: u64,
/// The computed hash of the tile map
hash: RefCell<u64>,
/// If the tiles are dirty (hash needs to be recomputed)
dirty: RefCell<bool>,
}
impl Floor {
/// Internal constructor for `Floor`
fn new(tiles: Box<[Tile; TILE_COUNT]>, player_start: Pos, seed: u64) -> Self {
Self {
tiles,
player_start,
seed,
hash: RefCell::new(0),
dirty: RefCell::new(true),
}
}
/// Generates a dungeon `Floor` using wave function collapse.
///
/// # Examples
///
/// ```no_run
/// use dungeon::Floor;
///
/// let floor = Floor::generate();
/// ```
#[must_use]
pub fn generate() -> Self {
let seed = rand::random();
Self::generate_seeded(seed)
}
/// Genreates a dungeon `Floor` using wave function collapse provided with a seed.
///
/// The provided seed is used for randomness in the wave function
/// collapse algorithm.
///
/// # Examples
///
/// ```no_run
/// use dungeon::Floor;
///
/// /// here is our very seedy seed
/// let seed = 2893249402u64;
/// let floor = Floor::generate_seeded(seed);
/// ```
#[must_use]
pub fn generate_seeded(seed: u64) -> Self {
let mut wfc = Wfc::new(seed, MAP_SIZE);
wfc.initialize_states();
wfc.run();
// TODO
unimplemented!()
}
/// Returns the start position of the player
#[must_use]
pub const fn player_start(&self) -> Pos {
self.player_start
}
/// Returns the seed used to generate the map
#[must_use]
pub const fn seed(&self) -> u64 {
self.seed
}
/// Returns a `Tile` on the dungeon grid at `Pos`.
#[must_use]
pub const fn get(&self, pos: Pos) -> Tile {
let idx = pos.idx();
self.tiles[idx]
}
/// Returns a multable reference to a `Tile` on the dungeon grid at `Pos`.
#[must_use]
pub fn get_mut(&mut self, pos: Pos) -> &mut Tile {
*self.dirty.get_mut() = true;
let idx = pos.idx();
&mut self.tiles[idx]
}
/// Returns a reference to all tiles in the `Floor`.
/// The size of this lise will always be `TILE_COUNT` long.
#[must_use]
pub const fn tiles(&self) -> &[Tile] {
&*self.tiles
}
/// Returns a mutable reference to all tiles in the `Floor`.
/// The size of this lise will always be `TILE_COUNT` long.
#[must_use]
pub fn tiles_mut(&mut self) -> &mut [Tile] {
*self.dirty.get_mut() = true;
&mut *self.tiles
}
/// Computes the hash of the tile map
#[must_use]
pub fn hash(&self) -> u64 {
// initial (immutable) dirty check
let dirty = self.dirty.borrow();
if *dirty {
return *self.hash.borrow();
}
drop(dirty);
// recompute hash
let mut dirty = self.dirty.borrow_mut();
let mut hash = self.hash.borrow_mut();
let mut s = DefaultHasher::new();
self.tiles.hash(&mut s);
*hash = s.finish();
*dirty = false;
*hash
}
}
impl Default for Floor {
/// Returns a floor with a single set of walls on the map border
fn default() -> Self {
let player_start = const_pos!(1, 1);
let mut tiles = Box::new([Tile::Air; TILE_COUNT]);
let seed = 0u64;
for pos in Pos::values() {
if pos.is_border() {
tiles[pos.idx()] = Tile::Wall;
}
}
Self::new(tiles, player_start, seed)
}
}
|