1 extern crate sdl2;
2
3 use sdl2::rect::{Point, Rect};
4 use sdl2::pixels::Color;
5 use sdl2::event::Event;
6 use sdl2::mouse::MouseButton;
7 use sdl2::keyboard::Keycode;
8 use sdl2::video::{Window, WindowContext};
9 use sdl2::render::{Canvas, Texture, TextureCreator};
10 use crate::game_of_life::{SQUARE_SIZE, PLAYGROUND_WIDTH, PLAYGROUND_HEIGHT};
11
12 mod game_of_life {
13 pub const SQUARE_SIZE: u32 = 16;
14 pub const PLAYGROUND_WIDTH: u32 = 49;
15 pub const PLAYGROUND_HEIGHT: u32 = 40;
16
17 #[derive(Copy, Clone)]
18 pub enum State {
19 Paused,
20 Playing,
21 }
22
23 pub struct GameOfLife {
24 playground: [bool; (PLAYGROUND_WIDTH*PLAYGROUND_HEIGHT) as usize],
25 state: State,
26 }
27
28 impl GameOfLife {
new() -> GameOfLife29 pub fn new() -> GameOfLife {
30 let mut playground = [false; (PLAYGROUND_WIDTH * PLAYGROUND_HEIGHT) as usize];
31
32 // let's make a nice default pattern !
33 for i in 1..(PLAYGROUND_HEIGHT-1) {
34 playground[(1 + i* PLAYGROUND_WIDTH) as usize] = true;
35 playground[((PLAYGROUND_WIDTH-2) + i* PLAYGROUND_WIDTH) as usize] = true;
36 }
37 for j in 2..(PLAYGROUND_WIDTH-2) {
38 playground[(PLAYGROUND_WIDTH + j) as usize] = true;
39 playground[((PLAYGROUND_HEIGHT-2)*PLAYGROUND_WIDTH + j) as usize] = true;
40 }
41
42 GameOfLife {
43 playground: playground,
44 state: State::Paused,
45 }
46 }
47
get(&self, x: i32, y: i32) -> Option<bool>48 pub fn get(&self, x: i32, y: i32) -> Option<bool> {
49 if x >= 0 && y >= 0 &&
50 (x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT {
51 Some(self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize])
52 } else {
53 None
54 }
55 }
56
get_mut(&mut self, x: i32, y: i32) -> Option<&mut bool>57 pub fn get_mut(&mut self, x: i32, y: i32) -> Option<&mut bool> {
58 if x >= 0 && y >= 0 &&
59 (x as u32) < PLAYGROUND_WIDTH && (y as u32) < PLAYGROUND_HEIGHT {
60 Some(&mut self.playground[(x as u32 + (y as u32)* PLAYGROUND_WIDTH) as usize])
61 } else {
62 None
63 }
64 }
65
toggle_state(&mut self)66 pub fn toggle_state(&mut self) {
67 self.state = match self.state {
68 State::Paused => State::Playing,
69 State::Playing => State::Paused,
70 }
71 }
72
state(&self) -> State73 pub fn state(&self) -> State {
74 self.state
75 }
76
update(&mut self)77 pub fn update(&mut self) {
78 let mut new_playground = self.playground;
79 for (u, square) in new_playground.iter_mut().enumerate() {
80 let u = u as u32;
81 let x = u % PLAYGROUND_WIDTH;
82 let y = u / PLAYGROUND_WIDTH;
83 let mut count : u32 = 0;
84 for i in -1..2 {
85 for j in -1..2 {
86 if !(i == 0 && j == 0) {
87 let peek_x : i32 = (x as i32) + i;
88 let peek_y : i32 = (y as i32) + j;
89 if let Some(true) = self.get(peek_x, peek_y) {
90 count += 1;
91 }
92 }
93 }
94 }
95 if count > 3 || count < 2 {
96 *square = false;
97 } else if count == 3 {
98 *square = true;
99 } else if count == 2 {
100 *square = *square;
101 }
102 }
103 self.playground = new_playground;
104 }
105 }
106
107
108
109 impl<'a> IntoIterator for &'a GameOfLife {
110 type Item = &'a bool;
111 type IntoIter = ::std::slice::Iter<'a, bool>;
into_iter(self) -> ::std::slice::Iter<'a, bool>112 fn into_iter(self) -> ::std::slice::Iter<'a, bool> {
113 self.playground.iter()
114 }
115 }
116 }
117
dummy_texture<'a>(canvas: &mut Canvas<Window>, texture_creator: &'a TextureCreator<WindowContext>) -> Result<(Texture<'a>, Texture<'a>), String>118 fn dummy_texture<'a>(canvas: &mut Canvas<Window>, texture_creator: &'a TextureCreator<WindowContext>) -> Result<(Texture<'a>, Texture<'a>), String> {
119 enum TextureColor {
120 Yellow,
121 White,
122 };
123 let mut square_texture1 = texture_creator.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).map_err(|e| e.to_string())?;
124 let mut square_texture2 = texture_creator.create_texture_target(None, SQUARE_SIZE, SQUARE_SIZE).map_err(|e| e.to_string())?;
125 // let's change the textures we just created
126 {
127 let textures = vec![
128 (&mut square_texture1, TextureColor::Yellow),
129 (&mut square_texture2, TextureColor::White)
130 ];
131 canvas.with_multiple_texture_canvas(textures.iter(), |texture_canvas, user_context| {
132 texture_canvas.set_draw_color(Color::RGB(0, 0, 0));
133 texture_canvas.clear();
134 match *user_context {
135 TextureColor::Yellow => {
136 for i in 0..SQUARE_SIZE {
137 for j in 0..SQUARE_SIZE {
138 if (i+j) % 4 == 0 {
139 texture_canvas.set_draw_color(Color::RGB(255, 255, 0));
140 texture_canvas.draw_point(Point::new(i as i32, j as i32))
141 .expect("could not draw point");
142 }
143 if (i+j*2) % 9 == 0 {
144 texture_canvas.set_draw_color(Color::RGB(200, 200, 0));
145 texture_canvas.draw_point(Point::new(i as i32, j as i32))
146 .expect("could not draw point");
147 }
148 }
149 }
150 },
151 TextureColor::White => {
152 for i in 0..SQUARE_SIZE {
153 for j in 0..SQUARE_SIZE {
154 // drawing pixel by pixel isn't very effective, but we only do it once and store
155 // the texture afterwards so it's still alright!
156 if (i+j) % 7 == 0 {
157 // this doesn't mean anything, there was some trial and error to find
158 // something that wasn't too ugly
159 texture_canvas.set_draw_color(Color::RGB(192, 192, 192));
160 texture_canvas.draw_point(Point::new(i as i32, j as i32))
161 .expect("could not draw point");
162 }
163 if (i+j*2) % 5 == 0 {
164 texture_canvas.set_draw_color(Color::RGB(64, 64, 64));
165 texture_canvas.draw_point(Point::new(i as i32, j as i32))
166 .expect("could not draw point");
167 }
168 }
169 }
170 }
171 };
172 for i in 0..SQUARE_SIZE {
173 for j in 0..SQUARE_SIZE {
174 // drawing pixel by pixel isn't very effective, but we only do it once and store
175 // the texture afterwards so it's still alright!
176 if (i+j) % 7 == 0 {
177 // this doesn't mean anything, there was some trial and serror to find
178 // something that wasn't too ugly
179 texture_canvas.set_draw_color(Color::RGB(192, 192, 192));
180 texture_canvas.draw_point(Point::new(i as i32, j as i32))
181 .expect("could not draw point");
182 }
183 if (i+j*2) % 5 == 0 {
184 texture_canvas.set_draw_color(Color::RGB(64, 64, 64));
185 texture_canvas.draw_point(Point::new(i as i32, j as i32))
186 .expect("could not draw point");
187 }
188 }
189 }
190 }).map_err(|e| e.to_string())?;
191 }
192 Ok((square_texture1, square_texture2))
193 }
194
main() -> Result<(), String>195 pub fn main() -> Result<(), String> {
196 let sdl_context = sdl2::init()?;
197 let video_subsystem = sdl_context.video()?;
198
199 // the window is the representation of a window in your operating system,
200 // however you can only manipulate properties of that window, like its size, whether it's
201 // fullscreen, ... but you cannot change its content without using a Canvas or using the
202 // `surface()` method.
203 let window = video_subsystem
204 .window("rust-sdl2 demo: Game of Life",
205 SQUARE_SIZE*PLAYGROUND_WIDTH,
206 SQUARE_SIZE*PLAYGROUND_HEIGHT)
207 .position_centered()
208 .build()
209 .map_err(|e| e.to_string())?;
210
211 // the canvas allows us to both manipulate the property of the window and to change its content
212 // via hardware or software rendering. See CanvasBuilder for more info.
213 let mut canvas = window.into_canvas()
214 .target_texture()
215 .present_vsync()
216 .build()
217 .map_err(|e| e.to_string())?;
218
219 println!("Using SDL_Renderer \"{}\"", canvas.info().name);
220 canvas.set_draw_color(Color::RGB(0, 0, 0));
221 // clears the canvas with the color we set in `set_draw_color`.
222 canvas.clear();
223 // However the canvas has not been updated to the window yet, everything has been processed to
224 // an internal buffer, but if we want our buffer to be displayed on the window, we need to call
225 // `present`. We need to call this everytime we want to render a new frame on the window.
226 canvas.present();
227
228 // this struct manages textures. For lifetime reasons, the canvas cannot directly create
229 // textures, you have to create a `TextureCreator` instead.
230 let texture_creator : TextureCreator<_> = canvas.texture_creator();
231
232 // Create a "target" texture so that we can use our Renderer with it later
233 let (square_texture1, square_texture2) = dummy_texture(&mut canvas, &texture_creator)?;
234 let mut game = game_of_life::GameOfLife::new();
235
236 let mut event_pump = sdl_context.event_pump()?;
237 let mut frame : u32 = 0;
238 'running: loop {
239 // get the inputs here
240 for event in event_pump.poll_iter() {
241 match event {
242 Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
243 break 'running
244 },
245 Event::KeyDown { keycode: Some(Keycode::Space), repeat: false, .. } => {
246 game.toggle_state();
247 },
248 Event::MouseButtonDown { x, y, mouse_btn: MouseButton::Left, .. } => {
249 let x = (x as u32) / SQUARE_SIZE;
250 let y = (y as u32) / SQUARE_SIZE;
251 match game.get_mut(x as i32, y as i32) {
252 Some(square) => {*square = !(*square);},
253 None => unreachable!(),
254 };
255 },
256 _ => {}
257 }
258 }
259
260 // update the game loop here
261 if frame >= 30 {
262 game.update();
263 frame = 0;
264 }
265
266 canvas.set_draw_color(Color::RGB(0, 0, 0));
267 canvas.clear();
268 for (i, unit) in (&game).into_iter().enumerate() {
269 let i = i as u32;
270 let square_texture = if frame >= 15 {
271 &square_texture1
272 } else {
273 &square_texture2
274 };
275 if *unit {
276 canvas.copy(square_texture,
277 None,
278 Rect::new(((i % PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32,
279 ((i / PLAYGROUND_WIDTH) * SQUARE_SIZE) as i32,
280 SQUARE_SIZE,
281 SQUARE_SIZE))?;
282 }
283 }
284 canvas.present();
285 if let game_of_life::State::Playing = game.state() {
286 frame += 1;
287 };
288 }
289
290 Ok(())
291 }
292