summaryrefslogtreecommitdiffstats
path: root/src/backend.rs
blob: 6dae109a82e6aca5bb676ad8fbe996db39bdddc3 (plain)
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
use anyhow::Result;
use std::sync::Arc;
use std::collections::LinkedList;
use tokio::sync::RwLock;

pub struct Landscape {
    elements: Vec<Arc<RwLock<LandscapeElement>>>,
}

impl Landscape {
    pub fn new(elements: Vec<usize>) -> Self {
        let elements = elements
            .into_iter()
            .map(LandscapeElement::new)
            .map(RwLock::new)
            .map(Arc::new)
            .collect();

        Landscape { elements }
    }

    // here is where the fun is
    pub async fn let_it_rain(mut self, hours: usize) -> Result<RainyLandscape> {
        unimplemented!()
    }
}

enum RainElement {
    FullRainElement,
    HalfRainElement
}

impl RainElement {
    fn as_f32(&self) -> f32 {
        match self {
            RainElement::FullRainElement => 1.0,
            RainElement::HalfRainElement => 0.5,
        }
    }
}

#[derive(getset::Setters, getset::Getters)]
struct LandscapeElement {
    #[getset(get = "pub")]
    height: usize,

    #[getset(get = "pub")]
    rain_elements: Vec<RainElement>,
}

impl LandscapeElement {
    fn new(height: usize) -> Self {
        LandscapeElement {
            height,
            rain_elements: Vec::new(),
        }
    }

    pub fn get_current_height(&self) -> f32 {
        let rain: f32 = self.rain_elements.iter().map(|e| e.as_f32()).sum();
        (self.height as f32) + rain
    }

    pub fn increase_rain_level(&mut self, el: RainElement) {
        self.rain_elements.push(el);
    }
}

pub struct ElementRainingSimulation {
    element: Arc<RwLock<LandscapeElement>>,

    left_neighbor: Option<Arc<RwLock<LandscapeElement>>>,
    right_neighbor: Option<Arc<RwLock<LandscapeElement>>>,
}

impl ElementRainingSimulation {
    pub async fn let_it_rain(self, mut hours: usize) -> Result<()> {
        while hours != 0 {
            match (self.left_neighbor.as_ref(), self.right_neighbor.as_ref()) {
                // simple case: this is the only element on the landscape, so it only rains on this
                // element
                (None, None) => self.element.write().await.increase_rain_level(RainElement::FullRainElement),


                // This element is the rightmost element in the landscape
                (Some(left), None) => {
                    let (mut left_writelock, mut this_writelock) = tokio::join!(left.write(), self.element.write());

                    if left_writelock.get_current_height() < this_writelock.get_current_height() {
                        left_writelock.increase_rain_level(RainElement::FullRainElement);
                    } else {
                        this_writelock.increase_rain_level(RainElement::FullRainElement);
                    }
                }

                // This element is the leftmost element in the landscape
                (None, Some(right)) => {
                    let (mut this_writelock, mut right_writelock) = tokio::join!(self.element.write(), right.write());

                    if this_writelock.get_current_height() > right_writelock.get_current_height() {
                        right_writelock.increase_rain_level(RainElement::FullRainElement);
                    } else {
                        this_writelock.increase_rain_level(RainElement::FullRainElement);
                    }
                }

                (Some(left), Some(right)) => {
                    let (mut left_writelock, mut this_writelock, mut right_writelock) = tokio::join!(left.write(), self.element.write(), right.write());

                    let l_h = left_writelock.get_current_height();
                    let t_h = this_writelock.get_current_height();
                    let r_h = right_writelock.get_current_height();

                    if l_h < t_h && r_h < t_h {
                        // we are higher than both our neighbors
                        left_writelock.increase_rain_level(RainElement::HalfRainElement);
                        right_writelock.increase_rain_level(RainElement::HalfRainElement);
                    } else if l_h < t_h && r_h >= t_h {
                        // only left of us is lower than us
                        left_writelock.increase_rain_level(RainElement::FullRainElement);
                    } else if l_h >= t_h && r_h < t_h {
                        // only right of us is lower than us
                        right_writelock.increase_rain_level(RainElement::FullRainElement);
                    } else if l_h >= t_h && r_h >= t_h {
                        // both neighbors are higher than us
                        this_writelock.increase_rain_level(RainElement::FullRainElement);
                    }
                }
            }

            hours -= 1;
        }

        Ok(())
    }
}

#[derive(getset::Getters)]
pub struct RainyLandscape {
    #[getset(get = "pub")]
    elements: LinkedList<LandscapeElement>
}

#[cfg(test)]
mod tests {
    #[test]
    fn let_it_rain_simple() {
        let ls = Landscape::new(vec![1]);
        let rls = ls.let_it_rain(1);

        assert_eq!(rls.elements().len(), 1);
        assert_eq!(rls.elements().get(0).unwrap(), 2);
    }
}