minimax/src/main.rs

219 lines
4.4 KiB
Rust
Raw Normal View History

2024-02-26 08:54:35 -08:00
use anyhow::Result;
2024-03-04 15:49:40 -08:00
use std::io::{stdin, stdout, StdoutLock, Write};
2024-02-26 08:54:35 -08:00
use termion::{
color::{self},
cursor::HideCursor,
event::Key,
input::TermRead,
raw::IntoRawMode,
};
2024-03-03 21:48:33 -08:00
mod agents;
2024-02-26 08:54:35 -08:00
mod board;
mod util;
use board::Board;
use util::{Player, Symb};
2024-03-04 17:03:28 -08:00
use crate::board::PlayerAction;
2024-02-26 08:54:35 -08:00
fn play(
stdout: &mut StdoutLock,
player_max: bool,
2024-03-04 17:03:28 -08:00
computer: &mut dyn agents::MinimizerAgent,
2024-02-26 08:54:35 -08:00
) -> Result<Board> {
let mut cursor = 0usize;
let cursor_offset = 10usize - 1;
let cursor_max = 10usize;
let mut board = Board::new(if player_max {
Player::Human
} else {
Player::Computer
});
let mut is_first = true;
let mut print_board = true;
2024-03-04 15:49:40 -08:00
// For human player UI
let symbols = [
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '+', '-', '×', '÷',
];
let mut selected_symbol = 0;
2024-02-26 08:54:35 -08:00
'outer: loop {
// Computer turn
if board.current_player() == Player::Computer && !board.is_done() {
2024-03-04 17:03:28 -08:00
computer.step_min(&mut board);
2024-02-26 08:54:35 -08:00
}
let min_color = if !player_max {
Player::Human.color()
} else {
Player::Computer.color()
};
let max_color = if player_max {
Player::Human.color()
} else {
Player::Computer.color()
};
if print_board {
println!(
"\r{}\r{}Min{}/{}Max{} {}{}{}",
" ".repeat(cursor_max + cursor_offset),
color::Fg(min_color),
color::Fg(color::Reset),
color::Fg(max_color),
color::Fg(color::Reset),
if is_first { '╓' } else { '║' },
board,
if is_first { '╖' } else { '║' },
);
is_first = false;
}
2024-03-04 15:49:40 -08:00
while board.contains(Symb::from_char(&symbols[selected_symbol]).unwrap()) {
if selected_symbol == symbols.len() - 1 {
selected_symbol = 0;
} else {
selected_symbol += 1;
}
}
2024-02-26 08:54:35 -08:00
print!(
"\r{}╙{}{}{}{}{}╜",
" ".repeat(cursor_offset),
" ".repeat(cursor),
color::Fg(board.current_player().color()),
2024-03-04 15:49:40 -08:00
if board.is_done() {
' '
} else {
symbols[selected_symbol]
},
2024-02-26 08:54:35 -08:00
color::Fg(color::Reset),
" ".repeat(cursor_max - cursor),
);
stdout.flush()?;
if board.is_done() {
break;
}
2024-03-03 21:48:33 -08:00
// Player turn
2024-02-26 08:54:35 -08:00
let stdin = stdin();
for c in stdin.keys() {
print_board = match c.unwrap() {
2024-03-04 15:49:40 -08:00
Key::Char('q') => break 'outer,
2024-02-26 08:54:35 -08:00
Key::Right => {
cursor = cursor_max.min(cursor + 1);
false
}
Key::Left => {
if cursor != 0 {
cursor -= 1
}
false
}
2024-03-04 15:49:40 -08:00
Key::Up => {
if selected_symbol == 0 {
selected_symbol = symbols.len() - 1;
} else {
selected_symbol -= 1;
}
while board.contains(Symb::from_char(&symbols[selected_symbol]).unwrap()) {
if selected_symbol == 0 {
selected_symbol = symbols.len() - 1;
} else {
selected_symbol -= 1;
}
}
false
}
Key::Down => {
if selected_symbol == symbols.len() - 1 {
selected_symbol = 0;
} else {
selected_symbol += 1;
}
while board.contains(Symb::from_char(&symbols[selected_symbol]).unwrap()) {
if selected_symbol == symbols.len() - 1 {
selected_symbol = 0;
} else {
selected_symbol += 1;
}
}
false
}
Key::Char('\n') => {
2024-03-04 17:03:28 -08:00
let symb = Symb::from_char(&symbols[selected_symbol]);
if let Some(symb) = symb {
let action = PlayerAction { symb, pos: cursor };
board.play(action)
2024-03-04 15:49:40 -08:00
} else {
false
}
}
Key::Char(c) => {
2024-03-04 17:03:28 -08:00
let symb = Symb::from_char(&c);
if let Some(symb) = symb {
let action = PlayerAction { symb, pos: cursor };
board.play(action)
2024-03-04 15:49:40 -08:00
} else {
false
}
}
2024-02-26 08:54:35 -08:00
_ => false,
};
continue 'outer;
}
}
return Ok(board);
}
fn main() -> Result<()> {
let stdout = HideCursor::from(stdout().into_raw_mode().unwrap());
let mut stdout = stdout.lock();
2024-03-04 17:03:28 -08:00
let mut agent = agents::DiffuseAgent {};
2024-02-26 08:54:35 -08:00
let a = play(&mut stdout, true, &mut agent)?;
if a.is_done() {
println!(
"\r\n{}Your score:{} {:.2}\n\n",
color::Fg(Player::Human.color()),
color::Fg(color::Reset),
a.evaluate().unwrap()
);
} else {
println!(
"\r\n{}Quitting{}\r\n",
color::Fg(color::Red),
color::Fg(color::Reset),
);
return Ok(());
}
2024-03-03 21:48:33 -08:00
let mut agent = agents::RandomAgent {};
2024-02-26 08:54:35 -08:00
let b = play(&mut stdout, false, &mut agent)?;
if b.is_done() {
println!(
"\r\n{}Computer score:{} {:.2}\n\n",
color::Fg(Player::Computer.color()),
color::Fg(color::Reset),
b.evaluate().unwrap()
);
} else {
println!(
"\r\n{}Quitting{}\r\n",
color::Fg(color::Red),
color::Fg(color::Reset),
);
return Ok(());
}
Ok(())
}