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}; use crate::board::PlayerAction; fn play( stdout: &mut StdoutLock, player_max: bool, computer: &mut dyn agents::MinimizerAgent, ) -> Result { 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_min(&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 symb = Symb::from_char(&symbols[selected_symbol]); if let Some(symb) = symb { let action = PlayerAction { symb, pos: cursor }; board.play(action) } else { false } } Key::Char(c) => { let symb = Symb::from_char(&c); if let Some(symb) = symb { let action = PlayerAction { symb, pos: cursor }; board.play(action) } 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::DiffuseAgent {}; 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(()) }