1use super::game::{Board, BoardState, ChipDescrip, Game, Player};
2use std::io::{stdin, stdout, Write};
3
4pub fn draw_term_board(game: &Board) {
5 let io = TermIO::new();
6 io.draw_board(game);
7}
8
9pub trait GameIO {
10 fn draw_board(&self, game: &Board);
11 fn get_move(&self, game: &Game) -> (isize, ChipDescrip);
12 fn display_gameover(&self, ending: BoardState);
13}
14
15pub const EMPTY: char = '◻';
16pub const FILLED: char = '◼';
17pub const BRIGHTEN: isize = 60;
18pub const FG: isize = 30;
19pub const BG: isize = 40;
20pub const BLK: isize = 0;
21pub const RED: isize = 1;
22pub const GRN: isize = 2;
23pub const YEL: isize = 3;
24pub const BLU: isize = 4;
25pub const MAG: isize = 5;
26pub const CYN: isize = 6;
27pub const WHT: isize = 7;
28pub const RST: isize = 9;
29
30pub struct TermIO {
31 fg: isize,
32 bg: isize,
33}
34
35impl TermIO {
36 pub fn new() -> Self {
37 Self { fg: RST, bg: RST }
38 }
39
40 fn paint(fg: isize, bg: isize) {
41 let esc = char::from(0x1b);
42 print!("{}[{};{}m", esc, fg + FG, bg + BG)
43 }
44 fn endpaint() {
45 let esc = char::from(0x1b);
46 print!("{}[0m", esc)
47 }
48
49 fn print_with_color(&mut self, s: char, fg: isize, bg: isize) {
50 if fg != self.fg || bg == self.fg {
51 Self::paint(fg, bg);
52 self.fg = fg;
53 self.bg = bg;
54 }
55 print!("{}", s);
56 }
57}
58impl GameIO for TermIO {
59 fn draw_board(&self, game: &Board) {
60 let mut drawer = Self { fg: 0, bg: 0 };
61 let chips = game.get_layout();
62 for i in 0..chips.len() {
63 let x = i % game.width as usize;
64 let y = i / game.width as usize;
65 let y = game.height as usize - y - 1;
66 let i = x + y * game.width as usize;
67 if let Some(chip) = chips[i] {
68 drawer.print_with_color(chip.graphic, chip.fg_color, chip.bg_color);
69 } else {
70 drawer.print_with_color(EMPTY, WHT, BLK + BRIGHTEN);
71 }
72 print!(" ");
73 if (i + 1) % game.width as usize == 0 {
74 drawer.print_with_color('\n', RST, RST);
75 }
76 }
77
78 drawer.print_with_color('1', WHT, BLK + BRIGHTEN);
79 (1..game.width as usize).for_each(|i| print!(" {}", i + 1));
80 print!(" ");
81 Self::endpaint();
82 println!();
83 }
84
85 fn get_move(&self, game: &Game) -> (isize, ChipDescrip) {
86 fn read_line() -> String {
87 let mut buffer = String::new();
88 stdout().flush().expect("Failed to flush");
89 let _res = stdin().read_line(&mut buffer);
90 buffer.trim().to_string()
91 }
92 fn get_num_in_range(lb: usize, ub: usize) -> usize {
93 print!("Enter a number in range [{},{}]: ", lb, ub);
94 if let Ok(v) = read_line().parse::<usize>() {
95 if v >= lb && v <= ub {
96 return v;
97 }
98 }
99 get_num_in_range(lb, ub)
100 }
101
102 println!(
103 "Player {} turn.",
104 game.get_turn() as usize % game.get_player_count() as usize + 1
105 );
106
107 let player = game.current_player();
108 let ch = if player.chip_options.len() == 1 {
109 player.chip_options[0]
110 } else {
111 fn get_chip_type(player: &Player) -> ChipDescrip {
112 let mut drawer = TermIO { fg: 0, bg: 0 };
113 println!("Select chip type:");
114 for chip in &player.chip_options {
115 drawer.print_with_color(chip.graphic, chip.fg_color, chip.bg_color);
116 drawer.print_with_color(' ', chip.fg_color, chip.bg_color)
117 }
118 TermIO::endpaint();
119 println!();
120 drawer.print_with_color('', WHT, BLK + BRIGHTEN);
121 for i in 0..player.chip_options.len() {
122 print!("{} ", i + 1);
123 }
124 TermIO::endpaint();
125 println!();
126
127 let l = player.chip_options.len();
128 player.chip_options[get_num_in_range(1, l) - 1]
129 }
130 get_chip_type(player)
131 };
132
133 let val = get_num_in_range(1, game.get_board().width as usize) - 1;
134 (val as isize, ch)
135 }
136
137 fn display_gameover(&self, ending: BoardState) {
138 match ending {
139 BoardState::Win(x) => println!("Player {} wins!", x),
140 BoardState::Draw => println!("It's a draw :("),
141 BoardState::Ongoing => (),
142 BoardState::Invalid => println!("Illegal move!"),
143 }
144 }
145}