215 lines
4.2 KiB
Rust
215 lines
4.2 KiB
Rust
use anyhow::Result;
|
||
use std::io::{stdin, stdout, StdoutLock, Write};
|
||
use termion::{
|
||
color::{self},
|
||
cursor::HideCursor,
|
||
event::Key,
|
||
input::TermRead,
|
||
raw::IntoRawMode,
|
||
};
|
||
|
||
mod agents;
|
||
mod board;
|
||
mod util;
|
||
use board::Board;
|
||
use util::{Player, Symb};
|
||
|
||
fn play(
|
||
stdout: &mut StdoutLock,
|
||
player_max: bool,
|
||
computer: &mut dyn agents::PlayerAgent,
|
||
) -> 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;
|
||
|
||
// For human player UI
|
||
let symbols = [
|
||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '+', '-', '×', '÷',
|
||
];
|
||
let mut selected_symbol = 0;
|
||
|
||
'outer: loop {
|
||
// Computer turn
|
||
if board.current_player() == Player::Computer && !board.is_done() {
|
||
computer.step(&mut board);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
while board.contains(Symb::from_char(&symbols[selected_symbol]).unwrap()) {
|
||
if selected_symbol == symbols.len() - 1 {
|
||
selected_symbol = 0;
|
||
} else {
|
||
selected_symbol += 1;
|
||
}
|
||
}
|
||
|
||
print!(
|
||
"\r{}╙{}{}{}{}{}╜",
|
||
" ".repeat(cursor_offset),
|
||
" ".repeat(cursor),
|
||
color::Fg(board.current_player().color()),
|
||
if board.is_done() {
|
||
' '
|
||
} else {
|
||
symbols[selected_symbol]
|
||
},
|
||
color::Fg(color::Reset),
|
||
" ".repeat(cursor_max - cursor),
|
||
);
|
||
stdout.flush()?;
|
||
|
||
if board.is_done() {
|
||
break;
|
||
}
|
||
|
||
// Player turn
|
||
let stdin = stdin();
|
||
for c in stdin.keys() {
|
||
print_board = match c.unwrap() {
|
||
Key::Char('q') => break 'outer,
|
||
Key::Right => {
|
||
cursor = cursor_max.min(cursor + 1);
|
||
false
|
||
}
|
||
Key::Left => {
|
||
if cursor != 0 {
|
||
cursor -= 1
|
||
}
|
||
false
|
||
}
|
||
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') => {
|
||
let s = Symb::from_char(&symbols[selected_symbol]);
|
||
if let Some(s) = s {
|
||
board.play(cursor, s)
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
Key::Char(c) => {
|
||
let s = Symb::from_char(&c);
|
||
if let Some(s) = s {
|
||
board.play(cursor, s)
|
||
} else {
|
||
false
|
||
}
|
||
}
|
||
_ => false,
|
||
};
|
||
continue 'outer;
|
||
}
|
||
}
|
||
|
||
return Ok(board);
|
||
}
|
||
|
||
fn main() -> Result<()> {
|
||
let stdout = HideCursor::from(stdout().into_raw_mode().unwrap());
|
||
let mut stdout = stdout.lock();
|
||
|
||
let mut agent = agents::MinMaxTree {};
|
||
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(());
|
||
}
|
||
|
||
let mut agent = agents::RandomAgent {};
|
||
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(())
|
||
}
|