rustの勉強がてらライフゲームを作ったので軽く紹介
ソースコードは↓
https://github.com/yamu77/rust-life-game
ライフゲームについては説明しないので各々で調べてもらえると

rustの勉強がてらライフゲームを作ったので軽く紹介
ソースコードは↓
https://github.com/yamu77/rust-life-game
ライフゲームについては説明しないので各々で調べてもらえると
1#[derive(Clone, Copy, PartialEq)]
2enum GameState {
3 Menu, // メニュー選択
4 Editing, // 編集モード
5 Running, // 実行中
6 Paused, // 一時停止
7}状態は
の4つで保持
1fn count_neighbors(&self, x: usize, y: usize) -> usize {
2 let mut count = 0;
3 for dy in -1..=1 {
4 for dx in -1..=1 {
5 if dx == 0 && dy == 0 {
6 continue;
7 }
8 let nx = x as i32 + dx;
9 let ny = y as i32 + dy;
10 if nx >= 0 && nx < BOARD_SIZE as i32 && ny >= 0 && ny < BOARD_SIZE as i32 {
11 if self.board[ny as usize][nx as usize] {
12 count += 1;
13 }
14 }
15 }
16 }
17 count
18}
19
20fn next_generation(&mut self) {
21 let mut new_board = [[false; BOARD_SIZE]; BOARD_SIZE];
22 for y in 0..BOARD_SIZE {
23 for x in 0..BOARD_SIZE {
24 let neighbors = self.count_neighbors(x, y);
25 let alive = self.board[y][x];
26 new_board[y][x] = match (alive, neighbors) {
27 (true, 2) | (true, 3) => true, // 生存
28 (false, 3) => true, // 誕生
29 _ => false, // 死亡または維持
30 };
31 }
32 }
33 self.board = new_board;
34}ライフゲームは自分の周囲8マスの生死の状態を見て自分の状態が決まるので
単純に2重ループ回して生存数をカウントして次の状態を決定
1fn render_menu(&self, stdout: &mut io::Stdout) -> Result<()> {
2 let board_width = BOARD_SIZE * CELL_SIZE;
3 let menu_x = (board_width + 5) as u16; // 盤面の右側にメニューを配置
4 let menu_y = 5u16;
5
6 // タイトル
7 execute!(
8 stdout,
9 MoveTo(menu_x, menu_y - 2),
10 SetForegroundColor(Color::Cyan),
11 Print("メニュー")
12 )?;
13
14 // メニュー項目
15 let menu_items = [
16 ("編集", MenuOption::Edit),
17 ("スタート", MenuOption::Start),
18 ("終了", MenuOption::Exit),
19 ];
20
21 for (i, (text, option)) in menu_items.iter().enumerate() {
22 let y = menu_y + i as u16;
23
24 if self.menu_selection == *option {
25 execute!(
26 stdout,
27 MoveTo(menu_x, y),
28 SetBackgroundColor(Color::Yellow),
29 SetForegroundColor(Color::Black),
30 Print(format!("> {} <", text))
31 )?;
32 } else {
33 execute!(
34 stdout,
35 MoveTo(menu_x, y),
36 SetBackgroundColor(Color::Reset),
37 SetForegroundColor(Color::Reset),
38 Print(format!(" {} ", text))
39 )?;
40 }
41 }
42
43 Ok(())
44}ターミナルで動かすのでcrosstermクレートのMoveToでカーソルを動かし、SetBackgroundColorとSetForegroundColorで背景色やら文字色やらを変えながら画面を表示
1fn main() -> Result<()> {
2 enable_raw_mode()?;
3 let mut stdout = stdout();
4 execute!(stdout, Hide)?;
5
6 let mut game = Game::new();
7 let mut last_update = Instant::now();
8 let update_interval = Duration::from_millis(200); // 世代更新の間隔
9
10 loop {
11 // キー入力のポーリング(非ブロッキング)
12 if event::poll(Duration::from_millis(10))? {
13 if let Event::Key(KeyEvent {
14 code,
15 kind,
16 ..
17 }) = event::read()? {
18 // PressとRepeatの両方を処理
19 if kind != KeyEventKind::Release {
20 match code {
21 KeyCode::Up => {
22 match game.state {
23 GameState::Menu => {
24 game.move_menu_selection(true);
25 }
26 GameState::Editing | GameState::Paused => {
27 if game.cursor_y > 0 {
28 game.move_cursor(0, -1);
29 }
30 }
31 _ => {}
32 }
33 }
34 KeyCode::Down => {
35 match game.state {
36 GameState::Menu => {
37 game.move_menu_selection(false);
38 }
39 GameState::Editing | GameState::Paused => {
40 if game.cursor_y < BOARD_SIZE - 1 {
41 game.move_cursor(0, 1);
42 }
43 }
44 _ => {}
45 }
46 }
47 KeyCode::Left => {
48 if game.state == GameState::Editing || game.state == GameState::Paused {
49 if game.cursor_x > 0 {
50 game.move_cursor(-1, 0);
51 }
52 }
53 }
54 KeyCode::Right => {
55 if game.state == GameState::Editing || game.state == GameState::Paused {
56 if game.cursor_x < BOARD_SIZE - 1 {
57 game.move_cursor(1, 0);
58 }
59 }
60 }
61 KeyCode::Enter => {
62 match game.state {
63 GameState::Menu => {
64 match game.menu_selection {
65 MenuOption::Edit => {
66 game.enter_editing();
67 }
68 MenuOption::Start => {
69 game.start_game();
70 }
71 MenuOption::Exit => {
72 // 終了処理
73 execute!(stdout, Clear(ClearType::All))?;
74 execute!(stdout, MoveTo(0, 0))?; // カーソルを一番左に移動
75 execute!(stdout, Show)?;
76 disable_raw_mode()?;
77 return Ok(());
78 }
79 }
80 }
81 GameState::Editing => {
82 if game.cursor_y < BOARD_SIZE {
83 game.toggle_cell();
84 }
85 }
86 GameState::Running | GameState::Paused => {
87 game.enter_editing_from_running();
88 }
89 }
90 }
91 KeyCode::Char(' ') => {
92 match game.state {
93 GameState::Running => {
94 game.pause();
95 }
96 GameState::Paused => {
97 game.resume();
98 }
99 _ => {}
100 }
101 }
102 KeyCode::Esc => {
103 match game.state {
104 GameState::Editing | GameState::Running | GameState::Paused => {
105 game.return_to_menu();
106 }
107 _ => {}
108 }
109 }
110 _ => {}
111 }
112 }
113 }
114 }
115
116 // 実行中は一定間隔で世代更新
117 if game.state == GameState::Running {
118 if last_update.elapsed() >= update_interval {
119 game.next_generation();
120 last_update = Instant::now();
121 }
122 }
123
124 // 画面を描画
125 game.render(&mut stdout)?;
126 }
127}あとはキー入力を受け付けて状態遷移やセルの生死切り替えをできるようにして、無限ループで画面を更新
所有権だったりトレイトだったりほかの言語だと聞かない用語が多くてハードルは高く感じたけど、軽く何かを作る分には少し慣れてきた程度でなんだかんだ作れる。
理解があやふやなだったり今回はあまり気にしなくていい概念とかrustの機能もあるだろうし、時間があるときにもう少し重めの開発もやってみたい