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