///|
/// Side of a piece or the player to move. Red moves first and
/// advances from rank 0 towards rank 9.
pub(all) enum Side {
  Red
  Black
} derive(Eq, Debug)

///|
/// The opposing side.
pub fn Side::opponent(self : Side) -> Side {
  match self {
    Red => Black
    Black => Red
  }
}

///|
pub fn Side::is_red(self : Side) -> Bool {
  self is Red
}

///|
/// The seven Xiangqi piece kinds.
pub(all) enum Kind {
  King
  Advisor
  Elephant
  Horse
  Rook
  Cannon
  Pawn
} derive(Eq, Debug)

///|
/// A single piece on the board.
pub struct Piece {
  side : Side
  kind : Kind
} derive(Eq, Debug)

///|
/// Constructors for the two sides.
pub fn Piece::red(kind : Kind) -> Piece {
  { side: Red, kind, }
}

///|
pub fn Piece::black(kind : Kind) -> Piece {
  { side: Black, kind, }
}

///|
/// Board position. `file` is 0..8 (files a..i, a on Red's left),
/// `rank` is 0..9 (rank 0 is Red's back rank, rank 9 is Black's).
pub struct Pos {
  file : Int
  rank : Int
} derive(Eq, Debug)

///|
pub fn Pos::new(file : Int, rank : Int) -> Pos {
  { file, rank, }
}

///|
/// True when the position lies on the 9x10 board.
pub fn Pos::is_valid(self : Pos) -> Bool {
  self.file >= 0 && self.file < 9 && self.rank >= 0 && self.rank < 10
}

///|
/// Linear index into the 90-square board array.
pub fn Pos::index(self : Pos) -> Int {
  self.rank * 9 + self.file
}

///|
/// True when the position is inside the palace of `side`.
pub fn Pos::in_palace(self : Pos, side : Side) -> Bool {
  if self.file >= 3 && self.file <= 5 {
    if side.is_red() {
      self.rank >= 0 && self.rank <= 2
    } else {
      self.rank >= 7 && self.rank <= 9
    }
  } else {
    false
  }
}

///|
/// True when the position is on `side`'s own half of the river.
/// The river lies between rank 4 and rank 5.
pub fn Pos::own_half(self : Pos, side : Side) -> Bool {
  if side.is_red() {
    self.rank <= 4
  } else {
    self.rank >= 5
  }
}

///|
/// A move from `from` to `to`.
pub struct Move {
  from : Pos
  to : Pos
} derive(Eq, Debug)

///|
pub fn Move::new(from : Pos, to : Pos) -> Move {
  { from, to, }
}