zoukankan      html  css  js  c++  java
  • rust贪吃蛇

     

     

     

      1 use std::{ io::{Write, stdout}, time::Duration};
      2 
      3 use crossterm::{QueueableCommand, Result, cursor::*, event::{*, self}, execute, style::*, terminal::{*, self}};
      4 use rand::Rng;
      5 
      6 fn main() -> Result<()> {
      7     let (columns, rows) = terminal::size()?;
      8 
      9     execute!(stdout(), 
     10     EnterAlternateScreen,             // 进入替代屏幕
     11     // SetSize(MAP_ROW as u16 * 2, MAP_COL  as u16 *2  + 20),   // 设置窗口大小
     12     SetSize(WIDTH , HEIGHT),         // 设置窗口大小
     13     SetTitle("贪吃蛇"),             // 标题
     14     DisableLineWrap,                 // 禁止自动换行
     15     Hide,                            // 影藏光标
     16     )?;
     17     println!("按键操作: 'Esc/Q' - 退出, '↑/↓/←/→' - 移动");
     18     // println!("{}", "◇◆●○■□██◁▶▉▉██╳╳><╱╲██♪♩▓▉88");
     19     enable_raw_mode()?; // 进入原始模式, 禁止 CTL+C 等特殊键, println!不能用等等
     20     
     21     let mut game = SnakeGame::new();
     22     game.draw()?;
     23     loop {
     24         // poll在指定时间内尝试获取事件, 一旦获取事件就返回
     25         // 所以一旦持续操作能加快速度, 这个可以通过睡眠(间隔时间 - 消耗时间)解决
     26         // 这里不做处理
     27         if event::poll(Duration::from_millis(150))? {
     28             let event = event::read()?;
     29 
     30             if let Event::Key(KeyEvent{code, modifiers: _}) = event {
     31                 let running = if let GameStatus::Running = game.status { true } else { false };
     32                 match code {
     33                     KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => break, // quit
     34                     KeyCode::Char('r') | KeyCode::Char('R') =>  // restart
     35                     if let GameStatus::Over = game.status {
     36                         game = SnakeGame::new();
     37                     }, 
     38                     // 刚开始不能向左 face(0, 0), 所有Left多一个判断
     39                     KeyCode::Left if running && game.face.1 == 0  && game.face.0 != 0 => game.face = (0, -1),
     40                     KeyCode::Right if running && game.face.1 == 0 =>  game.face = (0, 1),
     41                     KeyCode::Up if running && game.face.0 == 0 =>  game.face = (-1, 0),
     42                     KeyCode::Down if running && game.face.0 == 0  =>  game.face = (1, 0),
     43                     _ => (),
     44                 }
     45             }
     46         }
     47 
     48         if let GameStatus::Running = game.status {
     49             if !game.moving() { // game over
     50                 game.over();
     51             } } 
     52         
     53         game.draw()?;
     54     }
     55     disable_raw_mode()?;
     56     // 还原窗口
     57     execute!(stdout(), LeaveAlternateScreen, DisableLineWrap, SetSize(columns, rows))
     58 }
     59 
     60 
     61 const MAP_ROW: usize = 23;
     62 const MAP_COL: usize = 23;
     63 
     64 const OFFSET_ROW: u16 = 4;
     65 const OFFSET_COL: u16 = OFFSET_ROW * 2;
     66 const WIDTH: u16 = (MAP_COL as u16 + OFFSET_COL) * 2;
     67 const HEIGHT: u16 = MAP_ROW as u16 + OFFSET_ROW * 2;
     68 
     69 
     70 struct SnakeGame {
     71     map: [[CellValue; MAP_COL]; MAP_ROW],
     72     snake: Vec<(usize, usize, SnakeValue)>,
     73     face: (i32, i32),
     74     count: usize,
     75     status: GameStatus,
     76 }
     77 
     78 enum GameStatus {
     79     Running,
     80     Over,
     81 }
     82 
     83 #[derive(Clone, Copy)]
     84 enum CellValue {
     85     Empty,  //
     86     Food,   // 食物
     87     Snake(SnakeValue),  // 
     88 }
     89 
     90 #[derive(Clone, Copy)]
     91 enum SnakeValue {
     92     Head,
     93     Trunk,
     94     Grow,
     95     Tail,
     96 }   
     97 
     98 impl SnakeGame {
     99 
    100     fn new() -> Self {
    101         let mut map = [[CellValue::Empty; MAP_COL]; MAP_ROW];
    102         // new food
    103         let mut rng = rand::thread_rng();
    104         let (food_row, food_col) = (rng.gen_range(0..MAP_ROW), rng.gen_range(6..MAP_COL));
    105         map[food_row][food_col] = CellValue::Food;
    106         let snake = Self::new_snake(food_row, food_col - 2);
    107         for &(r, c, sv) in snake.iter() {
    108             map[r][c] = CellValue::Snake(sv);
    109         }
    110 
    111         Self {
    112             map,
    113             snake,
    114             face: (0, 0),
    115             count: 0,
    116             status: GameStatus::Running
    117         }
    118     }
    119 
    120         // 初始化, 和食物保持同一行并在左边2格外
    121     fn new_snake(row: usize, col: usize) -> Vec<(usize, usize, SnakeValue)> {
    122         let mut rng = rand::thread_rng();
    123         let (row, col) = (row, rng.gen_range(3..col));
    124         let snake = vec![
    125             (row, col, SnakeValue::Head), 
    126             (row, col - 1, SnakeValue::Trunk), 
    127             (row, col - 2, SnakeValue::Tail), 
    128             ];
    129         snake
    130     }
    131 
    132     fn rnd_food(&mut self) {
    133         let mut rng = rand::thread_rng();
    134         loop {
    135             let (row, col) = (rng.gen_range(0..MAP_ROW), rng.gen_range(0..MAP_COL));
    136             if let CellValue::Empty = self.map[row][col] {
    137                 self.map[row][col] = CellValue::Food;
    138                 break;
    139             }
    140         }
    141     }
    142 
    143     fn over(&mut self){
    144         self.status = GameStatus::Over;
    145     }
    146     
    147     fn draw(&mut self) -> Result<()> {
    148         let mut out = stdout();
    149         // 绘制地图
    150         let mut background = Color::Cyan;
    151         execute!(out, MoveTo(OFFSET_COL, OFFSET_ROW))?;
    152         for rows in &self.map {
    153             for value in rows {
    154                 background = if let Color::Cyan = background { Color::DarkCyan } else { Color::Cyan };
    155                 let (cell, color) = match value {
    156                     CellValue::Empty => ("  ", Color::DarkRed),
    157                     CellValue::Food =>  ("", Color::DarkRed) ,
    158                     CellValue::Snake(SnakeValue::Head) =>  ("", Color::DarkMagenta) ,
    159                     CellValue::Snake(SnakeValue::Trunk) => ("", Color::DarkMagenta) ,
    160                     CellValue::Snake(SnakeValue::Grow) =>  ("", Color::DarkMagenta) ,
    161                     CellValue::Snake(SnakeValue::Tail) =>  ("", Color::DarkMagenta) ,
    162                 };
    163                 execute!(out,
    164                     SetBackgroundColor(background),
    165                     SetForegroundColor(color),
    166                     // Print text
    167                     Print(cell.to_string()),
    168                     // Reset to default colors
    169                     ResetColor
    170                 )?;
    171             }
    172             execute!(out, MoveToNextLine(1), MoveToColumn(OFFSET_COL + 1))?;
    173         }
    174         // 记录
    175         out .queue(MoveToNextLine(1))?
    176             .queue(MoveToColumn(OFFSET_COL + 1))?
    177             .queue(PrintStyledContent(format!("eat: {:4}", self.count).red()))?
    178             ;
    179         // game over
    180         execute!(out, MoveTo(0, 1))?;
    181         let info = if let GameStatus::Over = self.status {
    182             execute!( out,
    183                 SetBackgroundColor(Color::DarkRed),
    184                 SetForegroundColor(Color::White))?;
    185             "Game Over! (Press key 'R' Restart)"
    186             
    187         }else {
    188             "  "
    189         };
    190         execute!( out,
    191             // Print text
    192             Print(format!("{:^width$}", "  ", width=WIDTH as usize)),  
    193             MoveToNextLine(1),
    194             Print(format!("{:^width$}", info, width=WIDTH as usize)), 
    195             MoveToNextLine(1),
    196             Print(format!("{:^width$}", "  ", width=WIDTH as usize)),
    197             // Reset to default colors
    198             ResetColor
    199         )?;
    200         out.flush()
    201     }
    202 
    203 
    204     fn moving(&mut self) -> bool {
    205         if self.face.0 == 0 && self.face.1 == 0 { return true; }    // 静止不动
    206         // 计算蛇头下一步
    207         let (forward_row, forward_col) = (self.snake[0].0 as i32 + self.face.0, self.snake[0].1 as i32 + self.face.1);
    208         // 撞墙
    209         if forward_row < 0 || forward_col < 0 { return false }
    210         let mut forward = (forward_row as usize, forward_col as usize, SnakeValue::Head);
    211         // 还是撞墙, 类型一致才能比较
    212         if forward.0 == MAP_ROW || forward.1 == MAP_COL {  return false }
    213         // 判定下一步
    214         match self.map[forward.0][forward.1] {
    215             CellValue::Empty => { // 移动, 所有格子向前移一步(交换)
    216                 for ele in self.snake.iter_mut() {
    217                     let temp = *ele;
    218                     let mut v = temp.2;
    219                     if let SnakeValue::Trunk = v {
    220                         if let SnakeValue::Grow = forward.2 {
    221                             v  = SnakeValue::Grow;
    222                         }
    223                     }else if let SnakeValue::Grow = v { 
    224                         v = SnakeValue::Trunk;
    225                     }
    226                     *ele = (forward.0, forward.1, v);
    227                      // 设置不能归纳为一个方法, for循环内 &mut self 不能同时存在多个
    228                     self.map[forward.0][forward.1] = CellValue::Snake(v);
    229                     forward = temp;
    230                 }
    231                 // 最后一格清空
    232                 self.map[forward.0][forward.1] = CellValue::Empty;
    233                 true
    234             },
    235             CellValue::Food =>  { // 食物, 头向前移, 原头增加一格
    236                 self.count += 1;
    237                 self.snake.insert(0, (forward.0, forward.1, SnakeValue::Head));
    238                 self.snake[1].2 = SnakeValue::Grow;
    239                 self.map[forward.0][forward.1] = CellValue::Snake(forward.2);
    240                 self.map[self.snake[1].0][self.snake[1].1] = CellValue::Snake(self.snake[1].2);
    241                 self.rnd_food();
    242                 true
    243             } ,
    244             _=> false   // 撞上蛇躯
    245         }
    246     }
    247 }

     

     

     

    Cargo.toml

    rand = "0.8.4"
    crossterm = "0.22.1"

     

  • 相关阅读:
    我回来了
    wget 官方jdk
    linux rpm命令安装卸载 初步使用
    关于一些对location认识的误区(转)
    直接插入排序
    冒泡排序
    Wireshark下TCP三次握手四次挥手
    linux内存使用率详解
    Linux下硬盘使用率详解及shell脚本实现
    Linux下CPU使用率详解
  • 原文地址:https://www.cnblogs.com/harvard/p/15544703.html
Copyright © 2011-2022 走看看