Compare commits
No commits in common. "e8614dd29f4eeef136a063084f6f57cf326a7259" and "7394e9db0b10a52fd291f649237e1b96541fff4f" have entirely different histories.
e8614dd29f
...
7394e9db0b
|
@ -170,7 +170,13 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimax"
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ops"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -180,12 +186,6 @@ dependencies = [
|
||||||
"termion",
|
"termion",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "numtoa"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "minimax"
|
name = "ops"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
28
README.md
28
README.md
|
@ -1,28 +0,0 @@
|
||||||
# Minimax
|
|
||||||
|
|
||||||
|
|
||||||
## Rules
|
|
||||||
|
|
||||||
This game is played in two rounds, starting with an empty eleven-space board. Red always goes first.
|
|
||||||
|
|
||||||
On Red's board (i.e, duing the first round), Red's job is to maximize the value of the expression; Blue's job is to minimize it.
|
|
||||||
Players take turns placing the fourteen symbols `0123456789+-×÷` on the board, with the maximizing player taking the first move.
|
|
||||||
|
|
||||||
A board's syntax must always be valid. Namely, the following rules are enforced:
|
|
||||||
- Each symbol may only be used once
|
|
||||||
- The binary operators `+-×÷` may not be next to one another, and may not be at the end slots.
|
|
||||||
- The unary operator `-` (negative) must have a number as an argument. Therefore, it cannot be left of an operator (like `-×`), and it may not be in the rightmost slot.
|
|
||||||
- `0` may not follow `÷`. This prevents most cases of zero-division, but isn't perfect. `÷-0` will break the game, and `÷0_+` is forbidden despite being valid syntax once the empty slot is filled (for example, with `÷03+`). This is done to simplyify game logic, and might be improved later.
|
|
||||||
|
|
||||||
|
|
||||||
## Building & Running
|
|
||||||
|
|
||||||
As always, run this project with `cargo run`. The app takes one argument by default: the name of the blue player. This can be any of the following:
|
|
||||||
- `human`: Play against a human
|
|
||||||
- `random`: Play against a random agent (very easy)
|
|
||||||
- `chase`: Play against a simple extremum-chasing agent (easy)
|
|
||||||
- `diffuse`: Play against a slightly more intellegent extremum chaser (medium)
|
|
||||||
|
|
||||||
For example, `cargo run -- random` will play a game against a random player.
|
|
||||||
|
|
||||||
Additional options are available, see `cargo run -- --help`.
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use super::{Agent, Chase, MaximizerAgent, MinimizerAgent, Random};
|
use super::{Agent, MaximizerAgent, MinimizerAgent, Random, SimpleMinimax};
|
||||||
use crate::{
|
use crate::{
|
||||||
board::{Board, PlayerAction},
|
board::{Board, PlayerAction},
|
||||||
util::{Player, Symb},
|
util::{Player, Symb},
|
||||||
|
@ -94,7 +94,7 @@ impl MinimizerAgent for Diffuse {
|
||||||
Ok(self.step_symb(board, *symb))
|
Ok(self.step_symb(board, *symb))
|
||||||
} else {
|
} else {
|
||||||
// No symbols available, play a random number
|
// No symbols available, play a random number
|
||||||
Chase::new(self.player).step_min(board)
|
SimpleMinimax::new(self.player).step_min(board)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ impl MaximizerAgent for Diffuse {
|
||||||
Ok(self.step_symb(board, *symb))
|
Ok(self.step_symb(board, *symb))
|
||||||
} else {
|
} else {
|
||||||
// No symbols available, play a random number
|
// No symbols available, play a random number
|
||||||
Chase::new(self.player).step_max(board)
|
SimpleMinimax::new(self.player).step_max(board)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@ use crate::{
|
||||||
util::{Player, Symb},
|
util::{Player, Symb},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Chase {
|
pub struct SimpleMinimax {
|
||||||
player: Player,
|
player: Player,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chase {
|
impl SimpleMinimax {
|
||||||
pub fn new(player: Player) -> Self {
|
pub fn new(player: Player) -> Self {
|
||||||
Self { player }
|
Self { player }
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,13 @@ impl Chase {
|
||||||
return Random::new(self.player).step_min(board);
|
return Random::new(self.player).step_min(board);
|
||||||
}
|
}
|
||||||
|
|
||||||
let t = free_slots_by_influence(board);
|
let t = free_slots_by_influence(&board);
|
||||||
if t.is_none() {
|
if t.is_none() {
|
||||||
bail!("could not compute next move!")
|
bail!("could not compute next move!")
|
||||||
}
|
}
|
||||||
let t = t.unwrap();
|
let t = t.unwrap();
|
||||||
|
|
||||||
if t.is_empty() {
|
if t.len() == 0 {
|
||||||
return Random::new(self.player).step_min(board);
|
return Random::new(self.player).step_min(board);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,11 +63,13 @@ impl Chase {
|
||||||
} else {
|
} else {
|
||||||
available_numbers[available_numbers.len() - 1 - offset]
|
available_numbers[available_numbers.len() - 1 - offset]
|
||||||
}
|
}
|
||||||
} else if val <= 0.0 {
|
} else {
|
||||||
|
if val <= 0.0 {
|
||||||
available_numbers[offset]
|
available_numbers[offset]
|
||||||
} else {
|
} else {
|
||||||
available_numbers[available_numbers.len() - 1 - offset]
|
available_numbers[available_numbers.len() - 1 - offset]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
offset += 1;
|
offset += 1;
|
||||||
}
|
}
|
||||||
|
@ -79,9 +81,9 @@ impl Chase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Agent for Chase {
|
impl Agent for SimpleMinimax {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"Chase"
|
"Minimax"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn player(&self) -> Player {
|
fn player(&self) -> Player {
|
||||||
|
@ -89,13 +91,13 @@ impl Agent for Chase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MinimizerAgent for Chase {
|
impl MinimizerAgent for SimpleMinimax {
|
||||||
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
||||||
self.step(board, true)
|
self.step(board, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaximizerAgent for Chase {
|
impl MaximizerAgent for SimpleMinimax {
|
||||||
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
||||||
self.step(board, false)
|
self.step(board, false)
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
mod chase;
|
|
||||||
mod diffuse;
|
mod diffuse;
|
||||||
mod human;
|
mod human;
|
||||||
|
mod minimax;
|
||||||
mod random;
|
mod random;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub use chase::Chase;
|
|
||||||
pub use diffuse::Diffuse;
|
pub use diffuse::Diffuse;
|
||||||
pub use human::Human;
|
pub use human::Human;
|
||||||
|
pub use minimax::SimpleMinimax;
|
||||||
pub use random::Random;
|
pub use random::Random;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub fn maximize_value(board: &Board) -> Option<Board> {
|
||||||
.filter(|x| !board.contains(*x))
|
.filter(|x| !board.contains(*x))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let slots = free_slots_by_influence(board)?;
|
let slots = free_slots_by_influence(&board)?;
|
||||||
|
|
||||||
let all_symbols = {
|
let all_symbols = {
|
||||||
// We need this many from the bottom, and this many from the top.
|
// We need this many from the bottom, and this many from the top.
|
||||||
|
@ -92,7 +92,7 @@ pub fn maximize_value(board: &Board) -> Option<Board> {
|
||||||
// equal-weight slots
|
// equal-weight slots
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
(0..s)
|
(0..s)
|
||||||
.map(|_| *a_iter.next().unwrap())
|
.map(|_| a_iter.next().unwrap().clone())
|
||||||
.permutations(s)
|
.permutations(s)
|
||||||
.unique()
|
.unique()
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::fmt::{Display, Write};
|
use std::fmt::Write;
|
||||||
use termion::color::{self, Color};
|
use termion::color::{self, Color};
|
||||||
|
|
||||||
use super::{PlayerAction, TreeElement};
|
use super::{PlayerAction, TreeElement};
|
||||||
|
@ -65,12 +65,14 @@ pub struct Board {
|
||||||
last_placed: Option<usize>,
|
last_placed: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Board {
|
impl ToString for Board {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn to_string(&self) -> String {
|
||||||
for c in self.board {
|
let mut s = String::new();
|
||||||
write!(f, "{}", c.map(|s| s.get_char().unwrap()).unwrap_or('_'))?
|
s.extend(
|
||||||
}
|
self.board
|
||||||
Ok(())
|
.map(|x| x.map(|s| s.to_char().unwrap()).unwrap_or('_')),
|
||||||
|
);
|
||||||
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +369,7 @@ impl Board {
|
||||||
if c == '_' {
|
if c == '_' {
|
||||||
Some(None)
|
Some(None)
|
||||||
} else {
|
} else {
|
||||||
Symb::from_char(c).map(Some)
|
Symb::from_char(c).map(|s| Some(s))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
46
src/cli.rs
46
src/cli.rs
|
@ -1,79 +1,59 @@
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
agents::{Chase, Diffuse, Human, MaximizerAgent, MinimizerAgent, Random},
|
agents::{Diffuse, Human, MaximizerAgent, MinimizerAgent, Random, SimpleMinimax},
|
||||||
util::Player,
|
util::Player,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(about)]
|
#[command(version, about)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
/// The agent that controls the Blue (opponent) player
|
pub red: AgentSelector,
|
||||||
pub blue: AgentSelector,
|
pub blue: AgentSelector,
|
||||||
|
|
||||||
/// The agent that controls the Red (home) player
|
|
||||||
#[arg(long, default_value = "human")]
|
|
||||||
pub red: AgentSelector,
|
|
||||||
|
|
||||||
/// If this is greater than one, repeat the game this many times and print a summary.
|
|
||||||
/// Best used with --silent.
|
|
||||||
#[arg(long, short, default_value = "0")]
|
#[arg(long, short, default_value = "0")]
|
||||||
pub repeat: usize,
|
pub repeat: usize,
|
||||||
|
|
||||||
/// If this is given, do not print boards.
|
|
||||||
/// Good for bulk runs with --repeat, bad for human players.
|
|
||||||
#[arg(long, short, default_value = "false")]
|
#[arg(long, short, default_value = "false")]
|
||||||
pub silent: bool,
|
pub silent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||||
pub enum AgentSelector {
|
pub enum AgentSelector {
|
||||||
/// A human agent. Asks for input.
|
|
||||||
Human,
|
|
||||||
|
|
||||||
/// A random agent (very easy)
|
|
||||||
Random,
|
Random,
|
||||||
|
|
||||||
/// A simple extremum-chaser (easy)
|
|
||||||
Chase,
|
|
||||||
|
|
||||||
/// A smarter extremum-chaser (medium)
|
|
||||||
Diffuse,
|
Diffuse,
|
||||||
|
Minimax,
|
||||||
|
Human,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AgentSelector {
|
impl AgentSelector {
|
||||||
pub fn get_maximizer(&self, player: Player) -> Box<dyn MaximizerAgent> {
|
pub fn to_maxi(&self, player: Player) -> Box<dyn MaximizerAgent> {
|
||||||
match self {
|
match self {
|
||||||
Self::Random => Box::new(Random::new(player)),
|
Self::Random => Box::new(Random::new(player)),
|
||||||
Self::Chase => Box::new(Chase::new(player)),
|
Self::Minimax => Box::new(SimpleMinimax::new(player)),
|
||||||
Self::Diffuse => Box::new(Diffuse::new(player)),
|
Self::Diffuse => Box::new(Diffuse::new(player)),
|
||||||
Self::Human => Box::new(Human::new(player)),
|
Self::Human => Box::new(Human::new(player)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_minimizer(&self, player: Player) -> Box<dyn MinimizerAgent> {
|
pub fn to_mini(&self, player: Player) -> Box<dyn MinimizerAgent> {
|
||||||
match self {
|
match self {
|
||||||
Self::Random => Box::new(Random::new(player)),
|
Self::Random => Box::new(Random::new(player)),
|
||||||
Self::Chase => Box::new(Chase::new(player)),
|
Self::Minimax => Box::new(SimpleMinimax::new(player)),
|
||||||
Self::Diffuse => Box::new(Diffuse::new(player)),
|
Self::Diffuse => Box::new(Diffuse::new(player)),
|
||||||
Self::Human => Box::new(Human::new(player)),
|
Self::Human => Box::new(Human::new(player)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for AgentSelector {
|
impl ToString for AgentSelector {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn to_string(&self) -> String {
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
match self {
|
match self {
|
||||||
Self::Random => "random",
|
Self::Random => "random",
|
||||||
Self::Diffuse => "diffuse",
|
Self::Diffuse => "diffuse",
|
||||||
Self::Chase => "chase",
|
Self::Minimax => "minimax",
|
||||||
Self::Human => "human",
|
Self::Human => "human",
|
||||||
}
|
}
|
||||||
)
|
.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -59,18 +59,11 @@ fn play(
|
||||||
let mut is_first_turn = true;
|
let mut is_first_turn = true;
|
||||||
let mut is_maxi_turn = true;
|
let mut is_maxi_turn = true;
|
||||||
|
|
||||||
let board_label = format!(
|
|
||||||
"{}{:6}{}",
|
|
||||||
color::Fg(color::LightBlack),
|
|
||||||
maxi.player(),
|
|
||||||
color::Fg(color::Reset)
|
|
||||||
);
|
|
||||||
|
|
||||||
while !board.is_done() {
|
while !board.is_done() {
|
||||||
// Print board
|
// Print board
|
||||||
println!(
|
println!(
|
||||||
"\r{}{}{}{}",
|
"\r{}{}{}{}",
|
||||||
board_label,
|
" ".repeat(6),
|
||||||
if is_first_turn { '╓' } else { '║' },
|
if is_first_turn { '╓' } else { '║' },
|
||||||
board.prettyprint()?,
|
board.prettyprint()?,
|
||||||
if is_first_turn { '╖' } else { '║' },
|
if is_first_turn { '╖' } else { '║' },
|
||||||
|
@ -106,8 +99,8 @@ fn play(
|
||||||
is_maxi_turn = !is_maxi_turn;
|
is_maxi_turn = !is_maxi_turn;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\r{}║{}║", board_label, board.prettyprint()?);
|
println!("\r{}║{}║", " ".repeat(6), board.prettyprint()?);
|
||||||
println!("\r{}╙{}╜", board_label, " ".repeat(board.size()));
|
println!("\r{}╙{}╜", " ".repeat(6), " ".repeat(board.size()));
|
||||||
Ok(board)
|
Ok(board)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,8 +112,8 @@ fn main() -> Result<()> {
|
||||||
let mut blue_wins = 0f32;
|
let mut blue_wins = 0f32;
|
||||||
|
|
||||||
for _ in 0..cli.repeat {
|
for _ in 0..cli.repeat {
|
||||||
let mut maxi = cli.red.get_maximizer(Player::Red);
|
let mut maxi = cli.red.to_maxi(Player::Red);
|
||||||
let mut mini = cli.blue.get_minimizer(Player::Blue);
|
let mut mini = cli.blue.to_mini(Player::Blue);
|
||||||
let red_board = match if cli.silent {
|
let red_board = match if cli.silent {
|
||||||
play_silent(&mut *maxi, &mut *mini)
|
play_silent(&mut *maxi, &mut *mini)
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,8 +126,8 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut maxi = cli.blue.get_maximizer(Player::Blue);
|
let mut maxi = cli.blue.to_maxi(Player::Blue);
|
||||||
let mut mini = cli.red.get_minimizer(Player::Red);
|
let mut mini = cli.red.to_mini(Player::Red);
|
||||||
let blue_board = match if cli.silent {
|
let blue_board = match if cli.silent {
|
||||||
play_silent(&mut *maxi, &mut *mini)
|
play_silent(&mut *maxi, &mut *mini)
|
||||||
} else {
|
} else {
|
||||||
|
@ -161,16 +154,16 @@ fn main() -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"Red win rate: {:.2} ({})",
|
"Red win rate: {:.2} ({})",
|
||||||
red_wins / cli.repeat as f32,
|
red_wins / cli.repeat as f32,
|
||||||
cli.red,
|
cli.red.to_string(),
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"Blue win rate: {:.2} ({}) ",
|
"Blue win rate: {:.2} ({}) ",
|
||||||
blue_wins / cli.repeat as f32,
|
blue_wins / cli.repeat as f32,
|
||||||
cli.blue,
|
cli.blue.to_string(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let mut maxi = cli.red.get_maximizer(Player::Red);
|
let mut maxi = cli.red.to_maxi(Player::Red);
|
||||||
let mut mini = cli.blue.get_minimizer(Player::Blue);
|
let mut mini = cli.blue.to_mini(Player::Blue);
|
||||||
let red_board = if cli.silent {
|
let red_board = if cli.silent {
|
||||||
play_silent(&mut *maxi, &mut *mini)
|
play_silent(&mut *maxi, &mut *mini)
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,8 +187,8 @@ fn main() -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut maxi = cli.blue.get_maximizer(Player::Blue);
|
let mut maxi = cli.blue.to_maxi(Player::Blue);
|
||||||
let mut mini = cli.red.get_minimizer(Player::Red);
|
let mut mini = cli.red.to_mini(Player::Red);
|
||||||
let blue_board = if cli.silent {
|
let blue_board = if cli.silent {
|
||||||
play_silent(&mut *maxi, &mut *mini)
|
play_silent(&mut *maxi, &mut *mini)
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,7 +220,7 @@ fn main() -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"\r\n{}Red ({}){} wins!",
|
"\r\n{}Red ({}){} wins!",
|
||||||
color::Fg(Player::Red.color()),
|
color::Fg(Player::Red.color()),
|
||||||
cli.red,
|
cli.red.to_string(),
|
||||||
color::Fg(color::Reset),
|
color::Fg(color::Reset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -235,7 +228,7 @@ fn main() -> Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"\r\n{}Blue ({}){} wins!",
|
"\r\n{}Blue ({}){} wins!",
|
||||||
color::Fg(Player::Blue.color()),
|
color::Fg(Player::Blue.color()),
|
||||||
cli.blue,
|
cli.blue.to_string(),
|
||||||
color::Fg(color::Reset),
|
color::Fg(color::Reset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl Symb {
|
||||||
self == &Self::Minus
|
self == &Self::Minus
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_char(&self) -> Option<char> {
|
pub const fn to_char(&self) -> Option<char> {
|
||||||
match self {
|
match self {
|
||||||
Self::Plus => Some('+'),
|
Self::Plus => Some('+'),
|
||||||
Self::Minus => Some('-'),
|
Self::Minus => Some('-'),
|
||||||
|
|
Loading…
Reference in New Issue