///|
/// The text printed for `moonchess help`.
let usage : String = "moonchess - a MoonBit chess core\n\nUsage: moonchess [FEN]\n\nCommands:\n board [FEN] draw the board with FEN letters (default)\n unicode [FEN] draw the board with Unicode pieces\n fen [FEN] print the canonical six-field FEN\n moves [FEN] list the pseudo-legal moves of the side to move\n validate [FEN] report whether a FEN document is valid\n help show this message\n\nWhen no FEN is given the standard starting position is used."
///|
/// Joins the tokens of a FEN document back into one string.
fn join_tokens(tokens : Array[String], from : Int32) -> String {
let buf = StringBuilder()
for index = from; index < tokens.length(); index = index + 1 {
if index > from {
buf.write_char(' ')
}
buf.write_string(tokens[index])
}
buf.to_string()
}
///|
fn main {
let raw = @env.args()
let args : Array[String] = []
for index = 1; index < raw.length(); index = index + 1 {
args.push(raw[index])
}
let command = if args.length() == 0 { "board" } else { args[0] }
let fen_text = if args.length() <= 1 {
@chess.start_fen()
} else {
join_tokens(args, 1)
}
match command {
"help" | "--help" | "-h" => println(usage)
"board" => draw(fen_text, false)
"unicode" => draw(fen_text, true)
"fen" => print_canonical_fen(fen_text)
"moves" => list_moves(fen_text)
"validate" => validate(fen_text)
_ => {
println("error: unknown command '" + command + "'")
println(usage)
abort("unknown command")
}
}
}
///|
/// Draws one position, optionally with the Unicode piece symbols.
fn draw(fen_text : String, unicode : Bool) -> Unit {
match @chess.Position::try_from_fen(fen_text) {
Ok(position) => {
if unicode {
println(position.to_unicode())
} else {
println(position.to_ascii())
}
println("")
println(position.to_fen())
}
Err(error) => report(error)
}
}
///|
/// Prints the canonical six-field FEN of one document.
fn print_canonical_fen(fen_text : String) -> Unit {
match @chess.Position::try_from_fen(fen_text) {
Ok(position) => println(position.to_fen())
Err(error) => report(error)
}
}
///|
/// Lists the pseudo-legal moves of the side to move, twelve to a line.
fn list_moves(fen_text : String) -> Unit {
match @chess.Position::try_from_fen(fen_text) {
Ok(position) => {
let moves = position.pseudo_legal_moves()
let buf = StringBuilder()
buf.write_string(position.turn().name())
buf.write_string(" to move, ")
buf.write_string(moves.length().to_string())
buf.write_string(" pseudo-legal moves, in check: ")
buf.write_string(position.in_check(position.turn()).to_string())
println(buf.to_string())
let lines = StringBuilder()
let mut column = 0
for candidate in moves {
if column == 12 {
lines.write_char('\n')
column = 0
}
lines.write_string(candidate.to_uci())
lines.write_char(' ')
column = column + 1
}
println(lines.to_string())
}
Err(error) => report(error)
}
}
///|
/// Reports whether a FEN document is valid and summarises it.
fn validate(fen_text : String) -> Unit {
match @chess.Position::try_from_fen(fen_text) {
Ok(position) => {
let summary = StringBuilder()
summary.write_string("valid: ")
summary.write_string(position.piece_count().to_string())
summary.write_string(" pieces, ")
summary.write_string(position.turn().name())
summary.write_string(" to move, ")
summary.write_string(position.castling().to_fen())
summary.write_string(" castling")
println(summary.to_string())
}
Err(error) => report(error)
}
}
///|
/// Prints a FEN error and stops the program.
fn report(error : @chess.FenError) -> Unit {
println("error: " + error.message())
abort("invalid FEN document")
}