Initial commit

This commit is contained in:
tilman
2019-09-11 17:15:46 +02:00
commit 5039891096
4 changed files with 1277 additions and 0 deletions

262
src/main.rs Normal file
View File

@@ -0,0 +1,262 @@
extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;
extern crate rand;
use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };
use rand::Rng;
pub struct App<T: Game> {
gl: GlGraphics, // OpenGL drawing backend.
field: Field,
game: T,
tile_size: i32,
shift_float: f64
}
type Color = [f32; 4];
pub struct Field {
default_color: Color,
field: Vec<Vec<Color>>,
width: usize,
height: usize,
shift: usize
}
pub trait Game {
fn init(&mut self, field: &mut Field);
fn step(&mut self, field: &mut Field);
}
impl Field {
fn new(width: usize, height: usize) -> Field {
let default_color: Color = [0.0, 0.0, 0.0, 0.0];
let mut data = Vec::with_capacity(width);
for _ in 0..width {
let mut row = Vec::with_capacity(height);
for _ in 0..height {
row.push(default_color)
}
data.push(row);
}
Field {
default_color,
field: data,
width,
height,
shift: 0
}
}
fn assert_range(&self, x: usize, y: usize) {
assert!(x < self.width, "X is not in range!");
assert!(y < self.height, "Y is not in range!");
}
fn shift(&mut self) {
let height = self.height;
let default_color = self.default_color;
let mut new_shift = (self.shift as isize + 1) % height as isize;
if new_shift < 0 {
new_shift = height as isize + new_shift;
}
self.shift = new_shift as usize;
for x in 0..self.width {
self.set_data(x as i64, height - 1, default_color);
}
}
fn get_shifted(&self, y: usize) -> usize {
(y + self.shift) % self.height
}
fn get_cylindric(&self, x: i64) -> usize {
//match x {
// 0 ... (self.width - 1) => x,
// (-self.width) ... (-1) => self.width - x,
// _ => panic!("Value for x out of bounds!")
//}
if x >= 0 && x < self.width as i64 {
x as usize
} else if x < 0 && x >= -(self.width as i64) {
(self.width as i64 + x) as usize
} else {
panic!("Value of x ({}) out of range!", x)
}
}
fn set_data(&mut self, x: i64, y: usize, data: Color) {
let y_shifted = self.get_shifted(y);
let x_cylindric = self.get_cylindric(x);
self.assert_range(x_cylindric, y_shifted);
self.field[x_cylindric][y_shifted] = data
}
fn get_data(&self, x: i64, y: usize) -> Color {
let y_shifted = self.get_shifted(y);
let x_cylindric = self.get_cylindric(x);
self.assert_range(x_cylindric, y_shifted);
self.field[x_cylindric][y_shifted]
}
}
impl<T: Game> App<T> {
fn render(&mut self, args: &RenderArgs) {
use graphics::*;
let square = rectangle::square(0.0, 0.0, self.tile_size as f64);
let tile_size = self.tile_size;
let field = &self.field;
self.gl.draw(args.viewport(), |c, gl| {
// Clear the screen.
clear([0.0, 0.0, 0.0, 1.0], gl);
for y in 0..field.height {
for x in 0..field.width {
let transform = c.transform
.trans(x as f64 * tile_size as f64, y as f64 * tile_size as f64);
rectangle(field.get_data(x as i64, y), square, transform, gl);
}
}
});
}
fn update(&mut self, args: &UpdateArgs) {
self.shift_float += args.dt * 4.0;
if self.shift_float >= 1.0 {
let shift = self.shift_float as usize;
for i in 0..shift {
self.game.step(&mut self.field);
}
self.shift_float = self.shift_float % 1.0;
}
}
}
pub struct WolframsUniverse {
init_prob: f64,
alive_rules: [u8; 2],
last_row: usize
}
impl WolframsUniverse {
fn new() -> WolframsUniverse {
WolframsUniverse {
init_prob: 0.5,
alive_rules: [2, 4],
last_row: 0
}
}
}
impl Game for WolframsUniverse {
fn init(&mut self, field: &mut Field) {
for x in 0..field.width {
if rand::thread_rng().gen_bool(0.5) {
field.set_data(x as i64, 0, [1.0, 1.0, 1.0, 1.0]);
}
}
}
fn step(&mut self, field: &mut Field) {
if self.last_row == field.height - 1 {
field.shift();
} else {
self.last_row += 1;
}
/*for x in 0..field.width as i64 {
let mut count: u8 = 0;
for parent_x in (x as i64 - 2)..(x as i64 + 2 + 1) {
if field.get_data(parent_x % field.width as i64, self.last_row - 1) != field.default_color {
count += 1;
}
}
for alive_rule in self.alive_rules.iter() {
if count == *alive_rule {
field.set_data(x, self.last_row, [1.0, 1.0, 1.0, 1.0]);
break;
}
}
}*/
let parent_y = self.last_row - 1;
let mut count = 0;
for parent_x in -2..3 {
if field.get_data(parent_x, parent_y) != field.default_color {
count += 1
}
}
for x in 0..field.width as i64 {
for alive_rule in self.alive_rules.iter() {
if count == *alive_rule {
field.set_data(x, self.last_row, [1.0, 1.0, 1.0, 1.0]);
break;
}
}
if count > 0 && field.get_data(x - 2, parent_y) != field.default_color {
count -= 1;
}
if field.get_data((x + 3) % field.width as i64, parent_y) != field.default_color {
count += 1;
}
}
}
}
fn main() {
// Change this to OpenGL::V2_1 if not working.
let opengl = OpenGL::V3_2;
let width_pixel = 1600;
let height_pixel = 1200;
let tile_size = 10;
// Create an Glutin window.
let mut window: Window = WindowSettings::new(
"Wolfram's Universe",
[width_pixel, height_pixel]
)
.opengl(opengl)
.exit_on_esc(true)
.build()
.unwrap();
// Create a new game and run it.
let mut app = App {
gl: GlGraphics::new(opengl),
field: Field::new((width_pixel / tile_size) as usize, (height_pixel / tile_size) as usize),
game: WolframsUniverse::new(),
tile_size: tile_size as i32,
shift_float: 0.0
};
app.game.init(&mut app.field);
// Debug
println!("{} {}", 4.7654321 % 1.0, 4.7654321 as usize);
//app.field.set_data(2, 0, [1.0, 1.0, 0.0, 1.0]);
//println!("Data at (2,0) {:?}", app.field.get_data(2, 0));
println!("Shift: {}", app.field.shift);
// Event handling and main thread
let mut events = Events::new(EventSettings::new());
while let Some(e) = events.next(&mut window) {
if let Some(r) = e.render_args() {
app.render(&r);
}
if let Some(u) = e.update_args() {
app.update(&u);
}
}
}