Add existing files to Git

This commit is contained in:
Steven Chase 2025-02-03 12:08:03 -05:00
parent 73d24e7748
commit 0c5991fa20
3 changed files with 248 additions and 14 deletions

7
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}

View File

@ -11,6 +11,14 @@ pub struct Board {
impl fmt::Display for Board {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let horiz_sep_line = "-".repeat(BOARD_SIZE * 2 + 1);
// Print numbers at top so I can read the board easier
write!(f, " ")?;
for j in 0..BOARD_SIZE {
write!(f, "{} ", j)?;
}
writeln!(f)?;
for i in 0..BOARD_SIZE {
writeln!(f, " {}", horiz_sep_line)?;
@ -24,12 +32,26 @@ impl fmt::Display for Board {
}
writeln!(f)?;
}
// put a line at the bottom of the board too
writeln!(f, "{}", horiz_sep_line)?;
writeln!(f, " {}", horiz_sep_line)?;
// Print the current score
let (white_score, black_score) = self.get_score();
writeln!(f, "White Score: {}\nBlack Score: {}", white_score, black_score);
// Print game over screen
if self.game_over() == true {
match self.get_winner() {
Some(Piece::Black) => writeln!(f, "Black Wins"),
Some(Piece::White) => writeln!(f, "White Wins"),
None => writeln!(f, "Tie"),
}?
}
Ok(())
}
}
impl Board {
@ -160,6 +182,56 @@ impl Board {
}
captured
}
// keep score of each color
pub fn get_score(&self) -> (usize, usize) {
let mut white_score = 0;
let mut black_score = 0;
for row in &self.board {
for &cell in row {
match cell {
Some(Piece::White) => white_score += 1,
Some(Piece::Black) => black_score += 1,
_ => {}
}
}
}
(white_score, black_score)
}
// Get the winning piece (for game over screen mainly)
pub fn get_winner(&self) -> Option<Piece> {
let (white_score, black_score) = self.get_score();
// White wins
if white_score > black_score {
Some(Piece::White)
}
// Black Wins
else if black_score > white_score {
Some(Piece::Black)
}
// Tie
else {
None
}
}
pub fn game_over(&self) -> bool {
let (white_score, black_score) = self.get_score();
let max_score = BOARD_SIZE * BOARD_SIZE;
let combined_score = black_score + white_score;
if max_score == combined_score {
true
}
else {
false
}
}
}
#[cfg(test)]
@ -262,4 +334,132 @@ mod test {
assert_eq!(board.place(2, 5, Piece::Black), Ok(()), "{}", board);
}
// Test corner capture from top-left corner
#[test]
fn corner_capture_top_left() {
let mut board = Board::new();
// Black pieces at (0, 0) and (1, 0), and (0, 1) to create capture opportunity
board.place_unchecked(0, 0, Piece::Black);
board.place_unchecked(1, 0, Piece::White);
board.place_unchecked(0, 1, Piece::White);
board.propegate_from(0, 0);
// Capture white piece at (0, 1) and (1, 0)
assert_eq!(board.get(0, 1), &Some(Piece::Black));
assert_eq!(board.get(1, 0), &Some(Piece::Black));
}
// Test corner capture from top-right corner
#[test]
fn corner_capture_top_right() {
let mut board = Board::new();
// Black pieces at (0, 7) and (1, 7), and (0, 6) to create capture opportunity
board.place_unchecked(0, 7, Piece::Black);
board.place_unchecked(1, 7, Piece::White);
board.place_unchecked(0, 6, Piece::White);
board.propegate_from(0, 7);
// Capture white piece at (0, 6) and (1, 7)
assert_eq!(board.get(0, 6), &Some(Piece::Black));
assert_eq!(board.get(1, 7), &Some(Piece::Black));
}
// Test corner capture from bottom-left corner
#[test]
fn corner_capture_bottom_left() {
let mut board = Board::new();
// Black pieces at (7, 0) and (6, 0), and (7, 1) to create capture opportunity
board.place_unchecked(7, 0, Piece::Black);
board.place_unchecked(6, 0, Piece::White);
board.place_unchecked(7, 1, Piece::White);
board.propegate_from(7, 0);
// Capture white piece at (7, 1) and (6, 0)
assert_eq!(board.get(7, 1), &Some(Piece::Black));
assert_eq!(board.get(6, 0), &Some(Piece::Black));
}
// Test corner capture from bottom-right corner
#[test]
fn corner_capture_bottom_right() {
let mut board = Board::new();
// Black pieces at (7, 7) and (6, 7), and (7, 6) to create capture opportunity
board.place_unchecked(7, 7, Piece::Black);
board.place_unchecked(6, 7, Piece::White);
board.place_unchecked(7, 6, Piece::White);
board.propegate_from(7, 7);
// Capture white piece at (7, 6) and (6, 7)
assert_eq!(board.get(7, 6), &Some(Piece::Black));
assert_eq!(board.get(6, 7), &Some(Piece::Black));
}
// Test capture from top-left corner (horizontal)
#[test]
fn capture_top_left_horiz() {
let mut board = Board::new();
// Create a scenario where a capture should happen horizontally from (0, 0)
board.place_unchecked(0, 0, Piece::Black);
board.place_unchecked(0, 1, Piece::White);
board.place_unchecked(0, 2, Piece::Black);
board.propegate_from(0, 2);
assert_eq!(board.get(0, 1), &Some(Piece::Black));
}
// Test capture from top-right corner (horizontal)
#[test]
fn capture_top_right_horiz() {
let mut board = Board::new();
// Create a scenario where a capture should happen horizontally from (0, 7)
board.place_unchecked(0, 7, Piece::Black);
board.place_unchecked(0, 6, Piece::White);
board.place_unchecked(0, 5, Piece::Black);
board.propegate_from(0, 5);
assert_eq!(board.get(0, 6), &Some(Piece::Black));
}
// Test capture from top-left corner (vertical)
#[test]
fn capture_top_left_vert() {
let mut board = Board::new();
// Create a scenario where a capture should happen vertically from (0, 0)
board.place_unchecked(0, 0, Piece::Black);
board.place_unchecked(1, 0, Piece::White);
board.place_unchecked(2, 0, Piece::Black);
board.propegate_from(2, 0);
assert_eq!(board.get(1, 0), &Some(Piece::Black));
}
// Test capture from bottom-left corner (vertical)
#[test]
fn capture_bottom_left_vert() {
let mut board = Board::new();
// Create a scenario where a capture should happen vertically from (7, 0)
board.place_unchecked(7, 0, Piece::Black);
board.place_unchecked(6, 0, Piece::White);
board.place_unchecked(5, 0, Piece::Black);
board.propegate_from(5, 0);
assert_eq!(board.get(6, 0), &Some(Piece::Black));
}
}

View File

@ -1,5 +1,4 @@
use crate::{agent::Agent, board::Board};
use std::time::Duration;
pub struct Game {
players: [Box<dyn Agent>; 2],
@ -29,31 +28,59 @@ impl Game {
}
}
// Handle when a move is made
pub fn step(&mut self, player_i: usize) {
let player_move = self.players[player_i].next_move(&self.board);
if let Some((i, j)) = player_move {
if let Ok(()) = self.board.place(i, j, self.players[player_i].color()) {
let player_color = self.players[player_i].color();
loop {
let player_move = self.players[player_i].next_move(&self.board);
if let Some((i, j)) = player_move {
match self.board.place(i, j, player_color) {
// Check if its a valid move
Ok(_) => {
println!("Player {} placed at ({}, {})", player_i, i, j);
break;
}
// Lets the player try again if the move is invalid
// Now we dont need to restart the game if we mess up
Err(err) => {
println!("Invalid move by Player {}: {}. Try again.", player_i, err);
}
}
} else {
todo!("handle invalid player move");
println!("Player {} did not make a move!", player_i);
return; // No valid move available
}
} else {
panic!("player {} did not make a move", player_i);
}
}
// TODO! make this loop good
pub fn game_loop(&mut self) {
let mut i = 0;
let mut current_player: usize = 0;
loop {
// alternate which player plays
i += 1;
i %= self.players.len();
current_player += 1;
current_player %= self.players.len();
println!("{}", self);
self.step(i);
// Check if the game is over
if self.board.game_over() == true {
break;
}
self.step(current_player);
// std::thread::sleep(Duration::from_millis(200));
}
// Print Game Over Screen
println!("{}", self);
match self.board.get_winner() {
Some(winner) => println!("Game Over! {} Wins!", winner.text()),
None => print!("It's a tie!! You either both suck or are both really good..."),
}
}
}