Documentation & Tweaks

master
Mark 2024-03-05 14:22:30 -08:00
parent 7394e9db0b
commit b344ae359b
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
3 changed files with 58 additions and 8 deletions

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# Symbol Strike
## 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)
- `minimax`: 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`.

View File

@ -6,24 +6,39 @@ use crate::{
}; };
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about)] #[command(about)]
pub struct Cli { pub struct Cli {
pub red: AgentSelector, /// The agent that controls the Blue (opponent) player
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 {
Random, /// A human agent. Asks for input.
Diffuse,
Minimax,
Human, Human,
/// A random agent (very easy)
Random,
/// A simple extremum-chaser (easy)
Minimax,
/// A smarter extremum-chaser (medium)
Diffuse,
} }
impl AgentSelector { impl AgentSelector {

View File

@ -59,11 +59,18 @@ 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().to_string(),
color::Fg(color::Reset)
);
while !board.is_done() { while !board.is_done() {
// Print board // Print board
println!( println!(
"\r{}{}{}{}", "\r{}{}{}{}",
" ".repeat(6), board_label,
if is_first_turn { '╓' } else { '║' }, if is_first_turn { '╓' } else { '║' },
board.prettyprint()?, board.prettyprint()?,
if is_first_turn { '╖' } else { '║' }, if is_first_turn { '╖' } else { '║' },
@ -99,8 +106,8 @@ fn play(
is_maxi_turn = !is_maxi_turn; is_maxi_turn = !is_maxi_turn;
} }
println!("\r{}{}", " ".repeat(6), board.prettyprint()?); println!("\r{}{}", board_label, board.prettyprint()?);
println!("\r{}{}", " ".repeat(6), " ".repeat(board.size())); println!("\r{}{}", board_label, " ".repeat(board.size()));
Ok(board) Ok(board)
} }