made wish.com paper.io lol
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.cargo.extraArgs": ["-Z", "unstable-options"]
|
||||
}
|
||||
@@ -330,6 +330,7 @@ pub fn special_char(ch: char) -> Option<u8> {
|
||||
'┌' => 218,
|
||||
'┼' => 197,
|
||||
'░' => 176,
|
||||
'▒' => 177,
|
||||
'▓' => 178,
|
||||
'«' => 174,
|
||||
_ => {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::std::{
|
||||
};
|
||||
use crate::println;
|
||||
|
||||
const CRYSTAL_LOGO: &str =
|
||||
const _CRYSTAL_LOGO: &str =
|
||||
"\n $$$$$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$\\
|
||||
$$ __$$\\ $$ | $$ $$ __$$\\$$ __$$\\
|
||||
$$ / \\__|$$$$$$\\ $$\\ $$\\ $$$$$$$\\$$$$$$\\ $$$$$$\\ $$ $$ / $$ $$ / \\__|
|
||||
@@ -27,6 +27,17 @@ const CRYSTAL_LOGO: &str =
|
||||
|
||||
const ZXQ5_LOGO: &str = "
|
||||
|
||||
/$$$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$
|
||||
|_____ $$ | $$ / $$ /$$__ $$ /$$__ $$ /$$$$
|
||||
/$$/ | $$/ $$/| $$ \\ $$| $$ \\__/ /$$ /$$|_ $$
|
||||
/$$/ \\ $$$$/ | $$ | $$| $$$$$$ | $$ /$$/ | $$
|
||||
/$$/ >$$ $$ | $$ | $$|\\____ $$ \\ $$/$$/ | $$
|
||||
/$$/ /$$/\\ $$| $$/$$ $$ /$$ \\ $$ \\ $$$/ | $$
|
||||
/$$$$$$$$| $$ \\ $$| $$$$$$/| $$$$$$/ \\ $/ /$$$$$$
|
||||
|________/|__/ |__/ \\____ $$$ \\______/ \\_/ |______/
|
||||
";
|
||||
const _ZXQ5_LOGO_OLD: &str = "
|
||||
|
||||
/$$$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$$$$$ /$$
|
||||
|_____ $$ | $$ / $$ /$$__ $$ /$$__ $$ /$$__ $$ /$$$$
|
||||
/$$/ | $$/ $$/| $$ \\ $$| $$ \\ $$| $$ \\__/ /$$ /$$|_ $$
|
||||
@@ -51,7 +62,7 @@ impl Application for CrystalFetch {
|
||||
|
||||
Screen::clear();
|
||||
|
||||
let logo_string = CRYSTAL_LOGO;
|
||||
let logo_string = ZXQ5_LOGO;
|
||||
let info_string = format!(
|
||||
" [ OS » {}
|
||||
[ BUILD » {}
|
||||
|
||||
@@ -4,6 +4,9 @@ use crate::std::io::{Color, ColorCode, Display, KeyStroke, Screen};
|
||||
use crate::std::render::{ColouredChar, Frame, Position, RenderError};
|
||||
use crate::user::lib::libgui::cg_core::CgComponent;
|
||||
|
||||
|
||||
|
||||
|
||||
use alloc::format;
|
||||
use alloc::{string::{String, ToString}, vec::Vec, boxed::Box};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::std::application::Error;
|
||||
use crate::std::application::Error::ApplicationError;
|
||||
use crate::std::render::{ColouredChar, Dimensions, Frame, Position, RenderError};
|
||||
use crate::std::io::{Color, ColorCode, Display, KeyStroke, Screen, Stdin};
|
||||
use crate::std::io::{Color, ColorCode, Display, KeyStroke, Stdin};
|
||||
use crate::std::random::Random;
|
||||
use crate::system::std::application::Application;
|
||||
use crate::user::lib::libgui::cg_core::{CgComponent, Widget};
|
||||
@@ -52,8 +52,8 @@ impl Application for Game {
|
||||
timer: GameTimer::new(),
|
||||
}
|
||||
}
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||
let d = Display::borrow();
|
||||
async fn run(&mut self, _args: Vec<String>) -> Result<(), Error> {
|
||||
let _d = Display::borrow();
|
||||
|
||||
let mut container_data =
|
||||
CgContainer::new(Position::new(0, 0), Dimensions::new(80, 25), true);
|
||||
@@ -147,7 +147,7 @@ impl Game {
|
||||
self.timer.advance();
|
||||
|
||||
let game_update_delay = 5.0 / self.gamespeed;
|
||||
let enemy_spawn_time = match self.difficulty_idx {
|
||||
let _enemy_spawn_time = match self.difficulty_idx {
|
||||
1 => 10,
|
||||
2 => 10,
|
||||
3 => 10,
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod gameoflife;
|
||||
pub mod crystalrpg;
|
||||
pub mod pong;
|
||||
pub mod snake;
|
||||
pub mod paper_rs;
|
||||
@@ -0,0 +1,4 @@
|
||||
/// this game is basically a ripoff of paper.io
|
||||
pub mod paper;
|
||||
|
||||
pub use paper::GameBoard;
|
||||
@@ -0,0 +1,464 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::{boxed::Box, format, string::String, vec::Vec, vec};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{std::{self, application::{Application, Error}, io::{Color, ColorCode, Display, KeyStroke, Stdin}, render::{ColouredChar, Dimensions, Frame, Position, RenderError}, time}, user::{bin::games::asteroids::Game, lib::libgui::cg_core::CgComponent}};
|
||||
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Solid(u8, bool),
|
||||
Tail(u8, bool),
|
||||
Head(u8, bool),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Player {
|
||||
id: u8,
|
||||
alive: bool,
|
||||
position: (i32, i32),
|
||||
ai_ticks: u32, // Ticks until next direction change
|
||||
ai_direction: (i32, i32), // Current direction for AI
|
||||
}
|
||||
|
||||
pub struct GameBoard {
|
||||
board: [[Cell; 80]; 25],
|
||||
players: [Player; 6],
|
||||
max_territory: u32, // Track maximum territory captured
|
||||
current_territory: u32, // Track current territory
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Application for GameBoard {
|
||||
fn new() -> GameBoard {
|
||||
GameBoard {
|
||||
board: [[Cell::Empty; 80]; 25],
|
||||
players: [
|
||||
Player { id: 0, alive: true, position: (10, 10), ai_ticks: 0, ai_direction: (1, 0) },
|
||||
Player { id: 1, alive: true, position: (70, 10), ai_ticks: 5, ai_direction: (-1, 0) },
|
||||
Player { id: 2, alive: true, position: (10, 15), ai_ticks: 10, ai_direction: (1, 0) },
|
||||
Player { id: 3, alive: true, position: (70, 15), ai_ticks: 15, ai_direction: (-1, 0) },
|
||||
Player { id: 4, alive: true, position: (35, 5), ai_ticks: 20, ai_direction: (0, 1) },
|
||||
Player { id: 5, alive: true, position: (35, 20), ai_ticks: 25, ai_direction: (0, -1) },
|
||||
],
|
||||
max_territory: 0,
|
||||
current_territory: 0,
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
|
||||
let _display = Display::borrow();
|
||||
|
||||
'outer: loop {
|
||||
|
||||
self.players.clone().into_iter().for_each(|p| {
|
||||
self.move_player(p.id, 0, 0);
|
||||
});
|
||||
|
||||
let mut player_direction: (i32, i32) = (0, 0);
|
||||
|
||||
// player controls player 1.
|
||||
loop {
|
||||
time::wait(0.1);
|
||||
|
||||
// first get player input
|
||||
if let Some(keystroke) = Stdin::try_keystroke() {
|
||||
match keystroke {
|
||||
KeyStroke::Up => player_direction = (0, -1),
|
||||
KeyStroke::Down => player_direction = (0, 1),
|
||||
KeyStroke::Left => player_direction = (-1, 0),
|
||||
KeyStroke::Right => player_direction = (1, 0),
|
||||
KeyStroke::Char('w') | KeyStroke::Char('W') => player_direction = (0, -1),
|
||||
KeyStroke::Char('s') | KeyStroke::Char('S') => player_direction = (0, 1),
|
||||
KeyStroke::Char('a') | KeyStroke::Char('A') => player_direction = (-1, 0),
|
||||
KeyStroke::Char('d') | KeyStroke::Char('D') => player_direction = (1, 0),
|
||||
KeyStroke::Char('`') => break 'outer,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
// perform movement commands
|
||||
self.move_player(0, player_direction.0, player_direction.1);
|
||||
|
||||
self.update_ai_players();
|
||||
|
||||
self.run_fill_algorithm();
|
||||
|
||||
self.render().unwrap().write_to_screen().unwrap();
|
||||
|
||||
if !self.players[0].alive {
|
||||
self.render_end_screen().await.unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for any key to exit
|
||||
match Stdin::keystroke().await {
|
||||
KeyStroke::Char('`') => break 'outer,
|
||||
_ => self.reset(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl CgComponent for GameBoard {
|
||||
fn render(&self) -> Result<Frame, RenderError> {
|
||||
let mut frame = Frame::new(Position::new(0, 0), Dimensions::new(80, 25))?;
|
||||
|
||||
for i in 0..25 {
|
||||
for j in 0..80 {
|
||||
|
||||
let character: char;
|
||||
let pid: Option<u8>;
|
||||
|
||||
match self.board[i][j] {
|
||||
Cell::Empty => { character = ' '; pid = None; }
|
||||
Cell::Solid(p, _) => { character = '░'; pid = Some(p); }
|
||||
Cell::Tail(p, _) => { character = '▒'; pid = Some(p); }
|
||||
Cell::Head(p, _) => { character = '▓'; pid = Some(p); }
|
||||
}
|
||||
|
||||
let colour = if let Some(p) = pid {
|
||||
match p {
|
||||
0 => ColorCode::new(Color::Green, Color::Black),
|
||||
1 => ColorCode::new(Color::Blue, Color::Black),
|
||||
2 => ColorCode::new(Color::Red, Color::Black),
|
||||
3 => ColorCode::new(Color::Yellow, Color::Black),
|
||||
4 => ColorCode::new(Color::Magenta, Color::Black),
|
||||
_ => ColorCode::new(Color::Cyan, Color::Black),
|
||||
}
|
||||
} else {
|
||||
ColorCode::new(Color::White, Color::Black)
|
||||
};
|
||||
|
||||
frame[i][j] = ColouredChar {
|
||||
character,
|
||||
colour,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn core::any::Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
}
|
||||
|
||||
impl GameBoard {
|
||||
const UP: (i32, i32) = (0, -1);
|
||||
const DOWN: (i32, i32) = (0, 1);
|
||||
const LEFT: (i32, i32) = (-1, 0);
|
||||
const RIGHT: (i32, i32) = (1, 0);
|
||||
const DIRS: [(i32, i32); 8] = [
|
||||
Self::UP, Self::DOWN, Self::LEFT, Self::RIGHT, // only cardinal directions
|
||||
(1, 1), (1, -1), (-1, 1), (-1, -1),
|
||||
];
|
||||
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.board = [[Cell::Empty; 80]; 25];
|
||||
self.players = [
|
||||
Player { id: 0, alive: true, position: (10, 10), ai_ticks: 0, ai_direction: (1, 0) },
|
||||
Player { id: 1, alive: true, position: (70, 10), ai_ticks: 5, ai_direction: (-1, 0) },
|
||||
Player { id: 2, alive: true, position: (10, 15), ai_ticks: 10, ai_direction: (1, 0) },
|
||||
Player { id: 3, alive: true, position: (70, 15), ai_ticks: 15, ai_direction: (-1, 0) },
|
||||
Player { id: 4, alive: true, position: (35, 5), ai_ticks: 20, ai_direction: (0, 1) },
|
||||
Player { id: 5, alive: true, position: (35, 20), ai_ticks: 25, ai_direction: (0, -1) },
|
||||
];
|
||||
self.max_territory = 0;
|
||||
self.current_territory = 0;
|
||||
}
|
||||
|
||||
fn respawn_ai_player(&mut self, player_id: u8) {
|
||||
// Try up to 10 times to find a valid spawn position
|
||||
for _ in 0..10 {
|
||||
let x = std::random::Random::int(5, 75) as i32;
|
||||
let y = std::random::Random::int(5, 20) as i32;
|
||||
|
||||
// Check if position is empty or not in someone's territory
|
||||
if let Cell::Empty = self.board[y as usize][x as usize] {
|
||||
let dir_idx = std::random::Random::int(0, 3) as usize;
|
||||
self.players[player_id as usize] = Player {
|
||||
id: player_id,
|
||||
alive: true,
|
||||
position: (x, y),
|
||||
ai_ticks: std::random::Random::int(5, 10) as u32,
|
||||
ai_direction: Self::DIRS[dir_idx],
|
||||
};
|
||||
// Place the head at spawn position
|
||||
self.board[y as usize][x as usize] = Cell::Head(player_id, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find a spot after 10 tries, just keep the player dead
|
||||
self.players[player_id as usize].alive = false;
|
||||
}
|
||||
|
||||
fn count_territory(&self) -> u32 {
|
||||
let mut count = 0;
|
||||
for row in &self.board {
|
||||
for cell in row {
|
||||
if let Cell::Solid(0, true) = cell {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
async fn render_end_screen(&mut self) -> Result<(), RenderError> {
|
||||
let mut frame = self.render()?;
|
||||
|
||||
// Game Over message
|
||||
let game_over_text = "Game Over!";
|
||||
let center_y = 12;
|
||||
let center_x = 40 - (game_over_text.len() / 2) as u16;
|
||||
|
||||
// Draw the game over text
|
||||
for (i, c) in game_over_text.chars().enumerate() {
|
||||
let pos = center_x + i as u16;
|
||||
frame[center_y][pos as usize] = ColouredChar {
|
||||
character: c,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
};
|
||||
}
|
||||
|
||||
// Show player's score
|
||||
let score_text = format!("Score: {}", self.current_territory);
|
||||
let score_x = 40 - (score_text.len() / 2) as u16;
|
||||
for (i, c) in score_text.chars().enumerate() {
|
||||
let pos = score_x + i as u16;
|
||||
frame[center_y + 1][pos as usize] = ColouredChar {
|
||||
character: c,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
};
|
||||
}
|
||||
|
||||
// Show restart instruction
|
||||
let restart_text = "Press any key to restart";
|
||||
let restart_x = 40 - (restart_text.len() / 2) as u16;
|
||||
for (i, c) in restart_text.chars().enumerate() {
|
||||
let pos = restart_x + i as u16;
|
||||
frame[center_y + 2][pos as usize] = ColouredChar {
|
||||
character: c,
|
||||
colour: ColorCode::new(Color::White, Color::Black),
|
||||
};
|
||||
}
|
||||
|
||||
frame.write_to_screen()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_player(&mut self, playerid: u8, dx: i32, dy: i32) {
|
||||
let (ox, oy) = self.players[playerid as usize].position;
|
||||
let nx = ox + dx;
|
||||
let ny = oy + dy;
|
||||
|
||||
// Bounds checking
|
||||
if nx < 0 || nx >= 80 || ny < 0 || ny >= 25 {
|
||||
if playerid == 0 {
|
||||
self.players[playerid as usize].alive = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for collisions with tails
|
||||
let mut player_to_eliminate = None;
|
||||
|
||||
// check if player hit an enemy's tail
|
||||
match self.board[ny as usize][nx as usize] {
|
||||
Cell::Tail(id, _) => {
|
||||
if id != playerid {
|
||||
// Hit other player's tail
|
||||
player_to_eliminate = Some(id);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Convert old head to tail
|
||||
if let Cell::Head(id, owned) = self.board[oy as usize][ox as usize] {
|
||||
if owned {
|
||||
self.board[oy as usize][ox as usize] = Cell::Solid(id, true);
|
||||
} else {
|
||||
self.board[oy as usize][ox as usize] = Cell::Tail(id, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Place new head
|
||||
if let Cell::Solid(p, _) = self.board[ny as usize][nx as usize] {
|
||||
self.board[ny as usize][nx as usize] = Cell::Head(playerid, p == playerid);
|
||||
} else {
|
||||
self.board[ny as usize][nx as usize] = Cell::Head(playerid, false);
|
||||
}
|
||||
|
||||
// Update the player's position
|
||||
self.players[playerid as usize].position = (nx, ny);
|
||||
|
||||
// Handle elimination if needed
|
||||
if let Some(pid) = player_to_eliminate {
|
||||
self.players[pid as usize].alive = false;
|
||||
// Clear territory and respawn if AI
|
||||
if pid != 0 {
|
||||
self.clear_player_territory(pid);
|
||||
self.respawn_ai_player(pid);
|
||||
} else {
|
||||
// Update max territory before game over
|
||||
self.current_territory = self.count_territory();
|
||||
self.max_territory = self.max_territory.max(self.current_territory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ai_players(&mut self) {
|
||||
// Skip player 0 (human player)
|
||||
for i in 1..6 {
|
||||
if !self.players[i].alive {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decrease tick counter
|
||||
if self.players[i].ai_ticks > 0 {
|
||||
self.players[i].ai_ticks -= 1;
|
||||
} else {
|
||||
// Time to change direction
|
||||
let random_dir = std::random::Random::int(0, 3);
|
||||
self.players[i].ai_direction = Self::DIRS[random_dir as usize]; //
|
||||
self.players[i].ai_ticks = 5 + (random_dir % 5) as u32; // Random ticks between 5-10
|
||||
}
|
||||
|
||||
// Move in current direction
|
||||
let dir = self.players[i].ai_direction;
|
||||
self.move_player(i as u8, dir.0, dir.1);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_fill_algorithm(&mut self) {
|
||||
// Pre-allocate a single reusable grid
|
||||
let mut fill_grid = [[false; 80]; 25];
|
||||
let mut queue = Vec::with_capacity(80 * 25);
|
||||
|
||||
// Process for each alive player
|
||||
for player in self.players.iter().filter(|p| p.alive) {
|
||||
let pid = player.id;
|
||||
|
||||
// Reset grid
|
||||
for row in fill_grid.iter_mut() {
|
||||
row.fill(false);
|
||||
}
|
||||
|
||||
// Mark all player lines as walls but don't fill them yet
|
||||
for y in 0..25 {
|
||||
for x in 0..80 {
|
||||
if let Cell::Head(p, _) | Cell::Tail(p, _) | Cell::Solid(p, _) = self.board[y][x] {
|
||||
if p == pid {
|
||||
fill_grid[y][x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear queue for reuse
|
||||
queue.clear();
|
||||
|
||||
// Add edge cells to queue
|
||||
for x in 0..80 {
|
||||
if !fill_grid[0][x] {
|
||||
queue.push((0, x));
|
||||
fill_grid[0][x] = true;
|
||||
}
|
||||
if !fill_grid[24][x] {
|
||||
queue.push((24, x));
|
||||
fill_grid[24][x] = true;
|
||||
}
|
||||
}
|
||||
for y in 1..24 {
|
||||
if !fill_grid[y][0] {
|
||||
queue.push((y, 0));
|
||||
fill_grid[y][0] = true;
|
||||
}
|
||||
if !fill_grid[y][79] {
|
||||
queue.push((y, 79));
|
||||
fill_grid[y][79] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark cells that are reachable from outside
|
||||
while let Some((y, x)) = queue.pop() {
|
||||
// Mark current cell as visited in the fill grid
|
||||
fill_grid[y][x] = true;
|
||||
|
||||
// Check all four directions
|
||||
for (dy, dx) in Self::DIRS {
|
||||
let ny = (y as i32 + dy) as usize;
|
||||
let nx = (x as i32 + dx) as usize;
|
||||
|
||||
if ny < 25 && nx < 80 && !fill_grid[ny][nx] {
|
||||
fill_grid[ny][nx] = true;
|
||||
queue.push((ny, nx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we found any cells to fill
|
||||
let mut found_territory = false;
|
||||
'outer: for y in 0..25 {
|
||||
for x in 0..80 {
|
||||
if !fill_grid[y][x] && matches!(self.board[y][x], Cell::Empty) {
|
||||
found_territory = true;
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only convert tails if we actually filled some territory
|
||||
if found_territory {
|
||||
// First convert all tails to solid for this player
|
||||
for y in 0..25 {
|
||||
for x in 0..80 {
|
||||
if let Cell::Tail(p, _) = self.board[y][x] {
|
||||
if p == pid {
|
||||
self.board[y][x] = Cell::Solid(pid, true);
|
||||
}
|
||||
}
|
||||
if let Cell::Head(p, _) = self.board[y][x] {
|
||||
if p == pid {
|
||||
self.board[y][x] = Cell::Head(pid, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert all unreached cells to territory
|
||||
for y in 0..25 {
|
||||
for x in 0..80 {
|
||||
if !fill_grid[y][x] {
|
||||
self.board[y][x] = Cell::Solid(pid, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_player_territory(&mut self, player_id: u8) {
|
||||
for y in 0..25 {
|
||||
for x in 0..80 {
|
||||
if let Cell::Head(pid, _) | Cell::Tail(pid, _) | Cell::Solid(pid, _) = self.board[y][x] {
|
||||
if pid == player_id {
|
||||
self.board[y][x] = Cell::Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,10 @@ async fn exec() -> Result<(), Error> {
|
||||
"games/pong" => {
|
||||
pong::Game::new().run(args).await?;
|
||||
}
|
||||
"games/paper.rs" => {
|
||||
let mut game = paper_rs::GameBoard::new();
|
||||
game.run(args).await?;
|
||||
}
|
||||
"serial" => {
|
||||
let c = Serial::reply_char('e');
|
||||
println!("{}", c);
|
||||
|
||||
Reference in New Issue
Block a user