///|
/// Render one rank row with pieces or dots.
fn render_rank(board : Board, rank : Int) -> String {
  let sb = StringBuilder()
  sb.write_string(num_label(rank))
  sb.write_string(" ")
  for file in 0..<9 {
    match board.get(Pos::new(file, rank)) {
      Some(piece) => sb.write_string(display_char(piece))
      None => sb.write_string("・")
    }
    if file < 8 {
      sb.write_string(" ")
    }
  }
  sb.write_string("\n")
  sb.to_string()
}

///|
/// Piece glyph for terminal display; traditional characters make the
/// two sides visually distinct.
fn display_char(piece : Piece) -> String {
  match piece.kind {
    King => if piece.side.is_red() { "帅" } else { "將" }
    Advisor => if piece.side.is_red() { "仕" } else { "士" }
    Elephant => if piece.side.is_red() { "相" } else { "象" }
    Horse => if piece.side.is_red() { "馬" } else { "马" }
    Rook => if piece.side.is_red() { "車" } else { "车" }
    Cannon => if piece.side.is_red() { "炮" } else { "砲" }
    Pawn => if piece.side.is_red() { "兵" } else { "卒" }
  }
}

///|
/// Rank label: Arabic digits 0-9 (rank 0 = Red's back rank).
fn num_label(rank : Int) -> String {
  rank.to_string()
}

///|
/// Full board rendering including river and side-to-move caption.
pub fn Board::render_board(self : Board) -> String {
  let sb = StringBuilder()
  let mut rank = 9
  while rank >= 0 {
    sb.write_string(render_rank(self, rank))
    if rank == 5 {
      sb.write_string("  ~ 楚 河 ~ 汉 界 ~\n")
    }
    rank = rank - 1
  }
  sb.write_string("   a b c d e f g h i\n")
  sb.write_string(
    if self.side_to_move.is_red() {
      "轮到红方\n"
    } else {
      "轮到黑方\n"
    },
  )
  sb.to_string()
}