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