Generate a grid with cellular automata
authorTomas Wenström <tomas.wenstrom@gmail.com>
Sat, 23 Jan 2021 20:17:58 +0000 (21:17 +0100)
committerTomas Wenström <tomas.wenstrom@gmail.com>
Sat, 23 Jan 2021 20:17:58 +0000 (21:17 +0100)
src/core/game.rs
src/core/level.rs [new file with mode: 0644]
src/core/mod.rs

index e5e2aa6..55bb80c 100644 (file)
@@ -2,10 +2,12 @@ use AppState;
 use common::Point2D;
 use core::controller::Controller;
 use core::controller::ControllerManager;
+use core::level::Level;
 use core::render::Renderer;
 use point;
 use sdl2::event::Event;
 use sdl2::joystick::PowerLevel;
+use sdl2::keyboard::Keycode;
 use sdl2::rect::Rect;
 use sprites::SpriteManager;
 use std::cell::RefCell;
@@ -44,7 +46,20 @@ impl AppState for GameState {
        self.world.render(renderer, sprites);
     }
 
-    fn handle_event(&mut self, _event: Event) {}
+    fn handle_event(&mut self, event: Event) {
+       match event {
+           Event::KeyDown { keycode: Some(Keycode::Space), .. } => {
+               self.world.level.regenerate();
+           }
+           Event::KeyDown { keycode: Some(Keycode::KpPlus), .. } => {
+               self.world.level.increase_iteration();
+           }
+           Event::KeyDown { keycode: Some(Keycode::KpMinus), .. } => {
+               self.world.level.decrease_iteration();
+           }
+           _ => {}
+       }
+    }
 }
 
 ////////// WORLD ///////////////////////////////////////////////////////////////
@@ -58,10 +73,7 @@ pub struct World {
 impl World {
     pub fn new() -> Self {
        World {
-           level: Level {
-               gravity: point!(0.0, 0.1),
-               ground: 600.0,
-           },
+           level: Level::new(point!(0.0, 0.1), 600.0),
            ..Default::default()
        }
     }
@@ -92,25 +104,6 @@ impl World {
     }
 }
 
-////////// LEVEL ///////////////////////////////////////////////////////////////
-
-#[derive(Default)]
-pub struct Level {
-    gravity: Point2D<f64>,
-    ground: f64,               // just to have something
-}
-
-impl Level {
-    pub fn render(&mut self, renderer: &mut Renderer, _sprites: &SpriteManager) {
-       let w = renderer.viewport().0 as i32;
-       for i in 1..11 {
-           let y = (i * i - 1) as i32 + self.ground as i32;
-           renderer.canvas().set_draw_color((255 - i * 20, 255 - i * 20, 0));
-           renderer.canvas().draw_line((0, y), (w, y)).unwrap();
-       }
-    }
-}
-
 ////////// OBJECT //////////////////////////////////////////////////////////////
 
 type Objects = Vec<Box<dyn Object>>;
@@ -248,15 +241,19 @@ impl Object for Boll {
            }
        }
 
-       if self.pos.x <= 0.0 || self.pos.x >= 1280.0 { // only for testing
-           self.pos.x = self.pos.x.max(0.0).min(1280.0);
-           self.vel.x = -self.vel.x;
-           self.bounces = 0;
+       let x = (self.pos.x / lvl.grid.cell_size as f64).min(lvl.grid.width as f64 - 1.0).max(0.0) as usize;
+       let y = (self.pos.y / lvl.grid.cell_size as f64).min(lvl.grid.height as f64 - 1.0).max(0.0) as usize;
+       if lvl.grid.cells[x][y] {
+           if self.bounces == 0 {
+               return Dead
+           }
+           self.vel = -self.vel;
+           self.bounces -= 1;
            use rand::distributions::{Distribution, Normal};
            let normal = Normal::new(0.5, 0.4);
            objects.push(Box::new(Boll {
-               vel: self.vel * normal.sample(&mut rand::thread_rng()),
-               ..*self
+               vel: self.vel * normal.sample(&mut rand::thread_rng()),
+               ..*self
            }));
        }
 
@@ -267,7 +264,7 @@ impl Object for Boll {
         let block = _sprites.get("block");
        let size = 4 + self.bounces * 6;
         renderer.blit(block, None, Rect::new(self.pos.x as i32 - size as i32 / 2, self.pos.y as i32 - size as i32 / 2, size as u32, size as u32));
-       // renderer.set_draw_color((0, self.bounces * 100, 255));
-       // renderer.draw_point((self.pos.x as i32, self.pos.y as i32)).unwrap();
+       // renderer.canvas().set_draw_color((0, self.bounces * 100, 255));
+       // renderer.canvas().draw_point((self.pos.x as i32, self.pos.y as i32)).unwrap();
     }
 }
diff --git a/src/core/level.rs b/src/core/level.rs
new file mode 100644 (file)
index 0000000..f0c6c0e
--- /dev/null
@@ -0,0 +1,125 @@
+use common::Point2D;
+use core::render::Renderer;
+use rand::Rng;
+use sprites::SpriteManager;
+
+////////// LEVEL ///////////////////////////////////////////////////////////////
+
+#[derive(Default)]
+pub struct Level {
+    pub gravity: Point2D<f64>,
+    pub ground: f64,           // just to have something
+    pub grid: Grid,
+    iterations: u8,
+}
+
+impl Level {
+    pub fn new(gravity: Point2D<f64>, ground: f64) -> Self {
+       Level { gravity, ground, grid: Grid::generate(10), iterations: 10 }
+    }
+
+    pub fn regenerate(&mut self) {
+       self.grid = Grid::generate(self.iterations);
+    }
+
+    pub fn increase_iteration(&mut self) {
+       self.iterations += 1;
+       self.regenerate();
+       println!("iterate {} time(s)", self.iterations);
+    }
+
+    pub fn decrease_iteration(&mut self) {
+       self.iterations -= 1;
+       self.regenerate();
+       println!("iterate {} time(s)", self.iterations);
+    }
+
+    pub fn render(&mut self, renderer: &mut Renderer, _sprites: &SpriteManager) {
+       let w = renderer.viewport().0 as i32;
+
+       renderer.canvas().set_draw_color((64, 64, 64));
+       let size = self.grid.cell_size;
+       for x in 0..self.grid.width {
+           for y in 0..self.grid.height {
+               if self.grid.cells[x][y] {
+                   renderer.canvas().fill_rect(sdl2::rect::Rect::new(x as i32 * size as i32, y as i32 * size as i32, size as u32, size as u32)).unwrap();
+               }
+           }
+       }
+
+       for i in 1..11 {
+           let y = (i * i - 1) as i32 + self.ground as i32;
+           renderer.canvas().set_draw_color((255 - i * 20, 255 - i * 20, 0));
+           renderer.canvas().draw_line((0, y), (w, y)).unwrap();
+       }
+    }
+}
+
+////////// GRID ////////////////////////////////////////////////////////////////
+
+#[derive(Default)]
+pub struct Grid {
+    pub width: usize,
+    pub height: usize,
+    pub cell_size: usize,
+    pub cells: Vec<Vec<bool>>,
+}
+
+impl Grid {
+    fn generate(iterations: u8) -> Grid {
+       let cell_size = 10;
+       let (width, height) = (1280 / cell_size, 600 / cell_size);
+       let mut cells = vec!(vec!(true; height); width);
+
+       let mut rng = rand::thread_rng();
+
+       // randomize
+       for x in 1..(width - 1) {
+           for y in 1..(height - 1) {
+               cells[x][y] = rng.gen_range(0, 100) > 55;
+           }
+       }
+
+       // smooth
+       // let mut count = 0;
+       // loop {
+       //     count += 1;
+       //     println!("iteration {}", count);
+       for _i in 0..iterations {
+           let mut next = vec!(vec!(true; height); width);
+           for x in 1..(width - 1) {
+               for y in 1..(height - 1) {
+                   match Grid::neighbours(&cells, x, y) {
+                       n if n < 4 => next[x][y] = false,
+                       n if n > 4 => next[x][y] = true,
+                       _ => next[x][y] = cells[x][y]
+                   };
+               }
+           }
+           if cells == next {
+               break;
+           } else {
+               cells = next;
+           }
+       }
+
+       Grid {
+           width,
+           height,
+           cell_size,
+           cells
+       }
+    }
+
+    fn neighbours(grid: &Vec<Vec<bool>>, px: usize, py: usize) -> u8 {
+       let mut count = 0;
+       for x in (px - 1)..=(px + 1) {
+           for y in (py - 1)..=(py + 1) {
+               if !(x == px && y == py) && grid[x][y] {
+                   count += 1;
+               }
+           }
+       }
+       count
+    }
+}
index c2a1f0a..9e25ba4 100644 (file)
@@ -2,3 +2,4 @@ pub mod app;
 pub mod controller;
 pub mod game;
 pub mod render;
+pub mod level;