From fcc5d5727a1bf0ab5d0e473ee56408c76e54492e Mon Sep 17 00:00:00 2001 From: Artlef Date: Sat, 14 Dec 2019 23:37:46 +0100 Subject: [PATCH] First commit --- .gitignore | 2 + Cargo.lock | 113 +++++++++++++++++++++++ Cargo.toml | 11 +++ README.md | 4 + src/chessboard.rs | 222 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 17 ++++ src/main.rs | 5 ++ 7 files changed, 374 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/chessboard.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2a47d1c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "atty" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "btoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "clichess" +version = "0.1.0" +dependencies = [ + "colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "shakmaty 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "colored" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "shakmaty" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "btoi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum btoi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ced8205e70d9e553d008d53ded735808fa6133597318d48f74fc2bf9861471" +"checksum colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "433e7ac7d511768127ed85b0c4947f47a254131e37864b2dc13f52aa32cd37e5" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +"checksum shakmaty 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "995611c42b955b8661f1eaf19e83beec36e72ec3ac51ed41a9018a19323c8022" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1329081 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "clichess" +version = "0.1.0" +authors = ["Artlef "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +shakmaty = "0.16.0" +colored = "1.9" diff --git a/README.md b/README.md new file mode 100644 index 0000000..01499db --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +Clichess +======== + +Chess playable with a command line interface. diff --git a/src/chessboard.rs b/src/chessboard.rs new file mode 100644 index 0000000..f782d59 --- /dev/null +++ b/src/chessboard.rs @@ -0,0 +1,222 @@ +use colored::Colorize; +use shakmaty::fen; +use shakmaty::fen::Fen; +use shakmaty::fen::ParseFenError; +use shakmaty::san::ParseSanError; +use shakmaty::san::San; +use shakmaty::san::SanError; +use shakmaty::{Chess, Color, IllegalMoveError, Position, PositionError, Setup, Square}; +use std::fmt; +use std::fs::File; +use std::io; +use std::io::{ErrorKind, Read, Write}; + +struct SquareToPrint { + color: String, + background_color: String, + square_representation: String, +} + +#[derive(Debug)] +pub enum MoveInputError { + SanSanError(SanError), + SanParseSanError(ParseSanError), + ShakmatyIllegalMoveError(IllegalMoveError), +} + +impl From for MoveInputError { + fn from(error: SanError) -> Self { + MoveInputError::SanSanError(error) + } +} + +impl From for MoveInputError { + fn from(error: ParseSanError) -> Self { + MoveInputError::SanParseSanError(error) + } +} + +impl From for MoveInputError { + fn from(error: IllegalMoveError) -> Self { + MoveInputError::ShakmatyIllegalMoveError(error) + } +} + +impl fmt::Display for MoveInputError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MoveInputError::SanSanError(san_error) => san_error.fmt(f), + MoveInputError::SanParseSanError(parse_san_error) => parse_san_error.fmt(f), + MoveInputError::ShakmatyIllegalMoveError(illegal_move_error) => { + illegal_move_error.fmt(f) + } + } + } +} + +#[derive(Debug)] +pub enum ReadingFileError { + ReadParseFenError(ParseFenError), + ReadFileError(io::Error), + ReadPositionError(PositionError), +} + +impl From for ReadingFileError { + fn from(error: ParseFenError) -> Self { + ReadingFileError::ReadParseFenError(error) + } +} + +impl From for ReadingFileError { + fn from(error: io::Error) -> Self { + ReadingFileError::ReadFileError(error) + } +} + +impl From for ReadingFileError { + fn from(error: PositionError) -> Self { + ReadingFileError::ReadPositionError(error) + } +} + +impl fmt::Display for ReadingFileError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ReadingFileError::ReadParseFenError(error) => error.fmt(f), + ReadingFileError::ReadFileError(error) => error.fmt(f), + ReadingFileError::ReadPositionError(error) => error.fmt(f), + } + } +} + +pub fn retrieve_chess_from_file_or_create(filename: &str) -> Result { + match retrieve_chess_from_file(filename) { + Ok(chess) => Ok(chess), + Err(ReadingFileError::ReadFileError(error)) => { + if error.kind() == ErrorKind::NotFound { + create_default_fen_in_file(filename) + } else { + Err(ReadingFileError::ReadFileError(error)) + } + } + other_error => other_error, + } +} + +pub fn save_chess_position(chess: &Chess, filename: &str) -> std::io::Result<()> { + let mut file = File::create(filename)?; + file.write_all(fen::fen(chess).as_bytes())?; + Ok(()) +} + +fn retrieve_chess_from_file(filename: &str) -> Result { + let mut file = File::open(filename)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let setup: Fen = contents.trim().parse()?; + let position: Chess = setup.position()?; + Ok(position) +} + +fn create_default_fen_in_file(filename: &str) -> Result { + let chess = Chess::default(); + save_chess_position(&Chess::default(), filename)?; + Ok(chess) +} + +pub fn board_representation(chess: &Chess, side: Color) -> String { + let mut s = String::from(""); + let mut rank_numbers; + if side == Color::White { + rank_numbers = 8; + } else { + rank_numbers = 1; + } + for v in board_string_representation(chess, side) { + for square_to_print in v { + s.push_str(&format!( + "{}", + square_to_print + .square_representation + .color(square_to_print.color) + .on_color(square_to_print.background_color) + )); + } + s.push_str(&format!("{}\n\r", rank_numbers)); + if side == Color::White { + rank_numbers -= 1; + } else { + rank_numbers += 1; + } + } + let mut files = vec!["a", "b", "c", "d", "e", "f", "g", "h"]; + if side == Color::Black { + files.reverse(); + } + for file in files { + s.push_str(&format!("{} ", file)); + } + s +} + +fn board_string_representation(chess: &Chess, side: Color) -> Vec> { + let mut full_board_to_print = Vec::new(); + for _ in 0..8 { + full_board_to_print.push(Vec::new()) + } + for i in 0..8 { + for j in 0..8 { + let square = Square::new(i + 8 * j); + let square_to_print = SquareToPrint { + color: "black".to_string(), + background_color: if square.is_light() { + "white".to_string() + } else { + "green".to_string() + }, + square_representation: get_square_representation(&square, chess), + }; + full_board_to_print[j as usize].push(square_to_print); + } + } + if side == Color::White { + full_board_to_print.reverse(); + } else { + for j in 0..8 { + full_board_to_print[j as usize].reverse(); + } + } + full_board_to_print +} + +pub fn try_to_play_move(chess: &Chess, movestr: String) -> Result { + let san: San = movestr.parse()?; + let mv = san.to_move(chess)?; + Ok((*chess).clone().play(&mv)?) +} + +fn get_square_representation(square: &Square, chess: &Chess) -> String { + let board = (*chess).board(); + match board.piece_at(*square) { + Some(piece) => format!("{} ", piece_char_to_utf8(piece.char())), + None => " ".to_string(), + } +} + +fn piece_char_to_utf8(piece: char) -> char { + match piece { + 'P' => '♙', + 'p' => '♟', + 'N' => '♘', + 'n' => '♞', + 'B' => '♗', + 'b' => '♝', + 'R' => '♖', + 'r' => '♜', + 'Q' => '♕', + 'q' => '♛', + 'K' => '♔', + 'k' => '♚', + c => c, + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3d0616e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,17 @@ +use std::io; +pub mod chessboard; +use shakmaty::{Chess, Color}; + +pub fn play() { + let mut chess = Chess::default(); + loop { + println!("{}", chessboard::board_representation(&chess, Color::White)); + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + print!("try to play: {}", input); + match chessboard::try_to_play_move(&chess, String::from(input.trim())) { + Ok(played_chess) => chess = played_chess, + Err(e) => println!("Error: {}", e), + }; + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0fe36f4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +use clichess; + +fn main() { + clichess::play(); +}