news 2026/8/19 12:48:40

Rust 实现贪吃蛇小游戏源码分享

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rust 实现贪吃蛇小游戏源码分享

Rust 实现贪吃蛇小游戏源码分享

  • 一、效果展示
  • 二、源码分享
    • 1、main.rs
    • 2、Cargo.toml
  • 三、rand 包详解
    • 1、 添加依赖
    • 2、 核心概念
      • 2.1、 随机数生成器 (RNG)
      • 2.2、 分布 (Distributions)
    • 3、 基本用法
      • 3.1、 生成随机数
      • 3.2 、随机布尔值与选择
    • 4、在贪吃蛇游戏中的应用
    • 5、 高级特性与性能
      • 5.1 、种子与可重现性
      • 5.2 、性能优化
    • 6、常见问题与陷阱
    • 7、总结

一、效果展示


二、源码分享

1、main.rs

usecrossterm::{cursor::{Hide,MoveTo,Show},event::{poll,read,Event,KeyCode,KeyEvent,KeyModifiers},execute,style::{Color,Print,ResetColor,SetBackgroundColor},terminal::{disable_raw_mode,enable_raw_mode,size,Clear,ClearType,EnterAlternateScreen,LeaveAlternateScreen,},};userand::Rng;usestd::{collections::VecDeque,io::{stdout,Result,Write},time::Duration,};/// 蛇的移动方向#[derive(Clone, Copy, PartialEq)]enumDirection{Up,Down,Left,Right,}/// 游戏状态structGame{snake:VecDeque<(usize,usize)>,direction:Direction,next_direction:Direction,food:(usize,usize),score:u32,width:usize,height:usize,game_over:bool,}implGame{fnnew(width:usize,height:usize)->Self{// 蛇从中间开始,初始长度 3letmid_x=width/2;letmid_y=height/2;letmutsnake=VecDeque::new();snake.push_back((mid_x,mid_y));snake.push_back((mid_x-1,mid_y));snake.push_back((mid_x-2,mid_y));letmutgame=Game{snake,direction:Direction::Right,next_direction:Direction::Right,food:(0,0),score:0,width,height,game_over:false,};game.spawn_food();game}/// 在空白位置随机生成食物fnspawn_food(&mutself){letmutrng=rand::thread_rng();loop{letx=rng.gen_range(0..self.width);lety=rng.gen_range(0..self.height);if!self.snake.contains(&(x,y)){self.food=(x,y);break;}}}/// 处理输入方向,禁止 180° 掉头fnset_direction(&mutself,dir:Direction){match(self.direction,dir){(Direction::Up,Direction::Down)|(Direction::Down,Direction::Up)|(Direction::Left,Direction::Right)|(Direction::Right,Direction::Left)=>{}// 忽略反向_=>self.next_direction=dir,}}/// 更新蛇的位置fnupdate(&mutself){ifself.game_over{return;}self.direction=self.next_direction;let(head_x,head_y)=self.snake.front().unwrap();let(nx,ny)=matchself.direction{Direction::Up=>(*head_x,head_y.wrapping_sub(1)),Direction::Down=>(*head_x,head_y+1),Direction::Left=>(head_x.wrapping_sub(1),*head_y),Direction::Right=>(*head_x+1,*head_y),};// 撞墙检测ifnx>=self.width||ny>=self.height{self.game_over=true;return;}// 撞自己检测ifself.snake.contains(&(nx,ny)){self.game_over=true;return;}// 移动蛇:头部插入新位置self.snake.push_front((nx,ny));// 吃到食物加分并生成新食物,否则去掉尾部if(nx,ny)==self.food{self.score+=10;self.spawn_food();}else{self.snake.pop_back();}}/// 渲染游戏画面fnrender(&self)->Result<()>{letmutstdout=stdout();execute!(stdout,Clear(ClearType::All))?;// 绘制上边框forxin0..self.width{execute!(stdout,MoveTo(xasu16,0),Print("─"))?;}// 绘制下边框forxin0..self.width{execute!(stdout,MoveTo(xasu16,(self.height+1)asu16),Print("─"))?;}// 绘制左右边框foryin0..=self.height{execute!(stdout,MoveTo(0,yasu16),Print("│"))?;execute!(stdout,MoveTo((self.width+1)asu16,yasu16),Print("│"))?;}// 绘制食物execute!(stdout,SetBackgroundColor(Color::Red),MoveTo(self.food.0asu16+1,self.food.1asu16+1),Print("●"),ResetColor)?;// 绘制蛇身for(i,(x,y))inself.snake.iter().enumerate(){execute!(stdout,MoveTo(*xasu16+1,*yasu16+1),SetBackgroundColor(ifi==0{Color::DarkGreen}else{Color::Green}),Print("■"),ResetColor)?;}// 显示分数和游戏结束提示letscore_y=(self.height+3)asu16;execute!(stdout,MoveTo(0,score_y),Print(format!("Score: {}",self.score)))?;ifself.game_over{execute!(stdout,MoveTo(0,score_y+1),Print("Game Over! Press 'q' to quit or 'r' to restart"))?;}execute!(stdout,MoveTo(0,score_y+2))?;stdout.flush()}}/// 检查是否退出fnshould_quit(key:KeyCode)->bool{key==KeyCode::Char('q')||key==KeyCode::Esc}fnmain()->Result<()>{let(term_w,term_h)=size()?;letwidth=(term_w.saturating_sub(4)asusize).min(50);letheight=(term_h.saturating_sub(8)asusize).min(25);// 初始化终端enable_raw_mode()?;execute!(stdout(),EnterAlternateScreen,Hide)?;letmutgame=Game::new(width,height);// 游戏主循环:120ms 一帧loop{game.render()?;ifpoll(Duration::from_millis(120))?{ifletEvent::Key(KeyEvent{code,modifiers,..})=read()?{ifshould_quit(code){break;}ifgame.game_over&&code==KeyCode::Char('r'){game=Game::new(width,height);continue;}ifmodifiers==KeyModifiers::NONE{matchcode{KeyCode::Up|KeyCode::Char('w')=>game.set_direction(Direction::Up),KeyCode::Down|KeyCode::Char('s')=>game.set_direction(Direction::Down),KeyCode::Left|KeyCode::Char('a')=>game.set_direction(Direction::Left),KeyCode::Right|KeyCode::Char('d')=>game.set_direction(Direction::Right),_=>{}}}}}game.update();}// 恢复终端execute!(stdout(),Show,LeaveAlternateScreen)?;disable_raw_mode()?;println!("Final score: {}",game.score);Ok(())}

2、Cargo.toml

[package]name="ttt"version="0.1.0"edition="2024"[dependencies]crossterm="0.28"rand="0.8"

三、rand 包详解

rand是 Rust 生态中用于生成随机数的核心库。它提供了高质量的随机数生成器(RNG)、多种分布采样方法以及方便的实用工具。在本贪吃蛇游戏中,我们使用rand来在游戏区域内随机生成食物位置。

1、 添加依赖

Cargo.toml中添加rand依赖:

[dependencies] rand = "0.8"

版本0.8是当前广泛使用的稳定版本,提供了丰富的 API 和良好的性能。

2、 核心概念

2.1、 随机数生成器 (RNG)

rand库的核心是随机数生成器(RNG),它负责产生随机比特序列。rand提供了多种 RNG 实现:

  • ThreadRng:线程局部的、密码学安全的 RNG,通过rand::thread_rng()获取。这是最常用的 RNG,性能良好且安全。
  • StdRng:基于 ChaCha 算法的密码学安全 RNG,适合需要可重现随机序列的场景。
  • SmallRng:非密码学安全的、高性能的 RNG,适合模拟和游戏等对速度要求高的场景。

2.2、 分布 (Distributions)

分布定义了如何将 RNG 产生的原始随机比特映射到特定范围的数值或类型。rand提供了多种分布:

  • 均匀分布Uniform,用于生成指定范围内的整数或浮点数。
  • 正态分布Normal,生成符合正态(高斯)分布的随机数。
  • 伯努利分布Bernoulli,以给定概率生成truefalse
  • 加权选择WeightedIndex,根据权重从列表中随机选择元素。

3、 基本用法

3.1、 生成随机数

userand::Rng;fnmain(){letmutrng=rand::thread_rng();// 生成一个随机整数(i32 类型)letn1:i32=rng.gen();println!("Random i32: {}",n1);// 生成一个 [0, 1) 之间的随机浮点数(f64 类型)letn2:f64=rng.gen();println!("Random f64 in [0,1): {}",n2);// 生成指定范围的随机整数letn3=rng.gen_range(0..10);// 包含 0,不包含 10println!("Random integer in [0, 10): {}",n3);// 生成指定范围的随机浮点数letn4=rng.gen_range(0.0..1.0);println!("Random float in [0.0, 1.0): {}",n4);}

3.2 、随机布尔值与选择

userand::Rng;fnmain(){letmutrng=rand::thread_rng();// 以 50% 的概率生成 trueletb:bool=rng.gen_bool(0.5);println!("Random bool: {}",b);// 从数组中随机选择一个元素letitems=["apple","banana","cherry"];letchoice=rng.choose(&items).unwrap();println!("Random choice: {}",choice);// 打乱数组(原地)letmutnums=vec![1,2,3,4,5];rng.shuffle(&mutnums);println!("Shuffled: {:?}",nums);}

4、在贪吃蛇游戏中的应用

在我们的贪吃蛇游戏中,rand用于在空白位置生成食物。相关代码位于Game::spawn_food方法中:

/// 在空白位置随机生成食物fnspawn_food(&mutself){letmutrng=rand::thread_rng();loop{letx=rng.gen_range(0..self.width);lety=rng.gen_range(0..self.height);if!self.snake.contains(&(x,y)){self.food=(x,y);break;}}}

代码解析:

  1. 获取 RNGlet mut rng = rand::thread_rng();获取当前线程的随机数生成器。
  2. 生成随机坐标rng.gen_range(0..self.width)生成一个在[0, self.width)范围内的随机整数作为 x 坐标。同理生成 y 坐标。
  3. 避免重复:使用loop循环确保生成的位置不在蛇身上(!self.snake.contains(&(x, y)))。如果冲突,则继续生成新的随机位置,直到找到空白位置为止。
  4. 设置食物:将找到的空白位置赋值给self.food

这种“生成-检查”循环是游戏开发中常见的随机位置生成模式,确保游戏逻辑的正确性。

5、 高级特性与性能

5.1 、种子与可重现性

如果需要可重现的随机序列(例如用于测试或回放),可以使用种子初始化 RNG:

userand::{Rng,SeedableRng};userand_chacha::ChaCha8Rng;// 需要额外依赖 rand_chachafnmain(){letseed=[0;32];// 32 字节的种子letmutrng=ChaCha8Rng::from_seed(seed);// 每次运行都会产生相同的随机数序列println!("{}",rng.gen::<u32>());}

5.2 、性能优化

  • 重用 RNG:避免在循环中反复创建thread_rng(),应在循环外创建并传递。
  • 选择合适的分布:对于简单的范围随机,gen_range已足够高效。对于需要大量采样的情况,考虑预先生成随机数数组。
  • 使用SmallRng:如果不需要密码学安全,可以使用SmallRng获得更好的性能。

6、常见问题与陷阱

  1. 范围包含性gen_range(a..b)包含a但不包含bgen_range(a..=b)包含两端。
  2. 线程安全thread_rng()返回的是线程局部的 RNG,不同线程会得到不同的随机序列。
  3. 性能瓶颈:在紧密循环中频繁调用gen_range可能成为性能瓶颈,可以考虑批量生成。
  4. 均匀性gen_range保证均匀分布,但某些自定义分布可能需要手动实现。

7、总结

rand库是 Rust 中处理随机性的标准工具,提供了从简单随机数生成到复杂分布采样的完整功能。在贪吃蛇游戏中,我们使用rand::thread_rng()gen_range方法实现了食物的随机生成,这是游戏随机性的核心来源。掌握rand的基本用法后,你可以轻松将其应用于游戏开发、模拟、测试数据生成等多种场景。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/19 12:48:24

TML-Bench:评估表格机器学习智能体能力的基准测试框架

1. 为什么我们需要一个表格机器学习智能体基准测试&#xff1f; 如果你在数据科学领域摸爬滚打超过三年&#xff0c;大概率已经历过这样的场景&#xff1a;面对一个新的表格数据预测任务&#xff0c;比如经典的泰坦尼克号生存预测&#xff0c;你打开Kaggle&#xff0c;找到几个…

作者头像 李华
网站建设 2026/8/19 12:48:03

中小企业用IDC数据中心最头疼什么?5大成本与运维痛点拆解

一、中小企业用IDC数据中心&#xff0c;钱花在哪最心疼 一家三十人电商团队租了整柜IDC数据中心&#xff0c;设备上架后才发现没人会处理系统蓝屏。这种客户我一年能碰到好几家。 租金只是明面成本&#xff0c;运维人力、带宽超量、故障停机才是大头。Uptime Institute的宕机研…

作者头像 李华
网站建设 2026/8/19 12:46:34

多智能体系统福利主义控制:从社会契约到分布式优化实践

1. 项目概述&#xff1a;当多智能体系统需要“社会契约” 最近在跟几个做机器人集群和自动驾驶车队的朋友聊天&#xff0c;大家不约而同地提到了一个共同的痛点&#xff1a;当一群智能体&#xff08;Agent&#xff09;在一起协作时&#xff0c;怎么才能让整个系统不只是“能跑起…

作者头像 李华
网站建设 2026/8/19 12:45:58

RT-Thread信号量实战指南:从原理到调试,嵌入式多线程同步核心

1. 从“抢车位”到“信号量”&#xff1a;一个嵌入式老兵的实战开场 干了十几年嵌入式&#xff0c;从51单片机裸跑到现在的RTOS满天飞&#xff0c;我见过太多项目因为资源同步问题而“翻车”。最近在带新人&#xff0c;发现他们学RT-Thread时&#xff0c;对信号量&#xff08;S…

作者头像 李华
网站建设 2026/8/19 12:42:18

Vue 3 中文文档完整指南:从零基础到项目实战的终极学习路线

Vue 3 中文文档完整指南&#xff1a;从零基础到项目实战的终极学习路线 【免费下载链接】docs-next-zh-cn :cn: Chinese translation for v3.vuejs.org 项目地址: https://gitcode.com/gh_mirrors/do/docs-next-zh-cn 如果你正在寻找一份能让你无障碍学懂 Vue 3 的中文资…

作者头像 李华