extern crate pancurses; use pancurses::Input::*; use pancurses::*; mod game; use crate::game::*; struct BoardGui { win: Window, cell_windows: Vec, size: u8, cell_height: u8, cell_width: u8, hoff: i32, cur_cell: Position, has_colours: bool, white_colour: u8, black_colour: u8, } #[derive(PartialEq)] enum BGAction { Quit, None, } impl BoardGui { fn draw_grid(&self) { let s: i32 = self.size as i32; let h: i32 = self.cell_height as i32; let w: i32 = self.cell_width as i32; for y in 0..h * s + 1 { for x in 0..w * s + 1 { if y % h == 0 { if x % w == 0 { self.win.mvaddch(y, x + self.hoff, '+'); } else { self.win.mvaddch(y, x + self.hoff, '-'); } } else if x % w == 0 { self.win.mvaddch(y, x + self.hoff, '|'); } } } for y in 0..s { self.win.mvaddstr(y * h + h / 2, 0, format!("{}.", s - y)); } for x in 0..s { self.win.mvaddstr( s * h + 1, w * x + w / 2 + self.hoff, format!("{:x}.", x + 10), ); } self.win.refresh(); } fn pos_to_idx(&self, pos: &Position) -> usize { let y = pos.y as usize; let x = pos.x as usize; x + y * (self.size as usize) } fn mv_to_cell(&self, pos: &Position, line: i32) { if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { win.mv(line, 0); win.refresh(); } } fn draw_stone(&self, win: &Window, top: bool, player: &Player, stone: &Stone) { if self.has_colours { match player { Player::Black => win.attrset(ColorPair(self.black_colour)), Player::White => win.attrset(ColorPair(self.white_colour)), }; if top { win.addch(match stone { Stone::Flat => '#', Stone::Standing => '/', Stone::Capstone => '*', }); } else { win.addstr(format!("{}", player)); } } else { win.addstr(format!("{}", player)); if top { win.addstr(format!("{}", stone)); } } } fn draw_stack(&self, pos: &Position, stack: &Stack) { if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { win.mv(0, 0); let l = stack.len() as i32; if l > 0 { for i in 0..l { let piece = &stack[i as usize]; self.draw_stone(win, i == l - 1, &piece.player, &piece.stone); } } win.refresh(); } } // Is this really the best way to do this? fn draw_game_state(&self, game_state: &GameState) { // ???? assert!(game_state.get_size() == self.size); let mut pos = Position { x: 0, y: 0 }; for y in 0..self.size { pos.y = y; for x in 0..self.size { pos.x = x; if let Some(stack) = game_state.query_square(&pos) { self.draw_stack(&pos, stack); } } } } fn handle_key(&mut self, key: Input) -> BGAction { match key { KeyDown => { if 0 < self.cur_cell.y { self.cur_cell.y -= 1; self.mv_to_cell(&self.cur_cell, 0); } BGAction::None } KeyUp => { if self.cur_cell.y + 1 < self.size { self.cur_cell.y += 1; self.mv_to_cell(&self.cur_cell, 0); } BGAction::None } KeyLeft => { if 0 < self.cur_cell.x { self.cur_cell.x -= 1; self.mv_to_cell(&self.cur_cell, 0); } BGAction::None } KeyRight => { if self.cur_cell.x + 1 < self.size { self.cur_cell.x += 1; self.mv_to_cell(&self.cur_cell, 0); } BGAction::None } Character('q') => BGAction::Quit, x => { self.win.addstr(format!("{:?}", x)); self.win.refresh(); BGAction::None } } } fn new( ypos: i32, xpos: i32, size: u8, cell_height: u8, cell_width: u8, hoff: u8, has_colours: bool, white_colour: u8, black_colour: u8, ) -> BoardGui { let mut cell_windows: Vec = Vec::new(); let hoff = hoff as i32; let s: i32 = size as i32; let h: i32 = cell_height as i32; let w: i32 = cell_width as i32; for i in 0..s * s { let y: i32 = i / s; let x: i32 = i % s; cell_windows.push(newwin(h - 1, w - 1, (s - y - 1) * h + 1, hoff + x * w + 1)); } let bg = BoardGui { win: newwin(h * s + 3, w * s + 3 + hoff, ypos, xpos), cell_windows: cell_windows, size: size, cell_height: cell_height, cell_width: cell_width, hoff: hoff, cur_cell: Position { x: 0, y: 0 }, has_colours: has_colours, black_colour: black_colour, white_colour: white_colour, }; bg.draw_grid(); // for i in 0..s * s { // let y: i32 = i / s; // let x: i32 = i % s; // let y = (s - y - 1) * h + 1; // let x = hoff + x * w + 1; // bg.cell_windows[i as usize].addstr(format!("{}:({},{})", i, y, x)); // bg.cell_windows[i as usize].refresh(); // } bg.mv_to_cell(&bg.cur_cell, 0); bg.win.refresh(); bg } } struct MoveEntry { win: Window, } impl MoveEntry { fn init(&self) { self.win.mvaddstr(0, 0, "Enter move: "); // Make shaded box for entry self.win.refresh(); } fn read_action(&self) -> &str { "a1" } } struct MessageLog { win: Window, has_colours: bool, red_colour: u8, } impl MessageLog { fn init(&self) {} fn log_error(&self, str: String) { if self.has_colours { self.win.attrset(ColorPair(self.red_colour)); } // self.win.clear(); self.win.addstr(str); self.win.attrset(Attribute::Normal); self.win.refresh(); } } fn main() { let screen = initscr(); // screen.nodelay(true); screen.keypad(true); cbreak(); noecho(); curs_set(2); let has_colours = has_colors(); let (black_colour, white_colour, red_colour): (u8, u8, u8) = (1, 2, 3); if has_colours { start_color(); use_default_colors(); init_pair(black_colour as i16, -1, COLOR_RED); init_pair(white_colour as i16, -1, COLOR_BLUE); init_pair(red_colour as i16, COLOR_RED, -1); } screen.refresh(); let mew = newwin(1, 30, 25, 0); let mlw = newwin(3, 70, 26, 0); let mut bg = BoardGui::new(0, 0, 5, 4, 8, 3, has_colours, white_colour, black_colour); let me = MoveEntry { win: mew }; let ml = MessageLog { win: mlw, has_colours: has_colours, red_colour: red_colour, }; me.init(); ml.init(); let log = |s| MessageLog::log_error(&ml, s); let game_state = GameState::new(5); let pos = Position { x: 0, y: 0 }; let action = Action::Place(Player::White, pos, Stone::Flat); // game_state.perform_action(&action); // game_state.is_legal_action(&Action::Move(Player::White, pos, Direction::Up, vec![0, 1])); // bg.draw_game_state(&game_state); loop { if let Some(inp) = screen.getch() { if bg.handle_key(inp) == BGAction::Quit { break; } } } endwin(); }