康威生命游戏:Rust实现与原理详解

0 阅读4分钟

我的源码仓库

简介

康威生命游戏(英语:Conway's Game of Life),又称康威生命棋,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.
它最初于1970年10月在《科学美国人》杂志上马丁·葛登能的“数学游戏”专栏出现.

核心规则

每个细胞有两种状态- 存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动(如图,黑色为存活,白色为死亡)

  • 当前细胞为存活状态时,当周围的存活细胞低于2个时(不包含2个),该细胞变成死亡状态。(模拟生命数量稀少)
  • 当前细胞为存活状态时,当周围有2个或3个存活细胞时,该细胞保持原样。
  • 当前细胞为存活状态时,当周围有超过3个存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
  • 当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。(模拟繁殖)

代码实现

(核心) fn main()

  • 核心的程序循环所在处
fn main() {
    // init an 2D vector
    let cols: usize = 40;
    let rows: usize = 100;
    let mut arr: Vec<Vec<MyCell>> = (0..cols)
        .map(|_| {
            (0..rows)
                .map(|_| MyCell {
                    is_alive: false,
                    alive_around_cnt: 0,
                })
                .collect()
        })
        .collect();

    // draw the gun(绘制"高斯帕机枪")
    draw_the_gun(&mut arr);

    // start the main loop("开始进行核心主循环")
    loop {
        draw(&arr);
        thread::sleep(Duration::from_secs_f32(0.15));
        flush_the_screen();
        update_alive_around_count(&mut arr);
        update_mycells(&mut arr);
    }
}

(核心) struct MyCell

  • is_alive: true->当前细胞存活,flase->当前细胞死亡;
    alive_around_cnt->当前细胞周围的存活细胞数量;
struct MyCell {
    is_alive: bool,
    alive_around_cnt: usize,
}

fn draw_the_gun(arr: &mut Vec<Vec>) -> ()

  • 绘制高斯帕机枪
fn draw_the_gun(arr: &mut Vec<Vec<MyCell>>) -> () {
    let directions: [(usize, usize); 36] = [
        (24, 0),
        (22, 1),
        (24, 1),
        (12, 2),
        (13, 2),
        (20, 2),
        (21, 2),
        (34, 2),
        (35, 2),
        (11, 3),
        (15, 3),
        (20, 3),
        (21, 3),
        (34, 3),
        (35, 3),
        (0, 4),
        (1, 4),
        (10, 4),
        (16, 4),
        (20, 4),
        (21, 4),
        (0, 5),
        (1, 5),
        (10, 5),
        (14, 5),
        (16, 5),
        (17, 5),
        (22, 5),
        (24, 5),
        (10, 6),
        (16, 6),
        (24, 6),
        (11, 7),
        (15, 7),
        (12, 8),
        (13, 8),
    ];
    for (dr, dc) in directions {
        set_seeds_alive(arr, dr + 2, dc + 25);
    }
}  

fn draw(arr: &Vec<Vec>) -> ()

  • 绘制"一次"当前的生命游戏图("O"表示存活细胞,"-"表示死亡细胞)
// draw the current cells' status
fn draw(arr: &Vec<Vec<MyCell>>) -> () {
    for i in arr {
        for j in i {
            match j.is_alive {
                true => print!("O"),
                false => print!("-"),
            }
        }
        print!("\n");
    }
}

thread::sleep(Duration::from_secs_f32(0.15))

  • 就是让当前的程序停止0.15秒再运行,防止"draw()"
    还没在终端画完所有字符就继续执行了
thread::sleep(Duration::from_secs_f32(0.15));

fn flush_the_screen() -> ()

  • 向终端写入"\x1B[2J\x1B[H"清空屏幕并把光标移动到左上角
fn flush_the_screen() -> () {
    print!("\x1B[2J\x1B[H");
}

fn update_alive_around_count(arr: &mut Vec<Vec>) -> ()

  • 遍历arr,更新arr中每个MyCell(细胞)周围的存活细胞数并存入MyCell结构体
    的"alive_around_cnt"成员中,方便统一管理MyCell
fn update_alive_around_count(arr: &mut Vec<Vec<MyCell>>) -> () {
    let max_cols: usize = arr.len() - 1;
    let max_rows: usize = arr[0].len() - 1;
    for i in 0..max_cols {
        for j in 0..max_rows {
            match count_cells_around(&arr, i, j) {
                -1 => continue,
                count => arr[i][j].alive_around_cnt = count as usize,
            }
        }
    }
}

fn update_mycells(arr: &mut Vec<Vec>) -> ()

  • 遍历arr,根据MyCell结构体中的"alive_around_cnt"和
    "is_alive"判断"下一代"时当前细胞的"is_alive"状态
fn update_mycells(arr: &mut Vec<Vec<MyCell>>) -> () {
    let max_cols: usize = arr.len() - 1;
    let max_rows: usize = arr[0].len() - 1;
    for i in 0..max_cols {
        for j in 0..max_rows {
            match arr[i][j].is_alive {
                true => {
                    if arr[i][j].alive_around_cnt < 2 {
                        arr[i][j].is_alive = false;
                    } else if arr[i][j].alive_around_cnt > 3 {
                        arr[i][j].is_alive = false;
                    }
                }
                false => {
                    if arr[i][j].alive_around_cnt == 3 {
                        arr[i][j].is_alive = true;
                    }
                }
            }
        }
    }
}

程序流程图

  • 在这里插入图片描述

杂谈总结

才疏学浅,想着练练手于是开始写这个有意思的小项目,然后记录一下过程
,后面也会继续写着几个有意思的小项目拿来分享;作为rust初学者,写这
个代码过程中还是遇到很多困难,但我尽量避免了使用ai辅助编码和答疑
以更加充分地训练自己快速学习一个新的编程语言的能力,还有阅读保存信
息,查阅文档的能力;这次写代码让我发现自己似乎更喜欢这种按照兴趣自
由探索书写代码的过程,我似乎更喜欢一个没有明确目的的"冒险式"编程,
而不是去做八股文的算法题(但是适当做那些算法题目还是很有必要的orz.
好吧大概就是这样,一个小无聊又小有趣的生命游戏极简原型的博客就写这
么多吧哈哈