youtube:egui-tictactoe
差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
youtube:egui-tictactoe [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1 | youtube:egui-tictactoe [2024/02/29 11:49] (現在) – freemikan | ||
---|---|---|---|
行 1: | 行 1: | ||
+ | ====== 三並べ (Tic-Tac-Toe) ====== | ||
+ | 作成日: 2022-12-14 (水) | ||
+ | |||
+ | {{: | ||
+ | |||
+ | ===== src/main.rs ===== | ||
+ | |||
+ | |||
+ | <file rust> | ||
+ | use eframe:: | ||
+ | |||
+ | fn main() { | ||
+ | let options = eframe:: | ||
+ | initial_window_size: | ||
+ | ..Default:: | ||
+ | }; | ||
+ | eframe:: | ||
+ | " | ||
+ | options, | ||
+ | Box:: | ||
+ | ); | ||
+ | } | ||
+ | |||
+ | struct MyApp { | ||
+ | turn: Player, | ||
+ | pieces: Vec< | ||
+ | square_size: | ||
+ | } | ||
+ | |||
+ | impl Default for MyApp { | ||
+ | fn default() -> Self { | ||
+ | Self { | ||
+ | turn: Player::O, | ||
+ | pieces: Vec::new(), | ||
+ | square_size: | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | impl eframe::App for MyApp { | ||
+ | fn update(& | ||
+ | let winner = winner(& | ||
+ | let draw_match = self.pieces.len() == 9 && winner == None; | ||
+ | |||
+ | if winner == None && !draw_match { | ||
+ | // step the game | ||
+ | if let Some(pos) = self.clicked_position(ctx) { | ||
+ | let board_pos = egui::pos2( | ||
+ | (pos.x / self.square_size).floor(), | ||
+ | (pos.y / self.square_size).floor(), | ||
+ | ); | ||
+ | |||
+ | let open_square = self.pieces.iter().all(|p| { | ||
+ | p.place.x as i32 != board_pos.x as i32 || p.place.y as i32 != board_pos.y as i32 | ||
+ | }); | ||
+ | let on_board = board_pos.x >= 0.0 | ||
+ | && board_pos.x <= 2.0 | ||
+ | && board_pos.y >= 0.0 | ||
+ | && board_pos.y <= 2.0; | ||
+ | |||
+ | if open_square && on_board { | ||
+ | self.pieces.push(Piece { | ||
+ | player: self.turn, | ||
+ | place: board_pos, | ||
+ | }); | ||
+ | |||
+ | // next turn | ||
+ | self.turn = if self.turn == Player::O { | ||
+ | Player::X | ||
+ | } else { | ||
+ | Player::O | ||
+ | }; | ||
+ | } | ||
+ | } | ||
+ | } else { | ||
+ | // wait for right button clicked to restart the game | ||
+ | if ctx.input().pointer.secondary_clicked() { | ||
+ | *self = Default:: | ||
+ | } | ||
+ | } | ||
+ | |||
+ | egui:: | ||
+ | self.draw_board(ui.painter(), | ||
+ | self.draw_pieces(ui.painter(), | ||
+ | |||
+ | if let Some(player_wins) = winner { | ||
+ | self.draw_winner(ui.painter(), | ||
+ | } | ||
+ | |||
+ | if draw_match { | ||
+ | self.draw_draw_match(ui.painter()); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | impl MyApp { | ||
+ | fn clicked_position(& | ||
+ | // avoid dead lock | ||
+ | // [[https:// | ||
+ | if let Some(pos) = { ctx.input().pointer.hover_pos() } { | ||
+ | if ctx.input().pointer.any_click() { | ||
+ | return Some(pos); | ||
+ | } | ||
+ | } | ||
+ | None | ||
+ | } | ||
+ | |||
+ | fn draw_board(& | ||
+ | for i in 0..3 { | ||
+ | for j in 0..3 { | ||
+ | let rect = egui:: | ||
+ | egui:: | ||
+ | egui:: | ||
+ | ); | ||
+ | let rounding = 0.0; | ||
+ | let color = egui:: | ||
+ | let stroke = egui:: | ||
+ | painter.rect(rect, | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fn draw_pieces(& | ||
+ | for piece in & | ||
+ | piece.draw(painter, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fn draw_winner(& | ||
+ | painter.text( | ||
+ | painter.clip_rect().center(), | ||
+ | egui:: | ||
+ | format!( | ||
+ | " | ||
+ | if winner == Player::O { " | ||
+ | ), | ||
+ | egui:: | ||
+ | egui:: | ||
+ | ); | ||
+ | } | ||
+ | |||
+ | fn draw_draw_match(& | ||
+ | painter.text( | ||
+ | painter.clip_rect().center(), | ||
+ | egui:: | ||
+ | " | ||
+ | egui:: | ||
+ | egui:: | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # | ||
+ | enum Player { | ||
+ | O, | ||
+ | X, | ||
+ | } | ||
+ | |||
+ | struct Piece { | ||
+ | player: Player, | ||
+ | place: egui::Pos2, | ||
+ | } | ||
+ | |||
+ | impl Piece { | ||
+ | fn draw(& | ||
+ | let p = egui:: | ||
+ | match self.player { | ||
+ | Player::O => { | ||
+ | painter.circle_stroke( | ||
+ | egui:: | ||
+ | square_size * 0.3, | ||
+ | (3.0, egui:: | ||
+ | ); | ||
+ | } | ||
+ | Player::X => { | ||
+ | painter.line_segment( | ||
+ | [ | ||
+ | egui:: | ||
+ | egui:: | ||
+ | ], | ||
+ | (3.0, egui:: | ||
+ | ); | ||
+ | painter.line_segment( | ||
+ | [ | ||
+ | egui:: | ||
+ | egui:: | ||
+ | ], | ||
+ | (3.0, egui:: | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fn winner(pieces: | ||
+ | for player in [Player::O, Player::X] { | ||
+ | for i in 0..3 { | ||
+ | if 3 == pieces | ||
+ | .iter() | ||
+ | .filter(|p| p.player == player) | ||
+ | .filter(|p| p.place.x as i32 == i) | ||
+ | .count() | ||
+ | { | ||
+ | return Some(player); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | for i in 0..3 { | ||
+ | if 3 == pieces | ||
+ | .iter() | ||
+ | .filter(|p| p.player == player) | ||
+ | .filter(|p| p.place.y as i32 == i) | ||
+ | .count() | ||
+ | { | ||
+ | return Some(player); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if 3 == pieces | ||
+ | .iter() | ||
+ | .filter(|p| p.player == player) | ||
+ | .filter(|p| p.place.x as i32 == p.place.y as i32) | ||
+ | .count() | ||
+ | { | ||
+ | return Some(player); | ||
+ | } | ||
+ | |||
+ | if 3 == pieces | ||
+ | .iter() | ||
+ | .filter(|p| p.player == player) | ||
+ | .filter(|p| 2 - p.place.x as i32 == p.place.y as i32) | ||
+ | .count() | ||
+ | { | ||
+ | return Some(player); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | None | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Cargo.toml ===== | ||
+ | |||
+ | |||
+ | < | ||
+ | [package] | ||
+ | name = " | ||
+ | version = " | ||
+ | edition = " | ||
+ | |||
+ | # See more keys and their definitions at https:// | ||
+ | |||
+ | [dependencies] | ||
+ | eframe = " | ||
+ | </ |