1
0

feat: initial commit?

This commit is contained in:
Rokas Puzonas 2022-06-03 21:02:24 +03:00
commit a2241e5123
6 changed files with 251 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

3
.rustfmt.toml Normal file
View File

@ -0,0 +1,3 @@
hard_tabs = true
tab_spaces = 2
max_width = 80

7
Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "rust-game-of-life"
version = "0.1.0"
edition = "2021"
[dependencies]
raylib = "3.7.0"

1
README.md Normal file
View File

@ -0,0 +1 @@
# Game Of Life using Rust

85
src/game_of_life.rs Normal file
View File

@ -0,0 +1,85 @@
use std::collections::HashSet;
pub type Point = (i32, i32);
#[derive(Debug)]
pub struct GameOfLife {
pub alive_tiles: HashSet<(i32, i32)>,
}
impl GameOfLife {
pub fn new() -> GameOfLife {
GameOfLife {
alive_tiles: HashSet::new(),
}
}
pub fn get_tile(&self, point: Point) -> bool {
self.alive_tiles.contains(&point)
}
pub fn toggle_tile(&mut self, point: Point) {
if self.alive_tiles.contains(&point) {
self.alive_tiles.remove(&point);
} else {
self.alive_tiles.insert(point);
}
}
fn iter_neighbours(&self, (x, y): Point) -> Vec<Point> {
(x-1..=x+1)
.flat_map(move |x| (y-1..=y+1).map(move |y| (x, y)))
.filter(|point| !(point.0 == x && point.1 == y))
.collect::<Vec<_>>()
}
fn count_neighbours(&self, point: Point) -> u8 {
self.iter_neighbours(point).iter()
.filter(|point| self.alive_tiles.contains(point))
.count() as u8
}
fn get_dead_tiles(&self) -> HashSet<Point> {
self.alive_tiles.iter()
.flat_map(|point| self.iter_neighbours(*point))
.filter(|point| !self.alive_tiles.contains(point))
.collect::<HashSet<_>>()
}
pub fn tick(&mut self) {
let mut new_alive_tiles = HashSet::new();
for alive_tile in &self.alive_tiles {
let neighours = self.count_neighbours(*alive_tile);
if neighours == 2 || neighours == 3 {
new_alive_tiles.insert(*alive_tile);
}
}
for dead_tile in self.get_dead_tiles() {
let neighours = self.count_neighbours(dead_tile);
if neighours == 3 {
new_alive_tiles.insert(dead_tile);
}
}
self.alive_tiles = new_alive_tiles;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut game = GameOfLife::new();
game.toggle_tile((3, 3));
game.toggle_tile((4, 3));
game.toggle_tile((4, 4));
game.toggle_tile((3, 4));
assert_eq!(game.count_neighbours((3, 3)), 3);
}
}

141
src/main.rs Normal file
View File

@ -0,0 +1,141 @@
mod game_of_life;
use std::ffi::CString;
use game_of_life::*;
use raylib::prelude::*;
fn draw_label(d: &mut RaylibDrawHandle, x: f32, y: f32, text: &str) {
d.gui_label(rrect(x, y, 50., 0.), Some(&CString::new(text).unwrap()));
}
fn to_tile_space(x: i32, y: i32, offset_x: i32, offset_y: i32, tile_size: i32) -> (i32, i32) {
(
((x - offset_x) as f32 / tile_size as f32).floor() as i32,
((y - offset_y) as f32 / tile_size as f32).floor() as i32,
)
}
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.resizable()
.title("Game Of Life")
.build();
rl.set_target_fps(60);
let mut game = GameOfLife::new();
let tile_size = 16;
let edge_thickness = 1.0;
let mut dead_color = Color::BLACK;
let mut alive_color = Color::WHITE;
let mut edge_color = Color::GRAY;
let overlay_color = Color { r: 0, g: 0, b: 0, a: 180 };
let mut running = false;
let mut update_rate = 10; // in times per second
let mut timer = 0;
let mut camera_x = 0;
let mut camera_y = 0;
let mut last_mouse = (0, 0);
let mut show_options = true;
while !rl.window_should_close() {
let screen_width = rl.get_screen_width();
let screen_height = rl.get_screen_height();
let offset_x = camera_x + screen_width/2;
let offset_y = camera_y + screen_height/2;
if rl.is_key_pressed(KeyboardKey::KEY_TAB) {
show_options = !show_options
}
if !show_options {
if rl.is_key_pressed(KeyboardKey::KEY_ENTER) {
running = !running;
}
if running {
if timer == 0 {
game.tick();
timer = 60 / update_rate;
}
timer -= 1;
}
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) && !running {
game.tick();
}
let mx = rl.get_mouse_x();
let my = rl.get_mouse_y();
let (tile_x, tile_y) = to_tile_space(mx, my, offset_x, offset_y, tile_size);
if rl.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
let (last_tile_x, last_tile_y) = to_tile_space(last_mouse.0, last_mouse.1, offset_x, offset_y, tile_size);
if !(tile_x == last_tile_x && tile_y == last_tile_y) {
game.toggle_tile((tile_x, tile_y));
running = false;
}
}
if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) {
game.toggle_tile((tile_x, tile_y));
running = false;
}
if rl.is_mouse_button_down(MouseButton::MOUSE_MIDDLE_BUTTON) {
let dx = rl.get_mouse_x() - last_mouse.0;
let dy = rl.get_mouse_y() - last_mouse.1;
camera_x += dx;
camera_y += dy;
}
last_mouse = (mx, my);
}
let mut d = rl.begin_drawing(&thread);
d.clear_background(dead_color);
for tile_dy in 0..screen_height/tile_size {
for tile_dx in 0..screen_width/tile_size {
let tile_x = tile_dx - offset_x / tile_size;
let tile_y = tile_dy - offset_y / tile_size;
if game.get_tile((tile_x, tile_y)) {
let x = tile_dx * tile_size + (offset_x % tile_size);
let y = tile_dy * tile_size + (offset_y % tile_size);
d.draw_rectangle(x, y, tile_size, tile_size, alive_color);
}
}
}
for y in (0..screen_height+tile_size).step_by(tile_size as usize) {
let y = y + offset_y % tile_size;
d.draw_line_ex(rvec2(0.0, y), rvec2(screen_width, y), edge_thickness, edge_color);
}
for x in (0..screen_width+tile_size).step_by(tile_size as usize) {
let x = x + offset_x % tile_size;
d.draw_line_ex(rvec2(x, 0.0), rvec2(x, screen_height), edge_thickness, edge_color);
}
if show_options {
d.draw_rectangle(0, 0, screen_width, screen_height, overlay_color);
if d.gui_window_box(rrect(10., 10., 300., 220.), Some(&CString::new("Options").unwrap())) {
show_options = false;
}
draw_label(&mut d, 15., 45., "Tab - Toggle options");
draw_label(&mut d, 15., 60., "Enter - Toggle simulation");
draw_label(&mut d, 15., 75., "Space - Step simulation");
draw_label(&mut d, 15., 90., "Left mouse click - Toggle cell");
draw_label(&mut d, 15., 105., "Middle mouse drag - Move camera");
draw_label(&mut d, 15., 132.5, "Simulation speed");
update_rate = d.gui_slider(rrect(150., 125., 80., 15.), Some(&CString::new("1 FPS").unwrap()), Some(&CString::new("30 FPS").unwrap()), update_rate as f32, 1., 30.) as i32;
draw_label(&mut d, 15., 160., "\"Dead\" color");
dead_color = d.gui_color_picker(rrect(20., 170., 40., 40.), dead_color);
draw_label(&mut d, 110., 160., "\"Alive\" color");
alive_color = d.gui_color_picker(rrect(115., 170., 40., 40.), alive_color);
draw_label(&mut d, 205., 160., "Edge color");
edge_color = d.gui_color_picker(rrect(210., 170., 40., 40.), edge_color);
}
}
}