core_rust/core/game/utils/
world.rs1use super::errors::GameError;
2use super::unit::Unit;
3
4#[derive(Debug, Clone, Copy)]
6pub enum Terrain {
7 Grass,
8 Mountain,
9 Goal,
10}
11
12#[derive(Debug, Clone, Copy)]
14pub struct Tile {
15 _x: usize,
16 _y: usize,
17 terrain: Terrain,
18 walkable: bool,
19 unit: Option<Unit>,
20}
21
22impl Tile {
23 pub fn new(x: usize, y: usize, terrain: Terrain) -> Self {
25 let walkable = !matches!(terrain, Terrain::Mountain);
26
27 Tile {
28 _x: x,
29 _y: y,
30 terrain,
31 walkable,
32 unit: None,
33 }
34 }
35
36 pub fn is_walkable(self) -> bool {
38 self.walkable
39 }
40
41 pub fn is_occupied(self) -> bool {
43 self.unit.is_some()
44 }
45
46 pub fn place_unit(&mut self, unit: Unit) {
48 self.unit = Some(unit)
49 }
50
51 pub fn remove_unit(&mut self) {
53 self.unit = None
54 }
55
56 pub fn terrain(&self) -> Terrain {
58 self.terrain
59 }
60}
61
62#[derive(Debug, Clone)]
64pub struct World {
65 pub size: usize,
66 tiles: Vec<Vec<Tile>>,
67 pub goal: (usize, usize),
68}
69
70impl World {
71 pub fn new(world_vector: &[Vec<char>]) -> Result<Self, GameError> {
73 let rows = world_vector.len();
74 if rows == 0 {
75 return Err(GameError::WorldShapeError);
76 }
77
78 for row in world_vector.iter() {
79 if row.len() != rows {
80 return Err(GameError::WorldShapeError);
81 }
82 }
83
84 let mut tiles: Vec<Vec<Tile>> = Vec::with_capacity(rows);
85 let mut goal_location: Option<(usize, usize)> = None;
86
87 for (i, world_row) in world_vector.iter().enumerate() {
88 let mut tile_row: Vec<Tile> = Vec::with_capacity(world_row.len());
90
91 for (j, ch) in world_row.iter().enumerate() {
92 let terrain = match *ch {
93 '.' => Terrain::Grass,
94 'X' => Terrain::Mountain,
95 'G' => {
96 if goal_location.is_some() {
98 return Err(GameError::DuplicateGoal);
100 }
101 goal_location = Some((i, j));
102 Terrain::Goal
103 }
104 character => {
105 return Err(GameError::InvalidTileCharacter { character });
107 }
108 };
109
110 tile_row.push(Tile::new(i, j, terrain));
111 }
112
113 tiles.push(tile_row);
114 }
115
116 let goal = goal_location.ok_or(GameError::MissingGoal)?;
118
119 Ok(World {
120 size: rows,
121 tiles,
122 goal,
123 })
124 }
125
126 pub fn print(&self) {
128 let cell_width = 5;
129 let size = self.size;
130
131 print!(" ");
132 for col in 0..size {
133 print!("{:^width$}", col, width = cell_width);
134 }
135 println!();
136
137 let hor = format!(" +{}+", "-".repeat(cell_width).repeat(size));
138 println!("{}", hor);
139
140 for (y, row) in self.tiles.iter().enumerate() {
141 print!("{:<2}|", y);
142
143 for tile in row {
144 let symbol = if let Some(unit) = &tile.unit {
145 unit.get_symbol().to_string()
146 } else {
147 match tile.terrain {
148 Terrain::Grass => "·".into(),
149 Terrain::Mountain => "X".into(),
150 Terrain::Goal => "G".into(),
151 }
152 };
153 print!("{:^width$}|", symbol, width = cell_width);
154 }
155 println!();
156
157 println!("{}", hor);
158 }
159 }
160
161 fn get_unit_position(&mut self) -> Result<(usize, usize), GameError> {
163 for (y, row) in self.tiles.iter().enumerate() {
164 for (x, tile) in row.iter().enumerate() {
165 if tile.is_occupied() {
166 return Ok((x, y));
167 }
168 }
169 }
170 Err(GameError::UnitNotFound)
171 }
172
173 pub fn place_unit(&mut self, unit: Unit) {
175 let (x, y) = unit.get_position();
176 self.tiles[y][x].place_unit(unit);
177 }
178
179 pub fn remove_unit(&mut self) -> Result<(), GameError> {
181 let (x, y) = match self.get_unit_position() {
182 Ok((x, y)) => (x, y),
183 Err(e) => return Err(e),
184 };
185
186 self.tiles[y][x].remove_unit();
187
188 Ok(())
189 }
190
191 pub fn get_tile(&mut self, x: usize, y: usize) -> &Tile {
193 &self.tiles[y][x]
194 }
195}