runningwater 2 лет назад
Родитель
Сommit
4464c9ddd8
2 измененных файлов с 211 добавлено и 42 удалено
  1. 1 0
      .idea/tetris.iml
  2. 210 42
      src/main.rs

+ 1 - 0
.idea/tetris.iml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module type="JAVA_MODULE" version="4">
+  <component name="Go" enabled="true" />
   <component name="NewModuleRootManager" inherit-compiler-output="true">
     <exclude-output />
     <content url="file://$MODULE_DIR$">

+ 210 - 42
src/main.rs

@@ -1,25 +1,29 @@
 extern crate rand;
 extern crate sdl2;
 
-use std::{io, thread};
 use std::fs::File;
+use std::io;
 use std::io::{Read, Write};
+use std::thread::sleep;
 use std::time::{Duration, SystemTime};
 
 use sdl2::event::Event;
 use sdl2::keyboard::Keycode;
 use sdl2::pixels::Color;
-use sdl2::rect::Rect;
 use sdl2::render::{Texture, TextureCreator, WindowCanvas};
 use sdl2::video::WindowContext;
 
 // Crate a texture with 32x32 size
 const TEXTURE_SIZE: u32 = 32;
 const SCORE_FILE_NAME: &str = "scores.txt";
+const LEVEL_TIMES: [u32; 10] = [1000, 850, 700, 600, 500, 400, 300, 250, 221, 190];
+const LEVEL_LINES: [u32; 10] = [20, 40, 60, 80, 100, 120, 140, 160, 180, 200]; // 需要消除多少行才能升级
+const NB_HIGHSCORES: usize = 5;
 type Piece = Vec<Vec<u8>>;
+type States = Vec<Piece>;
 
 struct Tetrimino {
-    states: Vec<Piece>,
+    states: States,
     x: isize,
     y: usize,
     current_state: u8,
@@ -325,8 +329,9 @@ impl Tetris {
         }
     }
     /// Remove lines when they're full
-    fn check_line(&mut self) {
+    fn check_lines(&mut self) {
         let mut y = 0;
+        let mut score_add = 0u32;
 
         while y < self.game_map.len() {
             let mut complete = true;
@@ -339,17 +344,25 @@ impl Tetris {
             }
 
             if complete == true {
+                score_add += self.current_level;
                 self.game_map.remove(y);
                 y -= 1;
                 // increase the number of self.lines
             }
             y += 1;
         }
+        if self.game_map.len() == 0 {
+            // A "tetris"!
+            score_add += 1000;
+        }
+        self.update_score(score_add);
         while self.game_map.len() < 16 {
+            self.increase_line();
             self.game_map.insert(0, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
         }
     }
     fn make_permanent(&mut self) {
+        let mut to_add = 0u32;
         if let Some(ref mut piece) = self.current_piece {
             let mut shift_y = 0;
 
@@ -373,10 +386,21 @@ impl Tetris {
 
                 shift_y += 1;
             }
+            to_add += self.current_level;
         }
-        self.check_line();
+        self.update_score(to_add);
+        self.check_lines();
         self.current_piece = None;
     }
+    fn update_score(&mut self, to_add: u32) {
+        self.score += to_add;
+    }
+    fn increase_line(&mut self) {
+        self.nb_lines += 1;
+        if self.nb_lines > LEVEL_LINES[self.current_level as usize - 1] {
+            self.current_level += 1;
+        }
+    }
 }
 
 #[derive(Clone, Copy)]
@@ -509,55 +533,199 @@ pub fn main() {
     )
     .expect("Failed to create a texture");
 
-    let timer = SystemTime::now();
-
     let mut event_pump = sdl_context
         .event_pump()
         .expect("Failed to get SDL event pump");
-    'running: loop {
+
+    let mut timer = SystemTime::now();
+    let mut tetris = Tetris::new();
+
+    loop {
+        if match timer.elapsed() {
+            Ok(elapsed) => elapsed.as_secs() >= 1,
+            Err(_) => false,
+        } {
+            let mut make_permanent = false;
+            if let Some(ref mut piece) = tetris.current_piece {
+                let x = piece.x;
+                let y = piece.y + 1;
+                make_permanent = !piece.change_position(&tetris.game_map, x, y);
+            }
+            if make_permanent {
+                tetris.make_permanent();
+            }
+
+            timer = SystemTime::now();
+        }
+
+        // We need to draw the tetris "grid" in here.
+
+        if tetris.current_piece.is_none() {
+            let current_piece = tetris.create_new_tetrimino();
+            if !current_piece.test_current_position(&tetris.game_map) {
+                print_game_information(&tetris);
+                break;
+            }
+            tetris.current_piece = Some(current_piece);
+        }
+        let mut quit = false;
+        if !handle_events(&mut tetris, &mut quit, &mut timer, &mut event_pump) {
+            if let Some(ref mut piece) = tetris.current_piece {
+                // We need to draw our current tetrimino in here.
+                todo!()
+            }
+        }
+        if quit {
+            print_game_information(&tetris);
+            break;
+        }
+
+        if is_time_over(&tetris, &timer) {
+            let mut make_permanent = false;
+            if let Some(ref mut piece) = tetris.current_piece {
+                let x = piece.x;
+                let y = piece.y + 1;
+                make_permanent = !piece.change_position(&tetris.game_map, x, y);
+            }
+            if make_permanent {
+                tetris.make_permanent();
+            }
+            timer = SystemTime::now();
+        }
+        // We need to draw the game map in here.
+
+        sleep(Duration::new(0, 1_000_000_000u32 / 60));
+    }
+}
+fn is_time_over(tetris: &Tetris, timer: &SystemTime) -> bool {
+    match timer.elapsed() {
+        Ok(elapsed) => {
+            let millis = elapsed.as_secs() as u32 * 1000 + elapsed.subsec_nanos() / 1_000_000;
+            millis > LEVEL_TIMES[tetris.current_level as usize - 1]
+        }
+        Err(_) => false,
+    }
+}
+fn handle_events(
+    tetris: &mut Tetris,
+    quit: &mut bool,
+    timer: &mut SystemTime,
+    event_pump: &mut sdl2::EventPump,
+) -> bool {
+    // The current tetrimino is still falling.
+    // If not, then it becomes true, the tetrimino is then put into
+    // the game map and we generate a new one.
+    let mut make_permanent = false;
+    if let Some(ref mut piece) = tetris.current_piece {
+        let mut tmp_x = piece.x;
+        let mut tmp_y = piece.y;
+
         for event in event_pump.poll_iter() {
             match event {
-                // If we receive a 'quit' event or if the user press the
-                // 'ESC' key, we quit.
                 Event::Quit { .. }
                 | Event::KeyDown {
                     keycode: Some(Keycode::Escape),
                     ..
-                } => break 'running,
+                } => {
+                    *quit = true;
+                    break;
+                }
+                Event::KeyDown {
+                    keycode: Some(Keycode::Down),
+                    ..
+                } => {
+                    *timer = SystemTime::now();
+                    tmp_y += 1;
+                }
+                Event::KeyDown {
+                    keycode: Some(Keycode::Right),
+                    ..
+                } => {
+                    tmp_x += 1;
+                }
+                Event::KeyDown {
+                    keycode: Some(Keycode::Left),
+                    ..
+                } => {
+                    tmp_x -= 1;
+                }
+                Event::KeyDown {
+                    keycode: Some(Keycode::Up),
+                    ..
+                } => {
+                    piece.rotate(&tetris.game_map);
+                }
+                Event::KeyDown {
+                    keycode: Some(Keycode::Space),
+                    ..
+                } => {
+                    let x = piece.x;
+                    let mut y = piece.y;
+                    while piece.change_position(&tetris.game_map, x, y + 1) == true {
+                        y += 1;
+                    }
+                    make_permanent = true;
+                }
                 _ => {}
             }
+        } // end for
+        if !make_permanent {
+            if piece.change_position(&tetris.game_map, tmp_x, tmp_y) == false && tmp_y != piece.y {
+                make_permanent = true;
+            }
         }
-        // The rest of the game loop goes here...
-        canvas.set_draw_color(Color::RGB(255, 0, 0));
-        // We draw it
-        canvas.clear();
-
-        // The rectangle switch happens here:
-        let display_green = match timer.elapsed() {
-            Ok(elapsed) => elapsed.as_secs() % 2 == 0,
-            Err(_) => {
-                // In case of error, we do nothing
-                true
+    }
+    if make_permanent {
+        tetris.make_permanent();
+        *timer = SystemTime::now();
+    }
+    make_permanent
+}
+fn update_vec(v: &mut Vec<u32>, value: u32) -> bool {
+    if v.len() < NB_HIGHSCORES {
+        v.push(value);
+        v.sort();
+        true
+    } else {
+        for entry in v.iter_mut() {
+            if value > *entry {
+                *entry = value;
+                return true;
             }
-        };
-        let square_texture = if display_green {
-            &green_square
-        } else {
-            &blue_square
-        };
-        // Copy our texture into the window.
-        canvas
-            .copy(
-                &square_texture,
-                None,
-                Rect::new(0, 0, TEXTURE_SIZE, TEXTURE_SIZE),
-            )
-            .expect("Couldn't copy texture in to window");
-        // We update window's display
-        canvas.present();
-
-        // We sleep enough to get ~60 fps. If we don't call this,
-        // the program will take 100% of a CPU time.
-        thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
+        }
+        false
+    }
+}
+fn print_game_information(tetris: &Tetris) {
+    let mut new_highest_highscore = true;
+    let mut new_highest_lines_sent = true;
+    if let Some((mut highscores, mut lines_sent)) = load_highscores_and_lines() {
+        new_highest_highscore = update_vec(&mut highscores, tetris.score);
+        new_highest_lines_sent = update_vec(&mut lines_sent, tetris.nb_lines);
+        if new_highest_lines_sent || new_highest_highscore {
+            save_highscore_and_lines(&highscores, &lines_sent);
+        }
+    } else {
+        save_highscore_and_lines(&[tetris.score], &[tetris.nb_lines]);
     }
+    println!("Game over...");
+    println!(
+        "Score:                     {}{}",
+        tetris.score,
+        if new_highest_highscore {
+            " [NEW HIGHSCORE]"
+        } else {
+            ""
+        }
+    );
+    println!(
+        "Number of lines: {}{}",
+        tetris.nb_lines,
+        if new_highest_lines_sent {
+            " [NEW HIGHSCORE]"
+        } else {
+            ""
+        }
+    );
+    print!("Current level:               {}", tetris.current_level);
 }