Initial commit
This commit is contained in:
262
src/main.rs
Normal file
262
src/main.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user