summaryrefslogtreecommitdiff
path: root/src/nn.rs
blob: be600eaec43f6b613b0132337e1db3337ab4bf92 (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
use rand::Rng;
use crate::game::{Game, GameLog, GameLogger, PointLogger, Status, Field};
use crate::doublebuffer::DoubleBuffer;

#[derive(Debug, Clone)]
pub struct Layer {
    /// vector of (vectors of weights onto the past layer)
    nodes: Vec<Vec<f32>>,
}

#[derive(Debug, Clone)]
pub struct Nn {
    layers: Vec<Layer>
}

fn randaround(x: f32) -> f32 {
    rand::thread_rng().gen_range(-x, x)
}

impl Nn {
    pub fn new_empty(layers: &[u32]) -> Self {
        Self {
            layers: layers.iter().take(layers.len() - 1).zip(layers.iter().skip(1)).map(|(&n1, &n2)| Layer {
                nodes: std::iter::repeat(std::iter::repeat(1.0).take(n1 as usize).collect()).take(n2 as usize).collect()
            }).collect(),
        }
    }

    pub fn new_random(layers: &[u32]) -> Self {
        Self {
            layers: layers.iter().take(layers.len() - 1).zip(layers.iter().skip(1)).map(|(&n1, &n2)| Layer {
                nodes: std::iter::repeat_with(|| (0..(n1 as usize)).map(|_| randaround(1.0)).collect()).take(n2 as usize).collect()
            }).collect(),
        }
    }

    pub fn add_noise(&mut self, hard_noise: f32, fine_noise: f32, w: f32, r: f32) {
        let complete = randaround(1.0).abs() < r;
        for Layer { nodes: layer_mappings } in self.layers.iter_mut() {
            for layer_mapping in layer_mappings.iter_mut() {
                layer_mapping.iter_mut().for_each(|v| {
                    if complete {
                        *v = randaround(hard_noise);
                    } else {
                        *v += if randaround(1.0).abs() < w
                            { randaround(hard_noise) } else { randaround(fine_noise) }
                    }
                });
            }
        }
    }

    pub fn new_by_game_res<F: Fn(&[u32]) -> Self>(cols: u32, rows: u32, f: &F) -> Self {
        // fields + 1xplayer_y + 1xplayer_v
        let input_layer_size = cols * rows + 1 + 1;
        f(&[input_layer_size,
          rows * cols,
          cols,
          1])
    }

    fn status_to_layer(status: &Status, _cols: u32, rows: u32) -> Vec<f32> {
        [status.player[1] as f32 / rows as f32, status.player_v as f32].iter()
            .map(|&x| x)
            .chain(
            status.fields.iter().map(|f| match f {
                Field::Air => 0.0,
                Field::Wall => 1.0,
                Field::Spike => -1.0,
            })
            ).collect()
    }

    pub fn execute<G: Game>(&self, game: G) -> u64 {
        self.execute_with_logger::<G, PointLogger>(game)
    }

    pub fn execute_logged<G: Game>(&self, game: G) -> GameLog {
        self.execute_with_logger::<G, GameLog>(game)
    }

    fn execute_with_logger<G: Game, L: GameLogger>(&self, mut game: G) -> L::Output {
        let (cols, rows) = game.size();
        let mut log = L::new_empty(cols, rows);
        log.append(&game.status());
        while let Some(status) = game.update() {
            log.append(&status);
            let input_layer = Self::status_to_layer(&status, cols, rows);
            let mut db = DoubleBuffer::new(input_layer, vec![]);
            for Layer {nodes: layer_mappings} in self.layers.iter().skip(1) {
                *db.second_mut() = layer_mappings.iter().map(|layer_mapping| {
                    layer_mapping.iter().zip(db.first().iter())
                        .map(|(w, v)| v * w)
                        .sum()
                }).collect();
                db.switch();
            }
            //println!("out: {:?} | threshold: {}", db.first(), cols);
            if db.first()[0] >= cols as f32 {
                game.jump();
            }
        }
        log.extract()
    }
}