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
//! Example generator usage:
//! ```
//! use rand::prelude::*;
//! use mapgen::{MapBuffer, MapFilter};
//! use mapgen::filter::DrunkardsWalk;
//! 
//! let mut rng = StdRng::seed_from_u64(100);
//! let gen = DrunkardsWalk::open_area();
//! let map = gen.modify_map(&mut rng, &MapBuffer::new(80, 50));
//! 
//! assert_eq!(map.width, 80);
//! assert_eq!(map.height, 50);
//! ```
//! 

use rand::prelude::*;
use crate::MapFilter;
use crate::{
    map_buffer::{MapBuffer, Symmetry},
    geometry::Point,
    random::Rng
};


#[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random }

pub struct DrunkardsWalk {
    spawn_mode : DrunkSpawnMode,
    drunken_lifetime : i32,
    floor_percent: f32,
    brush_size: usize,
    symmetry: Symmetry
}

impl MapFilter for DrunkardsWalk {
    fn modify_map(&self, rng: &mut StdRng, map: &MapBuffer)  -> MapBuffer {
        self.build(rng, map)
    }
}

impl DrunkardsWalk {
    pub fn new( spawn_mode: DrunkSpawnMode, 
                drunken_lifetime: i32, 
                floor_percent: f32,
                brush_size: usize, 
                symmetry: Symmetry) -> Box<DrunkardsWalk>
    {
        Box::new(DrunkardsWalk{
            spawn_mode,
            drunken_lifetime,
            floor_percent,
            brush_size,
            symmetry
        })
    }

    pub fn open_area() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::StartingPoint, 400, 0.5, 1, Symmetry::None)
    }

    pub fn open_halls() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.5, 1, Symmetry::None)
    }

    pub fn winding_passages() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::None)
    }

    pub fn fat_passages() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.4, 2, Symmetry::None)
    }

    pub fn fearful_symmetry() -> Box<DrunkardsWalk> {
        Self::new(DrunkSpawnMode::Random, 400, 0.4, 1, Symmetry::Both)
    }
    
    fn build(&self, rng: &mut StdRng, map: &MapBuffer) -> MapBuffer {
        let mut new_map = map.clone();
        // Set a central starting point
        let starting_position = Point::new( new_map.width / 2, new_map.height / 2 );
        new_map.set_walkable(starting_position.x, starting_position.y, true);

        let total_tiles = new_map.width * new_map.height;
        let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize;
        let mut floor_tile_count = new_map.walkables.iter().filter(|&&a| a).count();
        let mut digger_count = 0;
        while floor_tile_count  < desired_floor_tiles {
            let mut drunk_x;
            let mut drunk_y;
            match self.spawn_mode {
                DrunkSpawnMode::StartingPoint => {
                    drunk_x = starting_position.x;
                    drunk_y = starting_position.y;
                }
                DrunkSpawnMode::Random => {
                    if digger_count == 0 {
                        drunk_x = starting_position.x;
                        drunk_y = starting_position.y;
                    } else {
                        drunk_x = rng.roll_dice(1, new_map.width - 3) + 1;
                        drunk_y = rng.roll_dice(1, new_map.height - 3) + 1;
                    }
                }
            }
            let mut drunk_life = self.drunken_lifetime;

            while drunk_life > 0 {
                new_map.set_walkable(drunk_x, drunk_y, false); 
                new_map.paint(self.symmetry, self.brush_size, drunk_x, drunk_y);

                let stagger_direction = rng.roll_dice(1, 4);
                match stagger_direction {
                    1 => { if drunk_x > 1 { drunk_x -= 1; } }
                    2 => { if drunk_x < new_map.width-2 { drunk_x += 1; } }
                    3 => { if drunk_y > 1 { drunk_y -=1; } }
                    _ => { if drunk_y < new_map.height-2 { drunk_y += 1; } }
                }

                drunk_life -= 1;
            }

            digger_count += 1;
            floor_tile_count = new_map.walkables.iter().filter(|&&a| a).count();
        }

        new_map
    }
}