Repository: thomas-mauran/chess-tui (language: rust)

Issue:
feat: Implement UCI (Universal Chess Interface)
## Implement UCI
[UCI](https://fr.wikipedia.org/wiki/Universal_Chess_Interface) is an interface made to communicate between a UI and a chess engine. Implementing UCI will let us connect to chess engines like [Stockfish](https://github.com/official-stockfish/Stockfish) or Lichess api to play against other players



Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index e3eae6b..ca1a137 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -20,6 +20,54 @@ version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
 
+[[package]]
+name = "anstream"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -44,6 +92,15 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
 
+[[package]]
+name = "castaway"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
+dependencies = [
+ "rustversion",
+]
+
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -54,8 +111,70 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 name = "chess-tui"
 version = "1.1.0"
 dependencies = [
+ "clap",
  "crossterm 0.26.1",
+ "dirs",
  "ratatui",
+ "uci",
+]
+
+[[package]]
+name = "clap"
+version = "4.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "compact_str"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
+dependencies = [
+ "castaway",
+ "cfg-if",
+ "itoa",
+ "ryu",
+ "static_assertions",
 ]
 
 [[package]]
@@ -99,12 +218,44 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "dirs"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "either"
 version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.14.3"
@@ -129,19 +280,36 @@ checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
 
 [[package]]
 name = "itertools"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
 dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
 [[package]]
 name = "libc"
 version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
+[[package]]
+name = "libredox"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
+dependencies = [
+ "bitflags 2.4.1",
+ "libc",
+ "redox_syscall",
+]
+
 [[package]]
 name = "lock_api"
 version = "0.4.11"
@@ -152,6 +320,15 @@ dependencies = [
  "scopeguard",
 ]
 
+[[package]]
+name = "log"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
+dependencies = [
+ "log 0.4.20",
+]
+
 [[package]]
 name = "log"
 version = "0.4.20"
@@ -174,9 +351,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
 dependencies = [
  "libc",
- "log",
+ "log 0.4.20",
  "wasi",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -185,6 +362,12 @@ version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
 [[package]]
 name = "parking_lot"
 version = "0.12.1"
@@ -205,7 +388,7 @@ dependencies = [
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-targets",
+ "windows-targets 0.48.5",
 ]
 
 [[package]]
@@ -234,17 +417,19 @@ dependencies = [
 
 [[package]]
 name = "ratatui"
-version = "0.24.0"
+version = "0.26.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425"
+checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8"
 dependencies = [
  "bitflags 2.4.1",
  "cassowary",
+ "compact_str",
  "crossterm 0.27.0",
  "indoc",
  "itertools",
  "lru",
  "paste",
+ "stability",
  "strum",
  "unicode-segmentation",
  "unicode-width",
@@ -259,12 +444,29 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
+[[package]]
+name = "redox_users"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
 [[package]]
 name = "rustversion"
 version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
 
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -307,26 +509,59 @@ version = "1.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
 
+[[package]]
+name = "stability"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
 [[package]]
 name = "strum"
-version = "0.25.0"
+version = "0.26.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
+checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f"
 dependencies = [
  "strum_macros",
 ]
 
 [[package]]
 name = "strum_macros"
-version = "0.25.3"
+version = "0.26.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
  "rustversion",
- "syn",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -340,6 +575,35 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "thiserror"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "uci"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9705b2de483a94b19d1aa8fd1519914a782977d5a0f8a896d2ca558e2255ee0"
+dependencies = [
+ "log 0.3.9",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.12"
@@ -358,6 +622,12 @@ version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
 
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
@@ -398,7 +668,16 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
 ]
 
 [[package]]
@@ -407,13 +686,28 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
 ]
 
 [[package]]
@@ -422,42 +716,84 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
 [[package]]
 name = "zerocopy"
 version = "0.7.29"
@@ -475,5 +811,5 @@ checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.39",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index 60f0f76..e669f5d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,9 +10,12 @@ homepage = "https://github.com/thomas-mauran/chess-tui"
 repository = "https://github.com/thomas-mauran/chess-tui"
 
 [dependencies]
+clap = { version = "4.4.11", features = ["derive"] }
 crossterm = "0.26.1"
-ratatui = "0.24.0"
+dirs = "5.0.1"
+ratatui = "0.26.1"
+uci = "0.1.3"
 
 [features]
 chess-tui = []
-default = ["chess-tui"]
\ No newline at end of file
+default = ["chess-tui"]
diff --git a/README.md b/README.md
index a359fe4..d7abe6f 100644
--- a/README.md
+++ b/README.md
@@ -80,3 +80,16 @@ You can find the roadmap of the project [here](https://github.com/users/thomas-m
 ### Crates.io
 
 The project is also available on crates.io [here](https://crates.io/crates/chess-tui)
+
+### Connect a chess engine
+
+You can play chess-tui with any UCI compatible chess engines. To do so you will need to use the -e command to give the chess engine binary path.
+
+Example:
+
+```bash
+chess-tui -e /your/bin/path
+```
+
+Here I installed stockfish using homebrew and gave chess-tui the path the the engine binary.
+This command will store in your home directory the chess engine path so you don't have to relink it everytime !
diff --git a/examples/demo.gif b/examples/demo.gif
index 19cd2c3..b14afb8 100644
Binary files a/examples/demo.gif and b/examples/demo.gif differ
diff --git a/examples/demo.tape b/examples/demo.tape
index 864cb1d..7dff9d8 100644
--- a/examples/demo.tape
+++ b/examples/demo.tape
@@ -21,6 +21,7 @@ Sleep 1s
 Down @0.3s
 Down @0.3s
 Down @0.3s
+Down @0.3s
 Space @0.3s
 Sleep 3s
 
diff --git a/examples/helper.gif b/examples/helper.gif
index 3de5446..4838e43 100644
Binary files a/examples/helper.gif and b/examples/helper.gif differ
diff --git a/examples/helper.tape b/examples/helper.tape
index e380825..4010e76 100644
--- a/examples/helper.tape
+++ b/examples/helper.tape
@@ -18,6 +18,8 @@ Set WindowBarSize 40
 Type "cargo run" Sleep 500ms  Enter
 
 Sleep 2s
+Down @0.3s
+Down @0.3s
 Space @0.3s
 Sleep 1s
 
diff --git a/examples/play_against_bot.gif b/examples/play_against_bot.gif
new file mode 100644
index 0000000..318014a
Binary files /dev/null and b/examples/play_against_bot.gif differ
diff --git a/examples/play_against_bot.tape b/examples/play_against_bot.tape
new file mode 100644
index 0000000..910d408
--- /dev/null
+++ b/examples/play_against_bot.tape
@@ -0,0 +1,57 @@
+Output examples/play_against_bot.gif
+
+Require echo
+
+Set Margin 100
+Set MarginFill "#674EBB"
+
+Set BorderRadius 10
+Set Shell zsh
+
+Set FontSize 14
+Set Width 1920
+Set Height 1080
+
+Set WindowBar Colorful
+Set WindowBarSize 40
+
+Type "cargo run" Sleep 500ms  Enter
+
+Sleep 2s
+Down @0.3s
+Space @0.3s
+Sleep 1s
+
+Down @0.3s
+Down @0.3s
+Space @0.3s
+Space @0.3s
+
+Down @0.3s
+Space @0.3s
+Space @0.3s
+
+Up @0.3s
+Space @0.3s
+Space @0.3s
+
+Up @0.3s
+Left @0.3s
+Space @0.3s
+Space @0.3s
+
+Up @0.3s
+Left @0.3s
+Space @0.3s
+Right @0.3s
+Space @0.3s
+
+Up @0.3s
+Space @0.3s
+Right @0.3s
+Space @0.3s
+Right @0.3s
+Space @0.3s
+
+
+Sleep 5s
diff --git a/src/app.rs b/src/app.rs
index 6e7babf..bb73cd0 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,24 +1,23 @@
-use crate::board::Board;
+use crate::{board::Board, constants::Pages};
 use std::error;
 
 /// Application result type.
 pub type AppResult<T> = std::result::Result<T, Box<dyn error::Error>>;
 
 /// Application.
-#[derive(Debug)]
 pub struct App {
     /// Is the application running?
     pub running: bool,
     /// board
     pub board: Board,
-    /// show help popup
+    /// Current page to render
+    pub current_page: Pages,
+    /// Used to show the help popup during the game or in the home menu
     pub show_help_popup: bool,
-    /// show credit popup
-    pub show_credit_popup: bool,
-    /// show home menu
-    pub show_home_menu: bool,
     /// menu current cursor
     pub menu_cursor: u8,
+    /// path of the chess engine
+    pub chess_engine_path: Option<String>,
 }
 
 impl Default for App {
@@ -26,10 +25,10 @@ impl Default for App {
         Self {
             running: true,
             board: Board::default(),
+            current_page: Pages::Home,
             show_help_popup: false,
-            show_credit_popup: false,
-            show_home_menu: true,
             menu_cursor: 0,
+            chess_engine_path: None,
         }
     }
 }
@@ -41,10 +40,18 @@ impl App {
     }
 
     pub fn toggle_help_popup(&mut self) {
-        self.show_help_popup = !self.show_help_popup;
+        if self.current_page == Pages::Home {
+            self.current_page = Pages::Help
+        } else {
+            self.current_page = Pages::Home
+        }
     }
     pub fn toggle_credit_popup(&mut self) {
-        self.show_credit_popup = !self.show_credit_popup;
+        if self.current_page == Pages::Home {
+            self.current_page = Pages::Credit
+        } else {
+            self.current_page = Pages::Home
+        }
     }
 
     /// Handles the tick event of the terminal.
@@ -59,12 +66,12 @@ impl App {
         if self.menu_cursor > 0 {
             self.menu_cursor -= 1
         } else {
-            self.menu_cursor = 2
+            self.menu_cursor = 3
         }
     }
 
     pub fn menu_cursor_down(&mut self) {
-        if self.menu_cursor < 2 {
+        if self.menu_cursor < 3 {
             self.menu_cursor += 1
         } else {
             self.menu_cursor = 0
@@ -79,9 +86,10 @@ impl App {
 
     pub fn menu_select(&mut self) {
         match self.menu_cursor {
-            0 => self.show_home_menu = false,
-            1 => self.show_help_popup = true,
-            2 => self.show_credit_popup = true,
+            0 => self.current_page = Pages::Solo,
+            1 => self.current_page = Pages::Bot,
+            2 => self.current_page = Pages::Help,
+            3 => self.current_page = Pages::Credit,
             _ => {}
         }
     }
diff --git a/src/board.rs b/src/board.rs
index 42f8f37..6964d57 100644
--- a/src/board.rs
+++ b/src/board.rs
@@ -2,7 +2,8 @@ use crate::{
     constants::{BLACK, UNDEFINED_POSITION, WHITE},
     pieces::{PieceColor, PieceType},
     utils::{
-        color_to_ratatui_enum, convert_position_into_notation, get_int_from_char,
+        col_to_letter, color_to_ratatui_enum, convert_notation_into_position,
+        convert_position_into_notation, did_piece_already_move, get_int_from_char,
         get_king_coordinates, get_piece_color, get_piece_type, get_player_turn_in_modulo,
         is_getting_checked, is_valid,
     },
@@ -14,8 +15,8 @@ use ratatui::{
     widgets::{Block, BorderType, Borders, Padding, Paragraph},
     Frame,
 };
+use uci::Engine;
 
-#[derive(Debug)]
 pub struct Board {
     pub board: [[Option<(PieceType, PieceColor)>; 8]; 8],
     pub cursor_coordinates: [i8; 2],
@@ -29,6 +30,8 @@ pub struct Board {
     pub is_promotion: bool,
     pub promotion_cursor: i8,
     pub consecutive_non_pawn_or_capture: i32,
+    pub engine: Option<Engine>,
+    pub is_game_against_bot: bool,
 }
 
 impl Default for Board {
@@ -91,6 +94,8 @@ impl Default for Board {
             is_promotion: false,
             promotion_cursor: 0,
             consecutive_non_pawn_or_capture: 0,
+            engine: None,
+            is_game_against_bot: false,
         }
     }
 }
@@ -114,6 +119,8 @@ impl Board {
             is_promotion: false,
             promotion_cursor: 0,
             consecutive_non_pawn_or_capture: 0,
+            engine: None,
+            is_game_against_bot: false,
         }
     }
 
@@ -125,6 +132,16 @@ impl Board {
     pub fn set_player_turn(&mut self, player_turn: PieceColor) {
         self.player_turn = player_turn;
     }
+
+    pub fn set_engine(&mut self, engine_path: &str) {
+        self.is_game_against_bot = true;
+
+        self.engine = match Engine::new(engine_path) {
+            Ok(engine) => Some(engine),
+            _ => panic!("An error occcured with the selected chess engine path: {} Make sure you specified the right path using chess-tui -e", engine_path),
+        }
+    }
+
     // Check if a cell has been selected
     fn is_cell_selected(&self) -> bool {
         self.selected_coordinates[0] != UNDEFINED_POSITION
@@ -287,6 +304,18 @@ impl Board {
                     self.move_piece_on_the_board(selected_coords_usize, cursor_coords_usize);
                     self.unselect_cell();
                     self.switch_player_turn();
+                    // If we play against a bot we will play his move and switch the player turn again
+                    if self.is_game_against_bot {
+                        self.is_promotion = self.is_latest_move_promotion();
+                        if self.is_promotion == false {
+                            self.is_checkmate = self.is_checkmate();
+                            self.is_promotion = self.is_latest_move_promotion();
+                            if !self.is_checkmate {
+                                self.bot_move();
+                                self.switch_player_turn();
+                            }
+                        }
+                    }
                     self.is_draw = self.is_draw();
                 }
             }
@@ -295,6 +324,146 @@ impl Board {
         self.is_promotion = self.is_latest_move_promotion();
     }
 
+    pub fn bot_move(&mut self) {
+        let engine = match &self.engine {
+            Some(engine) => engine,
+            None => panic!("Missing the chess engine"),
+        };
+
+        engine.set_position(&self.fen_position()).unwrap();
+
+        let best_move = engine.bestmove();
+        let movement = match best_move {
+            Ok(movement) => movement,
+            Err(_) => panic!("An error as occured"),
+        };
+        let converted_move = convert_notation_into_position(movement);
+
+        let from_y = get_int_from_char(converted_move.chars().next());
+        let from_x = get_int_from_char(converted_move.chars().nth(1));
+        let to_y = get_int_from_char(converted_move.chars().nth(2));
+        let to_x = get_int_from_char(converted_move.chars().nth(3));
+
+        self.move_piece_on_the_board(
+            [from_y as usize, from_x as usize],
+            [to_y as usize, to_x as usize],
+        );
+    }
+    // Convert the history and game status to a FEN string
+    pub fn fen_position(&self) -> String {
+        let mut result = String::new();
+
+        for i in 0..8i8 {
+            for j in 0..8i8 {
+                match (
+                    get_piece_type(self.board, [i, j]),
+                    get_piece_color(self.board, [i, j]),
+                ) {
+                    (piece_type, piece_color) => {
+                        match PieceType::piece_to_fen_enum(piece_type, piece_color) {
+                            // Pattern match directly on the result of piece_to_fen_enum
+                            "" => {
+                                // Check if the string is not empty before using chars().last()
+                                if let Some(last_char) = result.chars().last() {
+                                    if last_char.is_ascii_digit() {
+                                        let incremented_char = char::from_digit(
+                                            last_char.to_digit(10).unwrap_or(0) + 1,
+                                            10,
+                                        )
+                                        .unwrap_or_default();
+                                        // Remove the old number and add the new incremented one
+                                        result.pop();
+                                        result.push_str(incremented_char.to_string().as_str());
+                                    } else {
+                                        result.push('1');
+                                    }
+                                } else {
+                                    result.push('1');
+                                }
+                            }
+                            letter => {
+                                // If the result is not an empty string, push '1'
+                                result.push_str(letter);
+                            }
+                        }
+                    }
+                }
+            }
+            result.push('/')
+        }
+
+        // we remove the last / and specify the player turn (black)
+        result.pop();
+
+        // We say it is blacks turn to play
+        result.push_str(" b");
+
+        // We add the castles availabilities for black
+        if !did_piece_already_move(&self.move_history, (Some(PieceType::King), [0, 4]))
+            && !is_getting_checked(self.board, PieceColor::Black, &self.move_history)
+        {
+            // king side black castle availability
+            if !did_piece_already_move(&self.move_history, (Some(PieceType::Rook), [0, 7])) {
+                result.push_str(" k");
+            }
+            // queen side black castle availability
+            if !did_piece_already_move(&self.move_history, (Some(PieceType::Rook), [0, 0])) {
+                result.push_str("q");
+            }
+        } else {
+            result.push_str(" -")
+        }
+
+        // We check if the latest move is a pawn moving 2 cells, meaning the next move can be en passant
+        if self.did_pawn_move_two_cells() {
+            // Use an if-let pattern for better readability
+            if let Some((_, latest_move_string)) = self.move_history.last() {
+                let mut converted_move: String = String::new();
+
+                if let (Some(from_y_char), Some(from_x_char)) = (
+                    latest_move_string.chars().nth(0),
+                    latest_move_string.chars().nth(1),
+                ) {
+                    let from_y = get_int_from_char(Some(from_y_char)) - 1;
+                    let from_x = get_int_from_char(Some(from_x_char));
+
+                    converted_move += &col_to_letter(from_x);
+                    converted_move += &format!("{}", 8 - from_y).to_string();
+
+                    result.push_str(" ");
+                    result.push_str(&converted_move);
+                }
+            }
+        } else {
+            result.push_str(" -");
+        }
+
+        result.push_str(" ");
+
+        result.push_str(&self.consecutive_non_pawn_or_capture.to_string());
+        result.push_str(" ");
+
+        result.push_str(&(self.move_history.len() / 2).to_string());
+
+        result
+    }
+
+    pub fn did_pawn_move_two_cells(&self) -> bool {
+        match self.move_history.last() {
+            Some((Some(piece_type), move_string)) => {
+                let from_y = get_int_from_char(move_string.chars().next());
+                let to_y = get_int_from_char(move_string.chars().nth(2));
+
+                let distance = (to_y - from_y).abs();
+
+                if piece_type == &PieceType::Pawn && distance == 2 {
+                    return true;
+                }
+                return false;
+            }
+            _ => false,
+        }
+    }
     pub fn promote_piece(&mut self) {
         if let Some(position) = self.move_history.last() {
             let to_y = get_int_from_char(position.1.chars().nth(2));
@@ -319,6 +488,9 @@ impl Board {
     }
 
     pub fn move_piece_on_the_board(&mut self, from: [usize; 2], to: [usize; 2]) {
+        if !is_valid([from[0] as i8, from[1] as i8]) || !is_valid([to[0] as i8, to[1] as i8]) {
+            return;
+        }
         let direction_y: i32 = if self.player_turn == PieceColor::White {
             -1
         } else {
@@ -354,26 +526,37 @@ impl Board {
             // we set the king 2 cells on where it came from
 
             let from_x: i32 = from[1] as i32;
-            let to_x: i32 = to[1] as i32;
+            let mut to_x: i32 = to[1] as i32;
+
             let distance = from_x - to_x;
             let direction_x = if distance > 0 { -1 } else { 1 };
+
             let mut row_index_rook = 0;
 
-            let row_index = from[1] as i32 + direction_x * 2;
+            let row_index = from_x + direction_x * 2;
+
             // We put move the king 2 cells
             self.board[to[0]][row_index as usize] = self.board[from[0]][from[1]];
 
             // We put the rook 3 cells from it's position if it's a big castling else 2 cells
+            // If it is playing against a bot we will receive 4 -> 6  and 4 -> 2 for to_x instead of 4 -> 7 and 4 -> 0
             // big castling
-            if distance == 4 {
-                row_index_rook = 3
-            } else if distance == -3 {
-                row_index_rook = 5
+            if distance > 0 {
+                row_index_rook = 3;
+                if self.is_game_against_bot && self.player_turn == PieceColor::Black {
+                    to_x = 0;
+                }
+            } else if distance < 0 {
+                row_index_rook = 5;
+                if self.is_game_against_bot && self.player_turn == PieceColor::Black {
+                    to_x = 7;
+                }
             }
-            self.board[to[0]][row_index_rook as usize] = self.board[to[0]][to[1]];
+
+            self.board[to[0]][row_index_rook as usize] = self.board[to[0]][to_x as usize];
 
             // We remove the latest rook
-            self.board[to[0]][to[1]] = None;
+            self.board[to[0]][to_x as usize] = None;
         } else {
             self.board[to[0]][to[1]] = self.board[from[0]][from[1]];
         }
@@ -438,10 +621,7 @@ impl Board {
         let distance = (from_x - to_x).abs();
 
         match (piece_type_from, piece_type_to) {
-            (Some(PieceType::King), _) => {
-                // Check if it's a move on more
-                distance > 1
-            }
+            (Some(PieceType::King), _) => distance > 1,
             _ => false,
         }
     }
@@ -625,7 +805,7 @@ impl Board {
             let number_move = &self.move_history[i].1;
 
             let utf_icon_white =
-                PieceType::piece_type_to_utf_enum(piece_type_from, Some(PieceColor::White));
+                PieceType::piece_to_utf_enum(piece_type_from, Some(PieceColor::White));
             let move_white = convert_position_into_notation(number_move.to_string());
 
             let mut utf_icon_black = "   ";
@@ -638,7 +818,7 @@ impl Board {
 
                 move_black = convert_position_into_notation(number.to_string());
                 utf_icon_black =
-                    PieceType::piece_type_to_utf_enum(piece_type_to, Some(PieceColor::Black))
+                    PieceType::piece_to_utf_enum(piece_type_to, Some(PieceColor::Black))
             }
 
             lines.push(Line::from(vec![
@@ -1453,4 +1633,148 @@ mod tests {
         board.move_piece_on_the_board([0, 2], [0, 1]);
         assert!(board.is_draw());
     }
+
+    #[test]
+    fn fen_converter_1() {
+        let custom_board = [
+            [
+                None,
+                None,
+                Some((PieceType::King, PieceColor::Black)),
+                None,
+                None,
+                None,
+                None,
+                Some((PieceType::Rook, PieceColor::White)),
+            ],
+            [None, None, None, None, None, None, None, None],
+            [
+                None,
+                None,
+                None,
+                None,
+                Some((PieceType::King, PieceColor::White)),
+                None,
+                None,
+                None,
+            ],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+        ];
+        // We setup the board
+        let board = Board::new(custom_board, PieceColor::White, vec![]);
+
+        // Move the king to replicate a third time the same position
+        assert_eq!(board.fen_position(), "2k4R/8/4K3/8/8/8/8/8 b - - 0 0");
+    }
+
+    #[test]
+    fn fen_converter_en_passant() {
+        let custom_board = [
+            [
+                None,
+                None,
+                Some((PieceType::King, PieceColor::Black)),
+                None,
+                None,
+                None,
+                None,
+                Some((PieceType::Rook, PieceColor::White)),
+            ],
+            [None, None, None, None, None, None, None, None],
+            [
+                None,
+                None,
+                None,
+                None,
+                Some((PieceType::King, PieceColor::White)),
+                None,
+                None,
+                None,
+            ],
+            [None, None, None, None, None, None, None, None],
+            [
+                None,
+                None,
+                Some((PieceType::Pawn, PieceColor::White)),
+                None,
+                None,
+                None,
+                None,
+                None,
+            ],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+        ];
+        // We setup the board
+        let board = Board::new(
+            custom_board,
+            PieceColor::White,
+            vec![(Some(PieceType::Pawn), "6242".to_string())],
+        );
+
+        // Move the king to replicate a third time the same position
+        assert_eq!(board.fen_position(), "2k4R/8/4K3/8/2P5/8/8/8 b - c3 0 0");
+    }
+    #[test]
+    fn fen_converter_castling() {
+        let custom_board = [
+            [
+                Some((PieceType::Rook, PieceColor::Black)),
+                Some((PieceType::Knight, PieceColor::Black)),
+                Some((PieceType::Bishop, PieceColor::Black)),
+                Some((PieceType::Queen, PieceColor::Black)),
+                Some((PieceType::King, PieceColor::Black)),
+                Some((PieceType::Bishop, PieceColor::Black)),
+                Some((PieceType::Knight, PieceColor::Black)),
+                Some((PieceType::Rook, PieceColor::Black)),
+            ],
+            [
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+                Some((PieceType::Pawn, PieceColor::Black)),
+            ],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [None, None, None, None, None, None, None, None],
+            [
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+                Some((PieceType::Pawn, PieceColor::White)),
+            ],
+            [
+                Some((PieceType::Rook, PieceColor::White)),
+                Some((PieceType::Knight, PieceColor::White)),
+                Some((PieceType::Bishop, PieceColor::White)),
+                Some((PieceType::Queen, PieceColor::White)),
+                Some((PieceType::King, PieceColor::White)),
+                Some((PieceType::Bishop, PieceColor::White)),
+                Some((PieceType::Knight, PieceColor::White)),
+                Some((PieceType::Rook, PieceColor::White)),
+            ],
+        ];
+        // We setup the board
+        let board = Board::new(custom_board, PieceColor::White, vec![]);
+
+        // Move the king to replicate a third time the same position
+        assert_eq!(
+            board.fen_position(),
+            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b kq - 0 0"
+        );
+    }
 }
diff --git a/src/constants.rs b/src/constants.rs
index 89f5641..1e07853 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -12,3 +12,12 @@ pub const TITLE: &str = r#"
 ╚██████╗██║  ██║███████╗███████║███████║      ██║   ╚██████╔╝██║
  ╚═════╝╚═╝  ╚═╝╚══════╝╚══════╝╚══════╝      ╚═╝    ╚═════╝ ╚═╝
 "#;
+
+#[derive(Debug, PartialEq)]
+pub enum Pages {
+    Home,
+    Solo,
+    Bot,
+    Help,
+    Credit,
+}
diff --git a/src/handler.rs b/src/handler.rs
index 2041a45..4adecd1 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -1,4 +1,7 @@
-use crate::app::{App, AppResult};
+use crate::{
+    app::{App, AppResult},
+    constants::Pages,
+};
 use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
 
 /// Handles the key events and updates the state of [`App`].
@@ -18,39 +21,41 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
         KeyCode::Right | KeyCode::Char('l') => app.board.cursor_right(),
         KeyCode::Left | KeyCode::Char('h') => app.board.cursor_left(),
         KeyCode::Up | KeyCode::Char('k') => {
-            if app.show_home_menu {
+            if app.current_page == Pages::Home {
                 app.menu_cursor_up()
             } else {
                 app.board.cursor_up()
             }
         }
         KeyCode::Down | KeyCode::Char('j') => {
-            if app.show_home_menu {
+            if app.current_page == Pages::Home {
                 app.menu_cursor_down()
             } else {
                 app.board.cursor_down()
             }
         }
         KeyCode::Char(' ') | KeyCode::Enter => {
-            if !app.show_home_menu {
+            if app.current_page != Pages::Home {
                 app.board.select_cell()
             } else {
                 app.menu_select()
             }
         }
         KeyCode::Char('?') => {
-            if !app.show_home_menu {
+            if app.current_page != Pages::Home {
                 app.show_help_popup = true
             }
         }
         KeyCode::Char('x') => {
-            app.show_credit_popup = false;
-            app.show_help_popup = false;
+            if app.current_page == Pages::Solo || app.current_page == Pages::Bot {
+                app.show_help_popup = false;
+            } else {
+                app.current_page = Pages::Home
+            }
         }
         KeyCode::Char('r') => app.restart(),
         KeyCode::Esc => {
             app.board.unselect_cell();
-            app.show_credit_popup = false;
             app.show_help_popup = false;
         }
         // Other handlers you could add here.
diff --git a/src/main.rs b/src/main.rs
index d8a8e38..2d07fe7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,14 +5,50 @@ use chess_tui::app::{App, AppResult};
 use chess_tui::event::{Event, EventHandler};
 use chess_tui::handler::handle_key_events;
 use chess_tui::tui::Tui;
+use clap::Parser;
 use ratatui::backend::CrosstermBackend;
 use ratatui::Terminal;
-use std::io;
+use std::fs::{self, File};
+use std::io::{self, Write};
+
+/// Simple program to greet a person
+#[derive(Parser, Debug)]
+#[command(author, version, about, long_about = None)]
+struct Args {
+    /// Path for the chess engine
+    #[arg(short, long, default_value = "")]
+    engine_path: String,
+}
 
 fn main() -> AppResult<()> {
+    // Parse the cli arguments
+    let args = Args::parse();
+
+    let config_path = dirs::home_dir().unwrap().join(".chess-tui");
+
+    if !args.engine_path.is_empty() {
+        if !config_path.exists() {
+            File::create(&config_path)?;
+        }
+
+        let mut file = File::create(&config_path)?;
+        file.write_all(args.engine_path.as_bytes())?;
+    }
+
     // Create an application.
     let mut app = App::new();
 
+    // We store the chess engine path if there is one
+    if let Ok(content) = fs::read_to_string(config_path) {
+        if content.trim().is_empty() {
+            app.chess_engine_path = None
+        } else {
+            app.chess_engine_path = Some(content)
+        }
+    } else {
+        println!("Error reading the file or the file does not exist");
+    }
+
     // Initialize the terminal user interface.
     let backend = CrosstermBackend::new(io::stderr());
     let terminal = Terminal::new(backend)?;
diff --git a/src/pieces/king.rs b/src/pieces/king.rs
index dc24ad1..5c7479f 100644
--- a/src/pieces/king.rs
+++ b/src/pieces/king.rs
@@ -114,6 +114,7 @@ impl King {
     "
     }
 
+    // Check if nothing is in between the king and a rook and if none of those cells are getting checked
     pub fn check_castling_condition(
         board: [[Option<(PieceType, PieceColor)>; 8]; 8],
         color: PieceColor,
diff --git a/src/pieces/mod.rs b/src/pieces/mod.rs
index 31869eb..23eef6a 100644
--- a/src/pieces/mod.rs
+++ b/src/pieces/mod.rs
@@ -88,7 +88,7 @@ impl PieceType {
         }
     }
 
-    pub fn piece_type_to_utf_enum(
+    pub fn piece_to_utf_enum(
         piece_type: Option<PieceType>,
         piece_color: Option<PieceColor>,
     ) -> &'static str {
@@ -109,6 +109,28 @@ impl PieceType {
         }
     }
 
+    pub fn piece_to_fen_enum(
+        piece_type: Option<PieceType>,
+        piece_color: Option<PieceColor>,
+    ) -> &'static str {
+        match (piece_type, piece_color) {
+            (Some(PieceType::Queen), Some(PieceColor::Black)) => "q",
+            (Some(PieceType::Queen), Some(PieceColor::White)) => "Q",
+            (Some(PieceType::King), Some(PieceColor::Black)) => "k",
+            (Some(PieceType::King), Some(PieceColor::White)) => "K",
+            (Some(PieceType::Rook), Some(PieceColor::Black)) => "r",
+            (Some(PieceType::Rook), Some(PieceColor::White)) => "R",
+            (Some(PieceType::Bishop), Some(PieceColor::Black)) => "b",
+            (Some(PieceType::Bishop), Some(PieceColor::White)) => "B",
+            (Some(PieceType::Knight), Some(PieceColor::Black)) => "n",
+            (Some(PieceType::Knight), Some(PieceColor::White)) => "N",
+            (Some(PieceType::Pawn), Some(PieceColor::Black)) => "p",
+            (Some(PieceType::Pawn), Some(PieceColor::White)) => "P",
+            (None, None) => "",
+            _ => unreachable!("Undefined piece and piece color tuple"),
+        }
+    }
+
     pub fn piece_type_to_string_enum(piece_type: Option<PieceType>) -> &'static str {
         match piece_type {
             Some(PieceType::Queen) => Queen::to_string(),
diff --git a/src/popups.rs b/src/popups.rs
index 9fca2a1..5b53665 100644
--- a/src/popups.rs
+++ b/src/popups.rs
@@ -12,7 +12,36 @@ use ratatui::{
     Frame,
 };
 
-// This renders the popup for a promotion
+// This renders a popup when the selected game mode is bot and there is no chess engine path
+pub fn render_engine_path_error_popup(frame: &mut Frame) {
+    let block = Block::default()
+        .title("Error")
+        .borders(Borders::ALL)
+        .border_type(BorderType::Rounded)
+        .padding(Padding::horizontal(1))
+        .border_style(Style::default().fg(WHITE));
+    let area = centered_rect(40, 40, frame.size());
+
+    let text = vec![
+        Line::from("You didn't specify the chess engine path").alignment(Alignment::Center),
+        Line::from(""),
+        Line::from("To do so use the -e argument when running chess-tui to store an engine path"),
+        Line::from(""),
+        Line::from("Example: "),
+        Line::from("chess-tui -e /opt/homebrew/opt/stockfish"),
+    ];
+
+    let paragraph = Paragraph::new(text)
+        .block(block.clone())
+        .alignment(Alignment::Left)
+        .wrap(Wrap { trim: true });
+
+    frame.render_widget(Clear, area); //this clears out the background
+    frame.render_widget(block, area);
+    frame.render_widget(paragraph, area);
+}
+
+// This renders a popup for a promotion
 pub fn render_end_popup(frame: &mut Frame, sentence: String) {
     let block = Block::default()
         .title("Game ended")
diff --git a/src/ui.rs b/src/ui.rs
index e0e5b24..f33d66c 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -9,9 +9,12 @@ use ratatui::{
 
 use crate::{
     app::App,
-    constants::TITLE,
+    constants::{Pages, TITLE},
     pieces::PieceColor,
-    popups::{render_credit_popup, render_end_popup, render_help_popup, render_promotion_popup},
+    popups::{
+        render_credit_popup, render_end_popup, render_engine_path_error_popup, render_help_popup,
+        render_promotion_popup,
+    },
     utils::get_opposite_color,
 };
 
@@ -19,17 +22,29 @@ use crate::{
 pub fn render(app: &mut App, frame: &mut Frame) {
     let main_area = frame.size();
 
-    if app.show_home_menu {
-        render_menu_ui(frame, app, main_area)
-    } else {
+    if app.current_page == Pages::Solo {
         render_game_ui(frame, app, main_area)
+    } else if app.current_page == Pages::Bot {
+        if app.board.engine.is_none() {
+            match &app.chess_engine_path {
+                Some(path) => {
+                    app.board.set_engine(path);
+                    render_game_ui(frame, app, main_area)
+                }
+                None => render_engine_path_error_popup(frame),
+            }
+        } else {
+            render_game_ui(frame, app, main_area)
+        }
+    } else {
+        render_menu_ui(frame, app, main_area)
     }
 
-    if app.show_help_popup {
+    if app.current_page == Pages::Help || app.show_help_popup {
         render_help_popup(frame)
     }
 
-    if app.show_credit_popup {
+    if app.current_page == Pages::Credit {
         render_credit_popup(frame)
     }
 }
@@ -83,7 +98,7 @@ pub fn render_menu_ui(frame: &mut Frame, app: &App, main_area: Rect) {
     frame.render_widget(sub_title, main_layout_horizontal[1]);
 
     // Board block representing the full board div
-    let menu_items = ["Normal game", "Help", "Credits"];
+    let menu_items = ["Normal game", "Play against a bot", "Help", "Credits"];
     let mut menu_body: Vec<Line<'_>> = vec![];
 
     for (i, menu_item) in menu_items.iter().enumerate() {
diff --git a/src/utils.rs b/src/utils.rs
index 9cb4565..f6b65bd 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -107,6 +107,20 @@ pub fn col_to_letter(col: i8) -> String {
     }
 }
 
+pub fn letter_to_col(col: Option<char>) -> i8 {
+    match col {
+        Some('a') => 0,
+        Some('b') => 1,
+        Some('c') => 2,
+        Some('d') => 3,
+        Some('e') => 4,
+        Some('f') => 5,
+        Some('g') => 6,
+        Some('h') => 7,
+        _ => unreachable!("Col out of bound"),
+    }
+}
+
 pub fn convert_position_into_notation(position: String) -> String {
     let mut result: String = "".to_string();
     let from_y = get_int_from_char(position.chars().next());
@@ -115,14 +129,24 @@ pub fn convert_position_into_notation(position: String) -> String {
     let to_x = get_int_from_char(position.chars().nth(3));
 
     result += &col_to_letter(from_x);
-    result += &format!("{}", (8 - from_y) % 9).to_string();
+    result += &format!("{}", 8 - from_y).to_string();
     result += "-";
     result += &col_to_letter(to_x);
-    result += &format!("{}", (8 - to_y) % 9).to_string();
+    result += &format!("{}", 8 - to_y).to_string();
 
     result
 }
 
+pub fn convert_notation_into_position(notation: String) -> String {
+    let from_x = &letter_to_col(notation.chars().next());
+    let from_y = (&get_int_from_char(notation.chars().nth(1)) - 8).abs();
+
+    let to_x = &letter_to_col(notation.chars().nth(2));
+    let to_y = (&get_int_from_char(notation.chars().nth(3)) - 8).abs();
+
+    format!("{}{}{}{}", from_y, from_x, to_y, to_x)
+}
+
 pub fn get_player_turn_in_modulo(color: PieceColor) -> usize {
     match color {
         PieceColor::White => 0,
@@ -250,7 +274,7 @@ pub fn color_to_ratatui_enum(piece_color: Option<PieceColor>) -> Color {
 
 #[cfg(test)]
 mod tests {
-    use crate::utils::convert_position_into_notation;
+    use crate::utils::{convert_notation_into_position, convert_position_into_notation};
 
     #[test]
     fn convert_position_into_notation_1() {
@@ -261,4 +285,17 @@ mod tests {
     fn convert_position_into_notation_2() {
         assert_eq!(convert_position_into_notation("0257".to_string()), "c8-h3")
     }
+
+    #[test]
+    fn convert_notation_into_position_1() {
+        assert_eq!(convert_notation_into_position("c8b7".to_string()), "0211")
+    }
+    #[test]
+    fn convert_notation_into_position_2() {
+        assert_eq!(convert_notation_into_position("g7h8".to_string()), "1607")
+    }
+    #[test]
+    fn convert_notation_into_position_3() {
+        assert_eq!(convert_notation_into_position("g1f3".to_string()), "7655")
+    }
 }


Test patch:
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 2272607..3137642 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -14,12 +14,14 @@ jobs:
     strategy:
       matrix:
         toolchain:
-        - stable
-        - beta
-        - nightly
+          - stable
+          - beta
+          - nightly
     steps:
-    - uses: actions/checkout@v4
-    - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
-    - run: cargo build --verbose
-    - run: cargo test --verbose
-
+      - uses: actions/checkout@v4
+      - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
+        continue-on-error: true
+      - run: cargo build --verbose
+        continue-on-error: true
+      - run: cargo test --verbose
+        continue-on-error: true