thedes_gen/
game.rs

1use rand::Rng;
2use rand_distr::{Triangular, TriangularError};
3use thedes_async_util::progress;
4use thedes_domain::{
5    game::{self, Game},
6    geometry::Coord,
7    player::{self, PlayerPosition},
8};
9use thedes_geometry::orientation::{Axis, Direction};
10use thiserror::Error;
11
12use crate::{map, random::PickedReproducibleRng};
13
14#[derive(Debug, Error)]
15pub enum InitError {
16    #[error("Error initializing map generator")]
17    Map(
18        #[from]
19        #[source]
20        map::InitError,
21    ),
22}
23
24#[derive(Debug, Error)]
25pub enum Error {
26    #[error("Error generating map")]
27    Map(
28        #[from]
29        #[source]
30        map::Error,
31    ),
32    #[error("Error creating random distribution for player's head in axis {1}")]
33    PlayerHeadDistr(#[source] TriangularError, Axis),
34    #[error("Failed to create a game")]
35    Creation(
36        #[source]
37        #[from]
38        game::InitError,
39    ),
40    #[error("Failed to create a player")]
41    CreatePlayer(
42        #[source]
43        #[from]
44        player::InitError,
45    ),
46}
47
48#[derive(Debug, Clone)]
49pub struct Config {
50    map_config: map::Config,
51}
52
53impl Default for Config {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl Config {
60    pub fn new() -> Self {
61        Self { map_config: map::Config::new() }
62    }
63
64    pub fn with_map(self, map_config: map::Config) -> Self {
65        Self { map_config, ..self }
66    }
67
68    pub fn finish(
69        self,
70        rng: &mut PickedReproducibleRng,
71    ) -> Result<Generator, InitError> {
72        Ok(Generator { map_gen: self.map_config.finish(rng)? })
73    }
74}
75
76#[derive(Debug)]
77pub struct Generator {
78    map_gen: map::Generator,
79}
80
81impl Generator {
82    pub fn progress_goal(&self) -> usize {
83        self.map_gen.progress_goal() + 1
84    }
85
86    pub async fn execute(
87        self,
88        rng: &mut PickedReproducibleRng,
89        progress_logger: progress::Logger,
90    ) -> Result<Game, Error> {
91        progress_logger.set_status("generating map");
92        let map = self.map_gen.execute(rng, progress_logger.nest()).await?;
93
94        progress_logger.set_status("generating player");
95        let player_head_distr = map
96            .rect()
97            .size
98            .map_with_axes(|coord, axis| {
99                let min = 2.0;
100                let max = f64::from(coord) - 2.0 - f64::EPSILON;
101                let mode = min + (max - min) / 2.0;
102                Triangular::new(min, max, mode)
103                    .map_err(|error| Error::PlayerHeadDistr(error, axis))
104            })
105            .transpose()?;
106        let player_head_offset =
107            player_head_distr.as_ref().map(|distr| rng.sample(distr) as Coord);
108        let player_head = map.rect().top_left + player_head_offset;
109        let player_facing_index = rng.random_range(0 .. Direction::ALL.len());
110        let player_facing = Direction::ALL[player_facing_index];
111        let player_pos = PlayerPosition::new(player_head, player_facing)?;
112        let game = Game::new(map, player_pos)?;
113        progress_logger.increment();
114
115        progress_logger.set_status("done");
116        Ok(game)
117    }
118}