Skip to content

Commit

Permalink
refactor: make most games use natural move
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoDog896 committed May 28, 2024
1 parent 2e0863a commit fedcca0
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 93 deletions.
32 changes: 21 additions & 11 deletions crates/games/src/chomp/cli.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
use std::{collections::HashMap, env::args};
use std::collections::HashMap;

use clap::Args;
use game_solver::{game::Game, move_scores};

use crate::chomp::Chomp;

pub fn main() {
let mut game = Chomp::new(6, 4);
use super::ChompMove;

#[derive(Args)]
pub struct ChompArgs {
// TODO: width default = 6, height default = 4
/// The height of the game
height: usize,
/// The width of the game
width: usize,
/// Chomp moves, ordered as x1-y1 x2-y2 ...
#[arg(value_parser = clap::value_parser!(ChompMove))]
moves: Vec<ChompMove>
}

// parse every move in args, e.g. 0-0 1-1 in args
args().skip(1).for_each(|arg| {
let numbers: Vec<usize> = arg
.split('-')
.map(|num| num.parse::<usize>().expect("Not a number!"))
.collect();
pub fn main(args: ChompArgs) {
let mut game = Chomp::new(args.width, args.height);

game.make_move(&(numbers[0], numbers[1]));
// parse every move in args, e.g. 0-0 1-1 in args
args.moves.iter().for_each(|arg| {
game.make_move(arg);
});

print!("{}", game);
Expand All @@ -34,7 +44,7 @@ pub fn main() {
println!("\n\nBest moves @ score {}:", score);
current_move_score = Some(score);
}
print!("({}, {}), ", game_move.0, game_move.1);
print!("({}), ", game_move);
}
println!();
}
Expand Down
62 changes: 33 additions & 29 deletions crates/games/src/chomp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! The bottom right square is poisoned, and the players take turns eating squares.
//! Every square they eat, every square to the right and above it is also eaten (inclusively)
//!
//! This is a flipped version of the traiditional [Chomp](https://en.wikipedia.org/wiki/Chomp) game.
//! This is a flipped version of the traditional [Chomp](https://en.wikipedia.org/wiki/Chomp) game.
//!
//! This is not the best example for analysis via a combinatorial game, as not only is it
//! impartial (making it analyzable via the Sprague-Grundy theorem), but it is also trivially
Expand All @@ -21,6 +21,8 @@ use std::{
hash::Hash,
};

use crate::util::move_natural::NaturalMove;

#[derive(Clone, Hash, Eq, PartialEq)]
struct Chomp {
width: usize,
Expand All @@ -44,8 +46,10 @@ impl Chomp {
}
}

pub type ChompMove = NaturalMove<2>;

impl Game for Chomp {
type Move = (usize, usize);
type Move = ChompMove;
type Iter<'a> = std::vec::IntoIter<Self::Move>;
type Player = ZeroSumPlayer;

Expand All @@ -66,9 +70,9 @@ impl Game for Chomp {
}

fn make_move(&mut self, m: &Self::Move) -> bool {
if *self.board.get(m.0, m.1).unwrap() {
for i in m.0..self.width {
for j in 0..=m.1 {
if *self.board.get(m.0[0], m.0[1]).unwrap() {
for i in m.0[0]..self.width {
for j in 0..=m.0[1] {
self.board.set(i, j, false).unwrap();
}
}
Expand All @@ -84,7 +88,7 @@ impl Game for Chomp {
for i in (0..self.height).rev() {
for j in 0..self.width {
if *self.board.get(j, i).unwrap() {
moves.push((j, i));
moves.push(NaturalMove([j, i]));
}
}
}
Expand Down Expand Up @@ -137,29 +141,29 @@ mod tests {
move_scores.sort();

let mut new_scores = vec![
((2, 2), 13),
((5, 0), -12),
((4, 0), -12),
((3, 0), -12),
((2, 0), -12),
((0, 0), -12),
((5, 1), -12),
((4, 1), -12),
((3, 1), -12),
((2, 1), -12),
((0, 1), -12),
((5, 2), -12),
((4, 2), -12),
((3, 2), -12),
((5, 3), -12),
((1, 0), -16),
((1, 1), -16),
((1, 2), -16),
((4, 3), -16),
((3, 3), -16),
((2, 3), -16),
((0, 2), -22),
((1, 3), -22),
(NaturalMove([2, 2]), 13),
(NaturalMove([5, 0]), -12),
(NaturalMove([4, 0]), -12),
(NaturalMove([3, 0]), -12),
(NaturalMove([2, 0]), -12),
(NaturalMove([0, 0]), -12),
(NaturalMove([5, 1]), -12),
(NaturalMove([4, 1]), -12),
(NaturalMove([3, 1]), -12),
(NaturalMove([2, 1]), -12),
(NaturalMove([0, 1]), -12),
(NaturalMove([5, 2]), -12),
(NaturalMove([4, 2]), -12),
(NaturalMove([3, 2]), -12),
(NaturalMove([5, 3]), -12),
(NaturalMove([1, 0]), -16),
(NaturalMove([1, 1]), -16),
(NaturalMove([1, 2]), -16),
(NaturalMove([4, 3]), -16),
(NaturalMove([3, 3]), -16),
(NaturalMove([2, 3]), -16),
(NaturalMove([0, 2]), -22),
(NaturalMove([1, 3]), -22),
];

new_scores.sort();
Expand Down
20 changes: 19 additions & 1 deletion crates/games/src/domineering/cli.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
use std::{collections::HashMap, env::args};
use std::{collections::HashMap, env::args, fmt::{Display, Formatter}};

use game_solver::{game::Game, move_scores};

use crate::domineering::DomineeringGame;

use super::Domineering;

impl<const WIDTH: usize, const HEIGHT: usize> Display for Domineering<WIDTH, HEIGHT> {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
for i in 0..HEIGHT {
for j in 0..WIDTH {
if *self.board.get(j, i).unwrap() {
write!(f, "X")?;
} else {
write!(f, ".")?;
}
}
writeln!(f)?;
}
Ok(())
}
}

pub fn main() {
let mut game = DomineeringGame::new();

Expand Down
21 changes: 1 addition & 20 deletions crates/games/src/domineering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ pub mod cli;
use array2d::Array2D;
use game_solver::game::{Game, ZeroSumPlayer};

use std::{
fmt::{Display, Formatter},
hash::Hash,
};
use std::hash::Hash;

#[derive(Clone, Hash, Eq, PartialEq)]
struct Domineering<const WIDTH: usize, const HEIGHT: usize> {
Expand Down Expand Up @@ -111,22 +108,6 @@ impl<const WIDTH: usize, const HEIGHT: usize> Game for Domineering<WIDTH, HEIGHT
}
}

impl<const WIDTH: usize, const HEIGHT: usize> Display for Domineering<WIDTH, HEIGHT> {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
for i in 0..HEIGHT {
for j in 0..WIDTH {
if *self.board.get(j, i).unwrap() {
write!(f, "X")?;
} else {
write!(f, ".")?;
}
}
writeln!(f)?;
}
Ok(())
}
}

// n, m
type DomineeringGame = Domineering<5, 5>;

Expand Down
34 changes: 21 additions & 13 deletions crates/games/src/nim/cli.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
use std::{env::args, fmt::{Display, Formatter}};
use std::fmt::{Display, Formatter};

use clap::Args;
use game_solver::{game::Game, par_move_scores};

use crate::{nim::Nim, util::move_natural::NaturalMove};
use crate::nim::Nim;

use super::NimMove;

impl Display for Nim {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
for (i, &heap) in self.heaps.iter().enumerate() {
writeln!(f, "Heap {}: {}", i, heap)?;
}

Ok(())
}
}

pub fn main() {
#[derive(Args)]
pub struct NimArgs {
/// The configuration of the game. For example, 3,5,7
/// creates a Nimbers game that has three heaps, where each
/// heap has 3, 5, and 7 objects respectively
configuration: String,
/// Nim moves, ordered as x1-y1 x2-y2 ...
#[arg(value_parser = clap::value_parser!(NimMove))]
moves: Vec<NimMove>
}

pub fn main(args: NimArgs) {
// parse the original configuration of the game from args
// e.g. 3,5,7 for 3 heaps with 3, 5, and 7 objects respectively
let config = args()
.nth(1)
.expect("Please provide a configuration of the game, e.g. 3,5,7")
let config = args.configuration
.split(',')
.map(|num| num.parse::<usize>().expect("Not a number!"))
.collect::<Vec<_>>();
Expand All @@ -27,13 +40,8 @@ pub fn main() {
let mut game = Nim::new(config);

// parse every move in args, e.g. 0-0 1-1 in args
args().skip(2).for_each(|arg| {
let numbers: Vec<usize> = arg
.split('-')
.map(|num| num.parse::<usize>().expect("Not a number!"))
.collect();

game.make_move(&NaturalMove([numbers[0], numbers[1]]));
args.moves.iter().for_each(|nim_move| {
game.make_move(&nim_move);
});

print!("{}", game);
Expand Down
38 changes: 20 additions & 18 deletions crates/games/src/tic_tac_toe/cli.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
use std::env::args;

use clap::Args;
use game_solver::{game::Game, par_move_scores};
use ndarray::IntoDimension;

use crate::tic_tac_toe::{format_dim, TicTacToe};

pub fn main() {
// get the amount of dimensions from the first argument
let dim = args()
.nth(1)
.expect("Please provide a dimension!")
.parse::<usize>()
.expect("Not a number!");

// get the size of the board from the second argument
let size = args()
.nth(2)
.expect("Please provide a game size")
.parse::<usize>()
.expect("Not a number!");
#[derive(Args)]
pub struct TicTacToeArgs {
/// The amount of dimensions in the game.
dimensions: usize,
/// The size of the board - i.e. with two dimensions
/// and a size of three, the board would look like
///
/// ```txt
/// * * *
/// * * *
/// * * *
/// ```
size: usize,
/// The moves to make in the game, by dimension and index in that dimension.
moves: Vec<String>
}

let mut game = TicTacToe::new(dim, size);
pub fn main(args: TicTacToeArgs) {
let mut game = TicTacToe::new(args.dimensions, args.size);

// parse every move in args, e.g. 0-0 1-1 in args
args().skip(3).for_each(|arg| {
args.moves.iter().for_each(|arg| {
let numbers: Vec<usize> = arg
.split('-')
.map(|num| num.parse::<usize>().expect("Not a number!"))
Expand Down
2 changes: 1 addition & 1 deletion crates/games/src/util/move_natural.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{fmt::Display, iter, str::FromStr};

use itertools::Itertools;

#[derive(Clone, Debug, Copy, PartialEq)]
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct NaturalMove<const LENGTH: usize>(pub [usize; LENGTH]);

impl<const LENGTH: usize> FromStr for NaturalMove<LENGTH> {
Expand Down

0 comments on commit fedcca0

Please sign in to comment.