地图生成与迭代器

上文我们大致了解了"文字地牢"小游戏的行为抽象,那么我们本章的目标是用 rand 实现简单随机生成;通过迭代器链处理地图数据,掌握闭包与适配器方法。

基本概念

随机数生成

Rust通过 rand crate提供随机数生成功能,可以生成各种类型的随机数,包括整数、浮点数和布尔值。

随机数生成器类型:

  1. ThreadRng:线程本地的随机数生成器,适合大多数情况
  2. StdRng:标准的随机数生成器,可配置种子
  3. SmallRng:快速但密码学不安全的生成器

随机数分布:

  1. 均匀分布gen_range()生成指定范围内的随机数
  2. 布尔分布gen_bool()生成布尔值
  3. 选择分布choose()从集合中随机选择

迭代器

迭代器是Rust中一种强大的抽象,允许我们以声明式的方式处理序列数据。它们是惰性求值的,只有在需要时才计算下一个值。

迭代器的特性:

  1. 惰性求值:适配器方法不会立即执行
  2. 零成本抽象:编译器优化后与手写循环性能相当
  3. 组合性:可以链式调用多种适配器方法
  4. 安全性:编译时保证内存安全

迭代器类型:

  1. Iterator trait:定义迭代器行为的核心trait
  2. IntoIterator trait:允许类型被用于 for循环
  3. Consuming adapters:消耗迭代器并产生结果
  4. Iterator adapters:转换迭代器但不消耗

闭包

闭包是匿名函数,可以捕获其环境中的变量。在迭代器链中经常使用闭包来转换或过滤数据。

闭包的特点:

  1. 匿名性:不需要显式命名
  2. 捕获环境:可以访问定义时作用域中的变量
  3. 灵活的语法:参数和返回类型可以自动推断
  4. 多种捕获模式:可以不可变借用、可变借用或获取所有权

如何使用

随机数生成详解

use rand::{Rng, thread_rng, SeedableRng};
use rand::rngs::StdRng;

// 使用线程本地生成器
let mut rng = thread_rng();
let random_bool = rng.gen_bool(0.5);
let random_int = rng.gen_range(1..=100);

// 使用种子生成器(可重现)
let mut rng = StdRng::seed_from_u64(42);
let reproducible_value = rng.gen::<f64>();

// 不同分布的随机数
let uniform = rng.gen_range(1..=10);
let normal = rand_distr::Normal::new(0.0, 1.0).unwrap();
let sample = rng.sample(normal);

迭代器适配器详解

// 基本适配器
let numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
let evens: Vec<&i32> = numbers.iter().filter(|&x| x % 2 == 0).collect();

// 复杂适配器链
let result: Vec<String> = (1..10)
    .filter(|x| x % 2 == 0)
    .map(|x| x * x)
    .take(3)
    .map(|x| x.to_string())
    .collect();

// 扁平化适配器
let nested = vec![vec![1, 2], vec![3, 4, 5]];
let flat: Vec<i32> = nested.into_iter().flatten().collect();

// 分组和窗口
let data = vec![1, 2, 3, 4, 5];
let windows: Vec<&[i32]> = data.windows(2).collect();
let chunks: Vec<&[i32]> = data.chunks(2).collect();

闭包捕获模式

let x = 42;

// 不可变借用(默认)
let closure1 = || println!("x is {}", x);

// 可变借用
let mut y = 10;
let mut closure2 = || {
    y += 1;
    println!("y is {}", y);
};

// 获取所有权(move)
let z = String::from("hello");
let closure3 = move || println!("z is {}", z);

随机生成示例

use rand::{Rng, thread_rng};
let mut rng = thread_rng();
for y in 1..height-1 {
    for x in 1..width-1 {
        if rng.gen_bool(0.15) { tiles[index(width, x, y)] = Tile::Floor; }
    }
}

迭代器与闭包

  • 统计地板数量:
let floor_count = (0..height).flat_map(|y| (0..width).map(move |x| (x, y)))
    .map(|(x,y)| map.get_tile(x,y))
    .filter(|t| matches!(t, Tile::Floor))
    .count();
  • map/filter/count 组合简洁高效;注意闭包中捕获的所有权与借用。

注意事项

  1. 迭代器是惰性求值的,必须使用消费适配器(如 countcollect)才会执行。
  2. 注意迭代器链中所有权的转移,避免不必要的克隆。
  3. 随机数生成器应正确初始化,以确保随机性。
  4. 在使用闭包时,注意变量捕获的方式(借用或移动)。
  5. 对于性能敏感的代码,迭代器链通常比手动循环更高效。
  6. 使用 move闭包时要确保变量的所有权正确转移。
  7. 在迭代器链中处理错误时,考虑使用 filter_maptry_fold等方法。
  8. 对于大型数据集,考虑使用 rayon crate进行并行迭代。

本项目中的使用

在我们的项目中,我们使用随机数生成来创建地图,并使用迭代器来处理地图数据:

use rand::{Rng, thread_rng};
let mut rng = thread_rng();
for y in 1..height-1 {
    for x in 1..width-1 {
        if rng.gen_bool(0.15) { tiles[index(width, x, y)] = Tile::Floor; }
    }
}

我们还使用迭代器来统计地图中的地板数量:

let floor_count = (0..height).flat_map(|y| (0..width).map(move |x| (x, y)))
    .map(|(x,y)| map.get_tile(x,y))
    .filter(|t| matches!(t, Tile::Floor))
    .count();

地图生成的改进方案

在地牢探险游戏中,我们可以使用更复杂的地图生成算法:

// 房间生成算法
fn generate_rooms(map: &mut Map, rng: &mut impl Rng) {
    let room_count = rng.gen_range(3..=8);
    for _ in 0..room_count {
        let width = rng.gen_range(3..=8);
        let height = rng.gen_range(3..=8);
        let x = rng.gen_range(1..map.width - width - 1);
        let y = rng.gen_range(1..map.height - height - 1);
      
        // 挖掘房间
        for ry in y..y+height {
            for rx in x..x+width {
                map.set_tile(rx, ry, Tile::Floor);
            }
        }
    }
}

// 连接房间的隧道算法
fn connect_rooms(map: &mut Map, rooms: &[Room]) {
    for window in rooms.windows(2) {
        let (room1, room2) = (&window[0], &window[1]);
        // 连接算法实现
    }
}

迭代器在游戏中的其他应用

  1. 视野计算
fn calculate_visible_tiles(player_pos: Position, range: usize) -> Vec<Position> {
    (-range as i32..=range as i32)
        .flat_map(|dx| (-range as i32..=range as i32).map(move |dy| (dx, dy)))
        .filter(|(dx, dy)| (dx * dx + dy * dy) as usize <= range * range)
        .map(|(dx, dy)| Position {
            x: (player_pos.x as i32 + dx) as usize,
            y: (player_pos.y as i32 + dy) as usize,
        })
        .filter(|pos| is_valid_position(pos))
        .collect()
}
  1. 敌人AI决策
fn find_nearest_player(enemies: &[Enemy], player: &Player) -> Option<&Enemy> {
    enemies
        .iter()
        .min_by_key(|enemy| {
            let dx = enemy.position.x as i32 - player.position.x as i32;
            let dy = enemy.position.y as i32 - player.position.y as i32;
            dx * dx + dy * dy
        })
}

高级迭代器模式

自定义迭代器

struct Fibonacci {
    current: u64,
    next: u64,
}

impl Fibonacci {
    fn new() -> Fibonacci {
        Fibonacci { current: 0, next: 1 }
    }
}

impl Iterator for Fibonacci {
    type Item = u64;
  
    fn next(&mut self) -> Option<u64> {
        let current = self.current;
        let next = self.next;
        self.current = next;
        self.next = current + next;
        Some(current)
    }
}

迭代器组合模式

// 链式处理数据
let result = data
    .iter()
    .filter(|x| x.is_valid())
    .map(|x| x.process())
    .take(100)
    .collect::<Vec<_>>();

性能优化建议

  1. 避免不必要的collect:只在需要时才收集结果
  2. 使用合适的迭代器适配器:如 filter_mapfilter+map更高效
  3. 考虑并行迭代:对于独立操作,使用 rayon进行并行处理
  4. 使用 fold进行累积操作:比先collect再处理更高效

练习:

  1. 将随机生成封装为 map::gen::dig_tunnels(&mut Map)
  2. 在 HUD 中显示 floor_count 与墙比例。

概念补充

  • 迭代器所有权:into_iter 消费集合、iter 借用不可变元素、iter_mut 借用可变元素;链式调用需留意谁拥有数据。
  • 惰性求值:迭代器适配器(map/filter/flat_map)是惰性的,只有在 “消费端”(如 countcollect)才执行。
  • collect 的目标类型:使用"涡轮鱼"注解指定 ::<Vec<_>>::<HashSet<_>>,或通过变量类型推断。
  • 闭包捕获:按需捕获(借用/移动);在多线程或 'static 约束下可能需要 move 闭包。
  • 性能:尽量使用迭代器链避免中间临时分配;对热路径可配合 rayon 并行迭代(后续扩展)。
  • 随机性与可重复:通过固定种子(StdRng::seed_from_u64)获得可重现关卡;生产中可使用时间种子。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐