aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortslil clingman <>2020-01-14 20:44:29 -0500
committertslil clingman <>2020-01-14 20:44:29 -0500
commit7dcf57f6f2e987d44391ae68b8c48824ba7523a6 (patch)
treea5966c819ba848c31e7b7ffde60780d9644b29a5
parent0c3c243d60d49e1c18e5a18fc2300e7e47292938 (diff)
Second draft of checking code, beginning on perform actions
-rw-r--r--src/board.rs297
-rw-r--r--src/main.rs31
2 files changed, 204 insertions, 124 deletions
diff --git a/src/board.rs b/src/board.rs
index 484c31a..4e3b0d5 100644
--- a/src/board.rs
+++ b/src/board.rs
@@ -1,13 +1,13 @@
use std::fmt;
-// #[derive(Clone, Copy)]
+#[derive(Clone, Copy)]
pub struct Position {
pub x: u8,
pub y: u8,
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:x}{}", 10 + self.x, self.y)
+ write!(f, "{:x}{}", 10 + self.x, self.y + 1)
}
}
@@ -96,13 +96,17 @@ impl fmt::Display for Action {
type Stack = Vec<(Player, Stone)>;
-pub struct Board {
+pub struct Board<L>
+where
+ L: Fn(String),
+{
size: u8,
black_flats: u8,
white_flats: u8,
black_capstones: u8,
white_capstones: u8,
board: Vec<Stack>,
+ log: L,
}
// How to solve code duplication between is_legal_action and
@@ -110,9 +114,40 @@ pub struct Board {
// TODO: Generate all legal actions for a given player
-pub type Logger = fn(String);
+// pub type Logger = fn(String);
+
+impl<L: Fn(String)> Board<L> {
+ pub fn new(size: u8, log: L) -> Option<Board<L>> {
+ if size >= 3 && size <= 8 {
+ let (flats, caps) = match size {
+ 3 => (10, 0),
+ 4 => (15, 0),
+ 5 => (21, 1),
+ 6 => (30, 1),
+ 7 => (40, 2),
+ _ => return None,
+ };
+ Some(Board {
+ size: size,
+ black_flats: flats,
+ white_flats: flats,
+ black_capstones: caps,
+ white_capstones: caps,
+ board: {
+ let mut v: Vec<Stack> = Vec::new();
+ for _i in 0..size * size {
+ let t: Stack = Vec::new();
+ v.push(t);
+ }
+ v
+ },
+ log: log,
+ })
+ } else {
+ None
+ }
+ }
-impl Board {
fn remaining_pieces(&self, player: &Player, stone: &Stone) -> u8 {
match player {
Player::Black => {
@@ -140,40 +175,40 @@ impl Board {
}
}
- fn lookup_square(&self, pos: &Position) -> Option<&Stack> {
+ fn pos_to_idx(&self, pos: &Position) -> usize {
let y: usize = pos.y as usize;
let x: usize = pos.x as usize;
- self.board.get(x + y * (self.size as usize))
+ x + y * (self.size as usize)
}
- fn is_legal_place(&self, player: &Player, pos: &Position, stone: &Stone, log: Logger) -> bool {
+ fn is_legal_place(&self, player: &Player, pos: &Position, stone: &Stone) -> bool {
/* In order to legally place a piece:
1. The desired square must be empty
2. The player must have sufficient pieces
*/
if self.within_bounds(pos) {
- match self.lookup_square(pos) {
- Some(stack) => {
- if stack.len() > 0 {
- log(format!("{} is already occupied.", pos));
- false
- } else if self.remaining_pieces(player, stone) == 0 {
- log(format!(
- "{} has no more remaining {} pieces.",
- player, stone
- ));
- false
- } else {
- true
- }
- }
- None => {
- log(format!("Internal error, lookup for {} failed.", pos));
+ if let Some(stack) = &self.board.get(self.pos_to_idx(pos)) {
+ if stack.len() > 0 {
+ (self.log)(format!("{} is already occupied.", pos));
false
+ } else if self.remaining_pieces(&player, &stone) == 0 {
+ (self.log)(format!(
+ "{} has no more remaining {} pieces.",
+ player, stone
+ ));
+ false
+ } else {
+ true
}
+ } else {
+ (self.log)(format!(
+ "Internal error: lookup for {} failed in is_legal_place.",
+ pos
+ ));
+ false
}
} else {
- log(format!("Position {} is not within bounds.", pos));
+ (self.log)(format!("Position {} is not within bounds.", pos));
false
}
}
@@ -184,119 +219,135 @@ impl Board {
pos: &Position,
direction: &Direction,
drops: &Vec<u8>,
- log: Logger,
) -> bool {
if self.within_bounds(pos) {
- match self.lookup_square(pos) {
- /* Rules for moving a stack:
- 0. There are stones
- 1. Top stone belongs to player
- 2. Zero or One stones dropped on starting square
- 3. Total number of stones moved does not exceed the carry capacity
- 4. Direction does not contain a capstone
- 5. Wall may only appear on last spot if it's capstone alone that covers
- 6. All stones are used up before then end of the board is met
- */
- Some(stack) => {
- if stack.len() == 0 {
- log(format!("{} has no stones to move.", pos));
- false
- } else if drops.len() == 0 {
- log(format!("A drop sequence for must be specified for a move."));
- false
- } else if stack[0].0 != *player {
- log(format!(
- "{} may not move the stack at {} as it belongs to {}.",
- player, pos, stack[0].0
- ));
- false
- } else if drops[0] > 1 {
- log(format!(
- "A move may only drop 0 or 1 stones at it's origin square."
- ));
- false
- // Do the sum in u32 just in case ?
- } else if drops.iter().map(|&d| d as u32).sum::<u32>() > self.size as u32 {
- log(format!(
- "A move may not exceed the carry limit of {} stones.",
- self.size
- ));
- false
+ /* Rules for moving a stack:
+ 0. There are stones
+ 1. Top stone belongs to player
+ 2. Zero or One stones dropped on starting square
+ 3. Total number of stones moved does not exceed the carry capacity
+ 4. Direction does not contain a capstone
+ 5. Wall may only appear on last spot if it's capstone alone that covers
+ 6. All stones are used up before then end of the board is met
+ */
+ if let Some(stack) = &self.board.get(self.pos_to_idx(pos)) {
+ if stack.len() == 0 {
+ (self.log)(format!("{} has no stones to move.", pos));
+ return false;
+ } else if drops.len() == 0 {
+ (self.log)(format!("A drop sequence for must be specified for a move."));
+ return false;
+ } else if stack[0].0 != *player {
+ (self.log)(format!(
+ "{} may not move the stack at {} as it belongs to {}.",
+ player, pos, stack[0].0
+ ));
+ return false;
+ } else if drops[0] > 1 {
+ (self.log)(format!(
+ "A move may only drop 0 or 1 stones at it's origin square."
+ ));
+ return false;
+ // Do the sum in u32 just in case ?
+ } else if drops.iter().map(|&d| d as u32).sum::<u32>() > self.size as u32 {
+ (self.log)(format!(
+ "A move may not exceed the carry limit of {} stones.",
+ self.size
+ ));
+ return false;
+ }
+ let mut steps: usize = drops.len() - 1;
+ let cap: bool = stack[0].1 == Stone::Capstone;
+ let mut pos_new = Position { x: pos.x, y: pos.y };
+
+ while steps > 0 {
+ steps -= 1;
+ if {
+ match direction {
+ Direction::Up => pos_new.y + 1 >= self.size,
+ Direction::Down => pos_new.y == 0,
+ Direction::Left => pos_new.x + 1 >= self.size,
+ Direction::Right => pos_new.x == 0,
+ }
+ } {
+ (self.log)(format!("A move may not extend past the board."));
+ return false;
} else {
- let mut steps: usize = drops.len() - 1;
- let cap: bool = stack[0].1 == Stone::Capstone;
- let mut pos_new = Position { x: pos.x, y: pos.y };
- let mut result = true;
- while steps > 0 {
- if {
- match direction {
- Direction::Up => pos_new.y + 1 >= self.size,
- Direction::Down => pos_new.y == 0,
- Direction::Left => pos_new.x + 1 >= self.size,
- Direction::Right => pos_new.x == 0,
- }
- } {
- log(format!("A move may not extend past the board."));
- result = false;
- break;
- } else {
- match direction {
- Direction::Up => pos_new.y += 1,
- Direction::Down => pos_new.y -= 1,
- Direction::Left => pos_new.x += 1,
- Direction::Right => pos_new.x -= 1,
- }
- match self.lookup_square(&pos_new) {
- Some(stack) => {
- if stack.len() > 0 {
- match stack[0].1 {
- Stone::Capstone => {
- log(format!(
- "A move may not cover a capstone."
- ));
- result = false;
- break;
- }
- Stone::Standing => {
- if (steps > 1) || (!cap) {
- log(format!(
- "A move may not cover a standing stone."
- ));
- result = false;
- break;
- }
- }
- Stone::Flat => (),
- }
- }
+ match direction {
+ Direction::Up => pos_new.y += 1,
+ Direction::Down => pos_new.y -= 1,
+ Direction::Left => pos_new.x += 1,
+ Direction::Right => pos_new.x -= 1,
+ }
+ if let Some(stack) = &self.board.get(self.pos_to_idx(&pos_new)) {
+ if stack.len() > 0 {
+ match stack[0].1 {
+ Stone::Capstone => {
+ (self.log)(format!("A move may not cover a capstone."));
+ return false;
}
- None => {
- log(format!("Internal error, lookup for {} failed.", pos));
- result = false;
- break;
+ Stone::Standing => {
+ if (steps > 1) || (!cap) {
+ (self.log)(format!(
+ "A move may not cover a standing stone."
+ ));
+ return false;
+ }
}
+ Stone::Flat => (),
}
}
- steps -= 1;
+ } else {
+ (self.log)(format!(
+ "Internal error: lookup for {} failed in is_legal_move (1).",
+ pos_new
+ ));
+ return false;
}
- result
}
}
- None => {
- log(format!("Internal error, lookup for {} failed.", pos));
- false
- }
+ return true;
+ } else {
+ (self.log)(format!(
+ "Internal error: lookup for {} failed in is_legal_move (2).",
+ pos
+ ));
+ return false;
}
} else {
- log(format!("Position {} is not within bounds.", pos));
+ (self.log)(format!("Position {} is not within bounds.", pos));
false
}
}
- fn is_legal_action(&self, mov: &Action, log: Logger) -> bool {
- match mov {
- Action::Place(player, pos, stone) => self.is_legal_place(player, pos, stone, log),
- Action::Move(player, pos, direction, drops) => false,
+ pub fn is_legal_action(&self, act: &Action) -> bool {
+ match act {
+ Action::Place(player, pos, stone) => self.is_legal_place(player, pos, stone),
+ Action::Move(player, pos, direction, drops) => {
+ self.is_legal_move(player, pos, direction, drops)
+ }
+ }
+ }
+
+ // Unconditional and so could lead to invalid state, but private
+ fn place_stone(&mut self, player: Player, pos: Position, stone: Stone) {
+ let idx = self.pos_to_idx(&pos);
+ self.board[idx].push((player, stone));
+ }
+
+ fn move_stack(&mut self, player: Player, pos: Position, direction: Direction, drops: Vec<u8>) {}
+
+ pub fn perform_action(&mut self, act: Action) -> bool {
+ if self.is_legal_action(&act) {
+ match act {
+ Action::Place(player, pos, stone) => self.place_stone(player, pos, stone),
+ Action::Move(player, pos, direction, drops) => {
+ self.move_stack(player, pos, direction, drops)
+ }
+ }
+ true
+ } else {
+ false
}
}
}
diff --git a/src/main.rs b/src/main.rs
index 5794458..4528e96 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -27,7 +27,7 @@ impl BoardGui {
let y: i32 = pos.y as i32;
let x: i32 = pos.x as i32;
if (y < self.size) && (x < self.size) {
- let y = y * self.cell_height + 1;
+ let y = (self.size - y) * self.cell_height + 1;
let x = self.hoff + x * self.cell_width + 1;
self.win.mv(y, x);
self.win.refresh();
@@ -123,6 +123,24 @@ impl MoveEntry {
// self.win.mvchgat(0,12,10,A_NORMAL,COLOR_PAIR(1));
self.win.refresh();
}
+
+ fn read_action(&self) -> &str {
+ "a1"
+ }
+}
+
+struct MessageLog {
+ win: Window,
+}
+
+impl MessageLog {
+ fn init(&self) {}
+
+ fn log_error(&self, str: String) {
+ // self.win.clear();
+ self.win.addstr(str);
+ self.win.refresh();
+ }
}
fn main() {
@@ -143,6 +161,7 @@ fn main() {
let bgw = newwin(25, 40, 0, 0);
let mew = newwin(1, 30, 25, 0);
+ let mlw = newwin(3, 70, 26, 0);
let mut bg = BoardGui {
win: bgw,
@@ -155,8 +174,18 @@ fn main() {
let me = MoveEntry { win: mew };
+ let ml = MessageLog { win: mlw };
+
bg.init();
me.init();
+ ml.init();
+
+ let board_option = Board::new(5, |s| MessageLog::log_error(&ml, s));
+ if let Some(mut board) = board_option {
+ let pos = Position { x: 0, y: 0 };
+ board.perform_action(Action::Place(Player::White, pos, Stone::Flat));
+ board.is_legal_action(&Action::Move(Player::White, pos, Direction::Up, vec![0, 1]));
+ }
loop {
if let Some(inp) = screen.getch() {