Skip to content

Commit e569dc8

Browse files
authored
Merge pull request #25 from PhilipCramer/bitwise_game_state
Bitwise game state implementation
2 parents c1f7570 + df66ac5 commit e569dc8

5 files changed

Lines changed: 732 additions & 229 deletions

File tree

src/bin/ai-test.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use rusty_othello_ai::mcts::MCTS;
2+
use rusty_othello_ai::othello::{caculate_win, Color, State};
3+
use std::isize;
4+
5+
pub fn main() {
6+
let args: Vec<String> = std::env::args().collect();
7+
let mut win_balance: isize = 0;
8+
let a: f32 = args
9+
.get(1)
10+
.expect("Missing value for A")
11+
.parse()
12+
.expect("Not a valid floatingpoint number");
13+
let b: f32 = args
14+
.get(2)
15+
.expect("Missing value for A")
16+
.parse()
17+
.expect("Not a valid floatingpoint number");
18+
19+
let mut state = State::new();
20+
let mut mcts = MCTS::new("true", a);
21+
let mut mcts2 = MCTS::new("false", b);
22+
let mut ai_iterations = 500;
23+
loop {
24+
state = ai_turn(&mut mcts, state.clone(), ai_iterations);
25+
if state.remaining_moves == 0 {
26+
break;
27+
}
28+
state = ai_turn(&mut mcts2, state.clone(), ai_iterations);
29+
if state.remaining_moves == 0 {
30+
break;
31+
}
32+
ai_iterations += ai_iterations / 100;
33+
}
34+
win_balance += match caculate_win(state) {
35+
Some(Color::WHITE) => 1,
36+
Some(Color::BLACK) => -1,
37+
None => 0,
38+
};
39+
println!("{win_balance}")
40+
}
41+
42+
fn ai_turn(mcts: &mut MCTS, state: State, iterations: usize) -> State {
43+
let dev_null = |_a: usize, _b: usize, _c: &Color| -> () {};
44+
let action = mcts.search(state.clone(), iterations, dev_null);
45+
if action.is_ok() {
46+
state.clone().do_action(Some(action.unwrap().clone()))
47+
} else {
48+
state.clone().do_action(None)
49+
}
50+
}

src/console_game.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::io::Write;
2+
use std::isize;
3+
use std::process::exit;
4+
5+
use crate::mcts::MCTS;
6+
use crate::othello::{caculate_win, print_state, Action, Color, Position, State};
7+
8+
enum GameCommand {
9+
SKIP,
10+
QUIT,
11+
INVALID,
12+
MOVE(usize, usize),
13+
}
14+
15+
pub fn console_game() {
16+
let mut win_balance: isize = 0;
17+
let a = 1.0;
18+
println!("Game mode: player vs AI\n");
19+
let mut state = State::new();
20+
let mut mcts = MCTS::new("true", a);
21+
_ = std::io::stdout().flush();
22+
let mut ai_iterations = 20000;
23+
loop {
24+
print_state(state);
25+
state = player_turn(state.clone());
26+
if state.remaining_moves == 0 {
27+
break;
28+
}
29+
print_state(state);
30+
state = ai_turn(&mut mcts, state.clone(), ai_iterations);
31+
ai_iterations += ai_iterations / 100;
32+
33+
if state.remaining_moves == 0 {
34+
break;
35+
}
36+
}
37+
//print_state(state);
38+
win_balance += match caculate_win(state) {
39+
Some(Color::WHITE) => {
40+
println!("White wins!");
41+
1
42+
}
43+
Some(Color::BLACK) => {
44+
println!("Black wins!");
45+
-1
46+
}
47+
None => {
48+
println!("Draw.");
49+
0
50+
}
51+
};
52+
//println!("\nGAME OVER\n");
53+
println!("\nResult: {win_balance}")
54+
}
55+
56+
fn ai_turn(mcts: &mut MCTS, state: State, iterations: usize) -> State {
57+
let dev_null = |_a: usize, _b: usize, _c: &Color| -> () { /*println!("Progress: {a}/{b}")*/ };
58+
let action = mcts.search(state.clone(), iterations, dev_null);
59+
if action.is_ok() {
60+
println!("{:?}", action.clone().unwrap().position);
61+
state.clone().do_action(Some(action.unwrap().clone()))
62+
} else {
63+
state.clone().do_action(None)
64+
}
65+
}
66+
67+
fn player_turn(state: State) -> State {
68+
let mut player_choice;
69+
loop {
70+
print!("Enter coordinates for desired move: ");
71+
let _ = std::io::stdout().flush();
72+
let cmd = read_command();
73+
match cmd {
74+
GameCommand::QUIT => exit(0),
75+
GameCommand::INVALID => {
76+
println!("Please provide a valid command 'quit' 'skip' or 'x,y'")
77+
}
78+
GameCommand::SKIP => {
79+
player_choice = None;
80+
break;
81+
}
82+
GameCommand::MOVE(x_index, y_index) => {
83+
player_choice = Some(Action {
84+
color: Color::BLACK,
85+
position: Position {
86+
x: x_index,
87+
y: y_index,
88+
},
89+
});
90+
if state
91+
.get_actions()
92+
.contains(&player_choice.clone().unwrap())
93+
{
94+
break;
95+
} else {
96+
println!("Invalid move.");
97+
let pos: Vec<(usize, usize)> = state
98+
.get_actions()
99+
.iter()
100+
.map(|a| (a.position.y, a.position.x))
101+
.collect();
102+
println!("Valid moves: {:?}", pos);
103+
print_state(state);
104+
}
105+
}
106+
}
107+
}
108+
state.clone().do_action(player_choice)
109+
}
110+
111+
fn read_command() -> GameCommand {
112+
let mut buf = String::new();
113+
let _ = std::io::stdin().read_line(&mut buf);
114+
match buf.to_lowercase().as_str().trim() {
115+
"quit" => GameCommand::QUIT,
116+
"skip" => GameCommand::SKIP,
117+
line => {
118+
let cmd: Vec<&str> = line.trim().split(",").clone().collect();
119+
match (cmd.get(0), cmd.get(1)) {
120+
(Some(cmd_1), Some(cmd_2)) => {
121+
match (cmd_1.parse::<usize>(), cmd_2.parse::<usize>()) {
122+
(Ok(y_index), Ok(x_index)) => GameCommand::MOVE(x_index, y_index),
123+
_ => GameCommand::INVALID,
124+
}
125+
}
126+
_ => GameCommand::INVALID,
127+
}
128+
}
129+
}
130+
}

src/main.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
use rusty_othello_ai::mcts::MCTS;
2-
use rusty_othello_ai::othello::{parse_state, Action, State};
3-
use std::thread::current;
1+
use std::process::exit;
42
use std::time::Duration;
53
use std::usize;
64
use std::{borrow::Borrow, thread::sleep};
75
use ureq::Response;
6+
mod console_game;
7+
mod mcts;
8+
mod othello;
9+
use console_game::console_game;
10+
use mcts::MCTS;
11+
use othello::{parse_state, Action, Color, State};
812

913
const SERVER_URL: &str = "http://localhost:8181";
1014

@@ -27,6 +31,10 @@ fn main() {
2731
x if x == "1" => ai_color = "true".to_string(),
2832
x if x == "w" => ai_color = "true".to_string(),
2933
x if x == "white" => ai_color = "true".to_string(),
34+
x if x == "console" => {
35+
console_game();
36+
exit(0);
37+
}
3038
_ => panic!("Please pass a proper argument to the AI"),
3139
}
3240
// Initialize the game state and the Monte Carlo Tree Search (MCTS)
@@ -60,7 +68,7 @@ fn main() {
6068
}
6169
// If it's not the AI's turn, it performs a search using MCTS and waits
6270
Ok(false) => {
63-
let dev_null = |_a: usize, _b: usize, _c: &i8| -> () {};
71+
let dev_null = |_a: usize, _b: usize, _c: &Color| -> () {};
6472
_ = mcts.search(state, 1000, dev_null);
6573
//sleep(Duration::from_secs(1));
6674
}
@@ -143,7 +151,7 @@ fn send_move(player: &String, ai_move: Option<Action>) -> Result<Response, ureq:
143151
let ai_choice = ai_move.unwrap();
144152
url = format!(
145153
"{}/setChoice/{}/{}/{}",
146-
SERVER_URL, ai_choice.x, ai_choice.y, player
154+
SERVER_URL, ai_choice.position.x, ai_choice.position.y, player
147155
);
148156
}
149157
// If the AI does not have a move, format the URL for the skipTurn endpoint
@@ -154,10 +162,10 @@ fn send_move(player: &String, ai_move: Option<Action>) -> Result<Response, ureq:
154162
resp = ureq::get(&url).call()?;
155163
Ok(resp)
156164
}
157-
fn send_progress(current: usize, total: usize, ai_color: &i8) {
165+
fn send_progress(current: usize, total: usize, ai_color: &Color) {
158166
let color = match ai_color {
159-
1 => "false",
160-
_ => "true",
167+
Color::BLACK => "false",
168+
Color::WHITE => "true",
161169
};
162170
let url = format!("{}/AIStatus/{}/{}/{}", SERVER_URL, current, total, color);
163171
_ = ureq::post(&url).call();

0 commit comments

Comments
 (0)