Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
ccd7ab749d |
202
Cargo.lock
generated
202
Cargo.lock
generated
@ -4,172 +4,232 @@
|
|||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "btoi"
|
name = "btoi"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4ced8205e70d9e553d008d53ded735808fa6133597318d48f74fc2bf9861471"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clichess"
|
name = "clichess"
|
||||||
version = "0.3.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json",
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"shakmaty",
|
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"termion",
|
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"shakmaty 0.16.2 (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 = "ctrlc"
|
||||||
|
version = "3.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.66"
|
version = "0.2.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "numtoa"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.1.57"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_termios"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
|
||||||
dependencies = [
|
|
||||||
"redox_syscall",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.104"
|
version = "1.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.104"
|
version = "1.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn",
|
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.44"
|
version = "1.0.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ryu",
|
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde",
|
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shakmaty"
|
name = "shakmaty"
|
||||||
version = "0.16.2"
|
version = "0.16.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f8f7fd48325e0b530cd257df0aa735efeefc923c70133d412f05d69de69108"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags",
|
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"btoi",
|
"btoi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote",
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-xid",
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termion"
|
|
||||||
version = "1.5.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"numtoa",
|
|
||||||
"redox_syscall",
|
|
||||||
"redox_termios",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "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 cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
||||||
|
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
"checksum colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "433e7ac7d511768127ed85b0c4947f47a254131e37864b2dc13f52aa32cd37e5"
|
||||||
|
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
|
||||||
|
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||||
|
"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 nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
|
||||||
|
"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
|
||||||
|
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||||
|
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||||
|
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||||
|
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||||
|
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||||
|
"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
|
||||||
|
"checksum shakmaty 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f8f7fd48325e0b530cd257df0aa735efeefc923c70133d412f05d69de69108"
|
||||||
|
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
||||||
|
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||||
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
"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"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "clichess"
|
name = "clichess"
|
||||||
version = "0.4.0"
|
version = "0.1.0"
|
||||||
authors = ["Artlef <artlef@localhost>"]
|
authors = ["Artlef <artlef@localhost>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
shakmaty = "0.16.2"
|
shakmaty = "0.16.2"
|
||||||
|
colored = "1.9"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
termion = "1.5.5"
|
ctrlc = "3.1.3"
|
||||||
|
@ -1,33 +1,39 @@
|
|||||||
extern crate termion;
|
extern crate ctrlc;
|
||||||
|
|
||||||
use clichess::{ClientRequest, GameInfo, RecvPositionError, UserRole, EXIT_MSG};
|
use clichess::Player;
|
||||||
use shakmaty::{fen, Chess, Color, Outcome, Position, Setup};
|
use clichess::{PlayingStatus, RecvPositionError, UserRole, EXIT_MSG};
|
||||||
|
use serde_json::json;
|
||||||
|
use shakmaty::fen::Fen;
|
||||||
|
use shakmaty::{Chess, Color, Outcome, Position, Setup};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::iter::Iterator;
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::mpsc::{channel, Receiver, TryRecvError};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
use termion::event::{parse_event, Event, Key};
|
|
||||||
|
|
||||||
use std::io::{stdin, stdout, Read, Write};
|
|
||||||
use termion::raw::{IntoRawMode, RawTerminal};
|
|
||||||
|
|
||||||
struct Client {
|
struct Client {
|
||||||
player: clichess::Player,
|
player: Player,
|
||||||
side: Color,
|
side: Color,
|
||||||
display_sender: Sender<DisplayMessage>,
|
running: Arc<AtomicBool>,
|
||||||
waiting_server_msg_receiver: Receiver<WaitingServerMsg>,
|
input_buffer: Arc<Mutex<String>>,
|
||||||
keyboard_input_recv: Receiver<String>,
|
|
||||||
server_message_recv: Receiver<String>,
|
server_message_recv: Receiver<String>,
|
||||||
opponent_name: Option<String>,
|
playing: bool,
|
||||||
last_move: Option<String>,
|
}
|
||||||
is_player_turn: Arc<Mutex<bool>>,
|
|
||||||
|
enum LobbyLoopOutcome {
|
||||||
|
ClientLeft,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PlayingLoopOutcome {
|
||||||
|
PlayerLeft,
|
||||||
|
Outcome(Outcome),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let running = setupctrlc();
|
||||||
println!("Running clichess version {}", version);
|
|
||||||
let username = std::env::args().nth(1).expect("no name given");
|
let username = std::env::args().nth(1).expect("no name given");
|
||||||
let public_key = std::env::args().nth(2).expect("no public key given");
|
let public_key = std::env::args().nth(2).expect("no public key given");
|
||||||
//send username and public key to server
|
//send username and public key to server
|
||||||
@ -38,462 +44,185 @@ fn main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let username_pubkey_json = json!({
|
||||||
let is_player_turn = Arc::new(Mutex::new(true));
|
"username": username,
|
||||||
let (display_sender, display_receiver) = channel();
|
"pubkey": public_key,
|
||||||
let (waiting_server_msg_sender, waiting_server_msg_receiver) = channel();
|
});
|
||||||
let keyboard_input_recv = start_keyboard_input_thread(
|
clichess::write_to_stream(&mut stream, username_pubkey_json.to_string()).unwrap();
|
||||||
display_sender.clone(),
|
let input_buffer = setup_input_buffer();
|
||||||
waiting_server_msg_sender.clone(),
|
|
||||||
is_player_turn.clone(),
|
|
||||||
);
|
|
||||||
start_display_thread(display_receiver);
|
|
||||||
//start a thread to listen to server messages
|
//start a thread to listen to server messages
|
||||||
let server_message_recv =
|
let server_message_recv = setup_server_message_recv(&stream).unwrap();
|
||||||
setup_server_message_recv(&stream, waiting_server_msg_sender.clone()).unwrap();
|
|
||||||
|
|
||||||
let mut client = Client {
|
let mut client = Client {
|
||||||
player: clichess::Player {
|
player: Player {
|
||||||
|
id: 0,
|
||||||
|
username: username,
|
||||||
|
public_key: public_key,
|
||||||
|
playing_status: PlayingStatus::WaitingInLobby,
|
||||||
role: UserRole::Spectator,
|
role: UserRole::Spectator,
|
||||||
username,
|
|
||||||
public_key,
|
|
||||||
},
|
},
|
||||||
side: Color::White,
|
side: Color::White,
|
||||||
display_sender: display_sender.clone(),
|
running: running.clone(),
|
||||||
waiting_server_msg_receiver,
|
input_buffer: input_buffer.clone(),
|
||||||
keyboard_input_recv,
|
|
||||||
server_message_recv,
|
server_message_recv,
|
||||||
opponent_name: Option::None,
|
playing: false,
|
||||||
last_move: Option::None,
|
|
||||||
is_player_turn,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let login_result = login(&mut client, &mut stream);
|
//get id from server
|
||||||
if login_result == LoginResult::UserExited {
|
let player_id = fetch_message_from_server(&client).parse::<usize>().unwrap();
|
||||||
send_request(&client, &mut stream, ClientRequest::Exit);
|
println!("got id {} from server", player_id);
|
||||||
client.display_sender.send(DisplayMessage::Exit).unwrap();
|
client.player.id = player_id;
|
||||||
return;
|
|
||||||
}
|
|
||||||
client.display_sender.send(DisplayMessage::Clear).unwrap();
|
|
||||||
if client.player.role == UserRole::Black {
|
|
||||||
client.side = Color::Black;
|
|
||||||
}
|
|
||||||
if client.player.role == UserRole::Spectator {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Information(format!(
|
|
||||||
"Hello, {} !\n\r You're spectating !",
|
|
||||||
client.player.username
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Information(format!(
|
|
||||||
"Hello, {} !\n\r You're playing with the {} pieces",
|
|
||||||
client.player.username,
|
|
||||||
client.player.role.to_string()
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
client.display_sender.send(DisplayMessage::Help).unwrap();
|
lobby_loop(&client, &mut stream);
|
||||||
if !game_loop(&mut client, &mut stream) {
|
match playing_loop(&client, &mut stream) {
|
||||||
|
PlayingLoopOutcome::PlayerLeft => {
|
||||||
|
println!("Bye");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
client.display_sender.send(DisplayMessage::Clear).unwrap();
|
PlayingLoopOutcome::Outcome(Outcome::Draw) => println!("Draw game."),
|
||||||
|
PlayingLoopOutcome::Outcome(Outcome::Decisive { winner }) => {
|
||||||
|
if winner == Color::White {
|
||||||
|
println!("White has won the game.")
|
||||||
|
} else {
|
||||||
|
println!("Black has won the game.")
|
||||||
}
|
}
|
||||||
client.display_sender.send(DisplayMessage::Clear).unwrap();
|
|
||||||
send_request(&client, &mut stream, ClientRequest::Exit);
|
|
||||||
client.display_sender.send(DisplayMessage::Exit).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum LoginResult {
|
|
||||||
Success,
|
|
||||||
UserExited,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn login(client: &mut Client, stream: &mut UnixStream) -> LoginResult {
|
|
||||||
let mut response = String::from("KO");
|
|
||||||
while &response != "OK" {
|
|
||||||
match prompt_user_for_role(client, stream) {
|
|
||||||
PromptedRoleResponse::Role(role) => {
|
|
||||||
client.player.role = role.clone();
|
|
||||||
response = send_request(
|
|
||||||
client,
|
|
||||||
stream,
|
|
||||||
ClientRequest::Login {
|
|
||||||
username: client.player.username.clone(),
|
|
||||||
pubkey: client.player.public_key.clone(),
|
|
||||||
role,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
PromptedRoleResponse::Exit => return LoginResult::UserExited,
|
|
||||||
PromptedRoleResponse::Retry => continue,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
LoginResult::Success
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn game_loop(client: &mut Client, stream: &mut UnixStream) -> bool {
|
fn lobby_loop(client: &Client, stream: &mut UnixStream) {
|
||||||
let mut replay = false;
|
println!("Sending first refresh to server...");
|
||||||
let mut current_position = fetch_initial_chess_position(client, stream);
|
clichess::write_to_stream(stream, String::from("refresh")).unwrap();
|
||||||
|
println!("Getting list of players from server...");
|
||||||
|
let players = fetch_players_from_server(client);
|
||||||
|
print_players(client, &players);
|
||||||
|
while client.running.load(Ordering::SeqCst) {
|
||||||
|
let mut user_input = client.input_buffer.lock().unwrap();
|
||||||
|
if user_input.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut sent_to_server = String::from(user_input.trim());
|
||||||
|
if sent_to_server == "refresh" || sent_to_server == "r" {
|
||||||
|
sent_to_server = String::from("refresh");
|
||||||
|
}
|
||||||
|
println!("Sending {} to server...", sent_to_server);
|
||||||
|
clichess::write_to_stream(stream, sent_to_server.clone()).unwrap();
|
||||||
|
user_input.clear();
|
||||||
|
if sent_to_server == "refresh" {
|
||||||
|
let players = fetch_players_from_server(client);
|
||||||
|
print_players(client, &players);
|
||||||
|
} else {
|
||||||
|
let response = fetch_message_from_server(client);
|
||||||
|
println!("response from server: {}", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
fn fetch_players_from_server(client: &Client) -> Vec<Player> {
|
||||||
let is_player_turn_loop = clichess::is_player_turn(&client.player, current_position.turn());
|
let data = fetch_message_from_server(client);
|
||||||
{
|
let v: Vec<Player> = serde_json::from_str(&data).unwrap();
|
||||||
let mut is_player_turn = client.is_player_turn.lock().unwrap();
|
v
|
||||||
*is_player_turn = is_player_turn_loop;
|
|
||||||
}
|
}
|
||||||
if is_player_turn_loop || client.player.role == UserRole::Spectator {
|
|
||||||
let mut message = String::from("");
|
fn print_players(client: &Client, players: &Vec<Player>) {
|
||||||
let opponent_name = client.opponent_name.clone();
|
let other_players: Vec<&Player> = players.iter().filter(|p| p.id != client.player.id).collect();
|
||||||
if opponent_name.is_some() {
|
if other_players.is_empty() {
|
||||||
message.push_str(&format!("{} played", opponent_name.expect("is some")));
|
println!("No one is connected.");
|
||||||
client
|
|
||||||
.last_move
|
|
||||||
.clone()
|
|
||||||
.map(|chessmove| message.push_str(&format!(" {}", chessmove)));
|
|
||||||
}
|
}
|
||||||
if message.len() > 0 {
|
for player in other_players {
|
||||||
client
|
let mut message_to_add: String;
|
||||||
.display_sender
|
match player.playing_status {
|
||||||
.send(DisplayMessage::Information(message))
|
PlayingStatus::WaitingInLobby => message_to_add = String::default(),
|
||||||
.unwrap();
|
PlayingStatus::Challenging(id) => {
|
||||||
}
|
message_to_add = String::from(if id == client.player.id {
|
||||||
}
|
" is challenging you"
|
||||||
client
|
} else {
|
||||||
.display_sender
|
""
|
||||||
.send(DisplayMessage::Chessboard {
|
|
||||||
fen: fen::fen(¤t_position),
|
|
||||||
side: client.side.clone(),
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
}
|
||||||
|
PlayingStatus::PlayingAgainst(id) => {
|
||||||
|
message_to_add = " is playing against ".to_owned();
|
||||||
|
message_to_add.push_str(
|
||||||
|
&players
|
||||||
|
.iter()
|
||||||
|
.filter(|p| p.id == id)
|
||||||
|
.last()
|
||||||
|
.expect("id is from the list")
|
||||||
|
.username,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("{}{}", player.username, message_to_add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn playing_loop(client: &Client, stream: &mut UnixStream) -> PlayingLoopOutcome {
|
||||||
|
let mut current_position = fetch_initial_chess_position(&client);
|
||||||
|
loop {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
clichess::board_representation(¤t_position, client.side)
|
||||||
|
);
|
||||||
//check if game is over.
|
//check if game is over.
|
||||||
if current_position.is_game_over() {
|
if current_position.is_game_over() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if is_player_turn_loop {
|
if clichess::is_player_turn(&client.player, current_position.turn()) {
|
||||||
//it's the user turn, taking user input
|
//it's the user turn, taking user input
|
||||||
let mut input = client.keyboard_input_recv.recv().unwrap();
|
let input = read_user_input(client);
|
||||||
input = String::from(input.trim());
|
clichess::write_to_stream(stream, String::from(input.trim())).unwrap();
|
||||||
if input == EXIT_MSG {
|
if input.trim() == EXIT_MSG {
|
||||||
client.waiting_server_msg_receiver.recv().unwrap();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let response = send_request(client, stream, ClientRequest::Play(input.clone()));
|
|
||||||
if response == String::from("KO") {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(format!("Invalid move: {}", input)))
|
|
||||||
.unwrap();
|
|
||||||
//go back to taking user input
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
client.last_move = Some(input);
|
//update position after playing.
|
||||||
match get_current_position(client, stream) {
|
match get_current_position(client) {
|
||||||
Ok(position) => current_position = position,
|
Ok(position) => current_position = position,
|
||||||
Err(_) => break,
|
Err(_) => {
|
||||||
};
|
clichess::write_to_stream(stream, String::from(EXIT_MSG)).unwrap();
|
||||||
//clear message
|
break;
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(String::default()))
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
match wait_for_next_move(client, stream) {
|
|
||||||
Ok(position) => current_position = position,
|
|
||||||
Err(_) => break,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let end_message = match current_position.outcome() {
|
|
||||||
None => "",
|
|
||||||
Some(Outcome::Draw) => "Draw game.",
|
|
||||||
Some(Outcome::Decisive { winner }) => {
|
|
||||||
if winner == Color::White {
|
|
||||||
"White has won the game."
|
|
||||||
} else {
|
|
||||||
"Black has won the game."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.to_string();
|
|
||||||
if !end_message.is_empty() {
|
|
||||||
//accept input
|
|
||||||
if client.player.role != UserRole::Spectator {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(format!(
|
|
||||||
"{}\n\rPress enter to rematch.",
|
|
||||||
end_message
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
let mut is_player_turn = client.is_player_turn.lock().unwrap();
|
|
||||||
*is_player_turn = true;
|
|
||||||
} else {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(format!("{}", end_message)))
|
|
||||||
.unwrap();
|
|
||||||
thread::sleep(time::Duration::from_secs(5));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let buffer = client.keyboard_input_recv.recv().unwrap();
|
|
||||||
|
|
||||||
if buffer == EXIT_MSG {
|
|
||||||
client.waiting_server_msg_receiver.recv().unwrap();
|
|
||||||
} else {
|
|
||||||
replay = true;
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(format!(
|
|
||||||
"{}\n\rWaiting for your opponent to restart...",
|
|
||||||
end_message
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
send_request(client, stream, ClientRequest::Reset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
replay
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_request(client: &Client, stream: &mut UnixStream, request: ClientRequest) -> String {
|
|
||||||
let response: String;
|
|
||||||
match serde_json::to_string(&request) {
|
|
||||||
Ok(request_str) => {
|
|
||||||
clichess::write_to_stream(stream, request_str).unwrap();
|
|
||||||
response = fetch_message_from_server(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(format!(
|
|
||||||
"Error when parsing client request: {}",
|
|
||||||
e
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
response = String::from("KO");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
response
|
}
|
||||||
|
match current_position.outcome() {
|
||||||
|
None => PlayingLoopOutcome::PlayerLeft,
|
||||||
|
Some(outcome) => PlayingLoopOutcome::Outcome(outcome),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
fn setup_input_buffer() -> Arc<Mutex<String>> {
|
||||||
pub enum DisplayMessage {
|
let buf = Arc::new(Mutex::new(String::new()));
|
||||||
Chessboard { fen: String, side: Color },
|
let buf2 = buf.clone();
|
||||||
Message(String),
|
|
||||||
Information(String),
|
|
||||||
Debug(String),
|
|
||||||
Help,
|
|
||||||
Input(Key),
|
|
||||||
RemoveLastInput,
|
|
||||||
SwitchAsciiMode,
|
|
||||||
Enter,
|
|
||||||
Clear,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_display_thread(request_recv: Receiver<DisplayMessage>) {
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let stdout = stdout();
|
|
||||||
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
|
||||||
write!(
|
|
||||||
stdout,
|
|
||||||
"{}{}",
|
|
||||||
termion::clear::All,
|
|
||||||
termion::cursor::Goto(3, 17)
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stdout.flush().unwrap();
|
|
||||||
let mut ascii_mode = false;
|
|
||||||
let mut last_fen_position = String::default();
|
|
||||||
let mut last_side = Color::White;
|
|
||||||
loop {
|
loop {
|
||||||
let msg = request_recv.recv().unwrap();
|
|
||||||
match msg {
|
|
||||||
DisplayMessage::Chessboard { fen, side } => {
|
|
||||||
let current_position = clichess::parse_position(&fen);
|
|
||||||
last_fen_position = fen.clone();
|
|
||||||
last_side = side.clone();
|
|
||||||
clichess::print_board_representation(
|
|
||||||
¤t_position,
|
|
||||||
side,
|
|
||||||
&mut stdout,
|
|
||||||
ascii_mode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
DisplayMessage::SwitchAsciiMode => {
|
|
||||||
ascii_mode = !ascii_mode;
|
|
||||||
write!(stdout, "{}", termion::cursor::Save,).unwrap();
|
|
||||||
|
|
||||||
clichess::print_board_representation(
|
|
||||||
&clichess::parse_position(&last_fen_position),
|
|
||||||
last_side,
|
|
||||||
&mut stdout,
|
|
||||||
ascii_mode,
|
|
||||||
);
|
|
||||||
write!(stdout, "{}", termion::cursor::Restore).unwrap();
|
|
||||||
}
|
|
||||||
DisplayMessage::Message(s) => write!(
|
|
||||||
stdout,
|
|
||||||
"{}{}{}{}{}",
|
|
||||||
termion::cursor::Save,
|
|
||||||
termion::cursor::Goto(3, 18),
|
|
||||||
termion::clear::CurrentLine,
|
|
||||||
s,
|
|
||||||
termion::cursor::Restore
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
DisplayMessage::Information(s) => write!(
|
|
||||||
stdout,
|
|
||||||
"{}{}{}{}{}",
|
|
||||||
termion::cursor::Save,
|
|
||||||
termion::cursor::Goto(3, 3),
|
|
||||||
termion::clear::CurrentLine,
|
|
||||||
s,
|
|
||||||
termion::cursor::Restore
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
DisplayMessage::Clear => write!(stdout, "{}", termion::clear::All).unwrap(),
|
|
||||||
DisplayMessage::Enter => write!(
|
|
||||||
stdout,
|
|
||||||
"{}{}",
|
|
||||||
termion::clear::CurrentLine,
|
|
||||||
termion::cursor::Goto(3, 17)
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
DisplayMessage::Input(k) => display_key(&mut stdout, k),
|
|
||||||
DisplayMessage::RemoveLastInput => write!(
|
|
||||||
stdout,
|
|
||||||
"{}{}",
|
|
||||||
termion::cursor::Left(1),
|
|
||||||
termion::clear::UntilNewline
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
DisplayMessage::Help => print_help(&mut stdout),
|
|
||||||
DisplayMessage::Debug(s) => write!(
|
|
||||||
stdout,
|
|
||||||
"{}{}{}{}{}",
|
|
||||||
termion::cursor::Save,
|
|
||||||
termion::cursor::Goto(3, 30),
|
|
||||||
termion::clear::CurrentLine,
|
|
||||||
s,
|
|
||||||
termion::cursor::Restore
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
DisplayMessage::Exit => break,
|
|
||||||
}
|
|
||||||
stdout.flush().unwrap();
|
|
||||||
}
|
|
||||||
write!(stdout, "{}", termion::clear::All,).unwrap();
|
|
||||||
stdout.flush().unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_help(stdout: &mut RawTerminal<io::StdoutLock>) {
|
|
||||||
write!(stdout, "{}", termion::cursor::Save).unwrap();
|
|
||||||
let help = [
|
|
||||||
"move are parsed using SAN (Nc3) or UCI (b1c3)",
|
|
||||||
"@ to toggle ascii mode",
|
|
||||||
"Ctrl-C to quit",
|
|
||||||
];
|
|
||||||
for (i, h) in help.iter().enumerate() {
|
|
||||||
write!(stdout, "{}{}", termion::cursor::Goto(3, 22 + (i as u16)), h).unwrap();
|
|
||||||
}
|
|
||||||
write!(stdout, "{}", termion::cursor::Restore).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_keyboard_input_thread(
|
|
||||||
display_sender: Sender<DisplayMessage>,
|
|
||||||
waiting_server_msg_sender: Sender<WaitingServerMsg>,
|
|
||||||
is_player_turn: Arc<Mutex<bool>>,
|
|
||||||
) -> Receiver<String> {
|
|
||||||
let (sender, receiver) = channel();
|
|
||||||
thread::spawn(move || {
|
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
let stdin = stdin();
|
//wait for user input
|
||||||
let stdin = stdin.lock();
|
io::stdin().read_line(&mut buffer).unwrap();
|
||||||
let mut bytes = stdin.bytes();
|
|
||||||
loop {
|
|
||||||
let b = bytes.next().unwrap().unwrap();
|
|
||||||
let e = parse_event(b, &mut bytes).unwrap();
|
|
||||||
let is_player_turn = is_player_turn.lock().unwrap();
|
|
||||||
if !*is_player_turn
|
|
||||||
&& e != Event::Key(Key::Ctrl('c'))
|
|
||||||
&& e != Event::Key(Key::Char('@'))
|
|
||||||
{
|
{
|
||||||
//ignore input when it's not the client turn
|
let mut user_input = buf2.lock().unwrap();
|
||||||
continue;
|
if user_input.is_empty() {
|
||||||
}
|
*user_input = buffer;
|
||||||
match e {
|
|
||||||
Event::Key(Key::Ctrl('c')) => {
|
|
||||||
sender.send(String::from(EXIT_MSG)).unwrap();
|
|
||||||
waiting_server_msg_sender
|
|
||||||
.send(WaitingServerMsg::UserCanceled)
|
|
||||||
.unwrap();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Event::Key(Key::Char('\n')) => {
|
|
||||||
display_sender.send(DisplayMessage::Enter).unwrap();
|
|
||||||
sender.send(buffer.clone()).unwrap();
|
|
||||||
if buffer == EXIT_MSG {
|
|
||||||
waiting_server_msg_sender
|
|
||||||
.send(WaitingServerMsg::UserCanceled)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
Event::Key(Key::Char('@')) => {
|
|
||||||
display_sender
|
|
||||||
.send(DisplayMessage::SwitchAsciiMode)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
Event::Key(Key::Char(c)) => {
|
|
||||||
if buffer.len() < 10 {
|
|
||||||
buffer.push_str(&c.to_string());
|
|
||||||
display_sender
|
|
||||||
.send(DisplayMessage::Input(Key::Char(c)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Key(Key::Backspace) => {
|
|
||||||
if buffer.len() > 0 {
|
|
||||||
buffer.pop();
|
|
||||||
display_sender
|
|
||||||
.send(DisplayMessage::RemoveLastInput)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Key(_) => continue,
|
|
||||||
Event::Mouse(_) => continue,
|
|
||||||
Event::Unsupported(_) => continue,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
receiver
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_key(stdout: &mut RawTerminal<io::StdoutLock>, key: Key) {
|
fn setupctrlc() -> Arc<AtomicBool> {
|
||||||
match key {
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
Key::Char(c) => write!(stdout, "{}", c).unwrap(),
|
let r = running.clone();
|
||||||
_ => {}
|
ctrlc::set_handler(move || {
|
||||||
};
|
r.store(false, Ordering::SeqCst);
|
||||||
|
})
|
||||||
|
.expect("Error setting Ctrl-C handler");
|
||||||
|
running
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WaitingServerMsg {
|
fn setup_server_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>> {
|
||||||
MsgReceived,
|
|
||||||
UserCanceled,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_server_message_recv(
|
|
||||||
stream: &UnixStream,
|
|
||||||
waiting_server_msg_sender: Sender<WaitingServerMsg>,
|
|
||||||
) -> io::Result<Receiver<String>> {
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
let thread_stream = stream.try_clone()?;
|
let thread_stream = stream.try_clone()?;
|
||||||
|
|
||||||
@ -503,131 +232,72 @@ fn setup_server_message_recv(
|
|||||||
let buffer =
|
let buffer =
|
||||||
clichess::read_line_from_stream(&thread_stream).expect("Error message from server");
|
clichess::read_line_from_stream(&thread_stream).expect("Error message from server");
|
||||||
sender.send(buffer).unwrap();
|
sender.send(buffer).unwrap();
|
||||||
waiting_server_msg_sender
|
|
||||||
.send(WaitingServerMsg::MsgReceived)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(receiver)
|
Ok(receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
//get current position from server
|
fn read_user_input(client: &Client) -> String {
|
||||||
fn get_current_position(
|
//clear input before waiting for a new one
|
||||||
client: &mut Client,
|
{
|
||||||
stream: &mut UnixStream,
|
let mut user_input = client.input_buffer.lock().unwrap();
|
||||||
) -> Result<Chess, RecvPositionError> {
|
user_input.clear();
|
||||||
let response = send_request(client, stream, ClientRequest::GetGameInfo);
|
}
|
||||||
|
let mut input = String::new();
|
||||||
|
while client.running.load(Ordering::SeqCst) {
|
||||||
|
thread::sleep(time::Duration::from_millis(10));
|
||||||
|
{
|
||||||
|
let user_input = client.input_buffer.lock().unwrap();
|
||||||
|
if user_input.is_empty() {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
input = user_input.clone();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if client.running.load(Ordering::SeqCst) {
|
||||||
|
input
|
||||||
|
} else {
|
||||||
|
String::from(EXIT_MSG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//wait for next position from server, then return the current board.
|
||||||
|
fn get_current_position(client: &Client) -> Result<Chess, RecvPositionError> {
|
||||||
|
let response = fetch_message_from_server(client);
|
||||||
if response.is_empty() {
|
if response.is_empty() {
|
||||||
Err(RecvPositionError::UserCanceledError)
|
Err(RecvPositionError::UserCanceledError)
|
||||||
} else {
|
} else {
|
||||||
let game_info: GameInfo = serde_json::from_str(&response).unwrap();
|
Ok(parse_position(&response))
|
||||||
update_client(&game_info, client);
|
|
||||||
Ok(clichess::parse_position(&game_info.game_fen))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_for_next_move(
|
|
||||||
client: &mut Client,
|
|
||||||
stream: &mut UnixStream,
|
|
||||||
) -> Result<Chess, RecvPositionError> {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Information(String::from(
|
|
||||||
"Waiting for opponent move...",
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
send_request(client, stream, ClientRequest::WaitForNextMove);
|
|
||||||
let response = fetch_message_from_server(client);
|
|
||||||
if response == EXIT_MSG {
|
|
||||||
Err(RecvPositionError::UserCanceledError)
|
|
||||||
} else {
|
|
||||||
let game_info: GameInfo = serde_json::from_str(&response).unwrap();
|
|
||||||
update_client(&game_info, client);
|
|
||||||
Ok(clichess::parse_position(&game_info.game_fen))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_client(game_info: &GameInfo, client: &mut Client) {
|
|
||||||
if game_info.opponent_name != "" {
|
|
||||||
client.opponent_name = Some(game_info.opponent_name.clone());
|
|
||||||
}
|
|
||||||
if game_info.last_move != "" {
|
|
||||||
client.last_move = Some(game_info.last_move.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_message_from_server(client: &Client) -> String {
|
fn fetch_message_from_server(client: &Client) -> String {
|
||||||
match client.waiting_server_msg_receiver.recv().unwrap() {
|
let mut response = String::new();
|
||||||
WaitingServerMsg::MsgReceived => client.server_message_recv.recv().unwrap(),
|
while client.running.load(Ordering::SeqCst) {
|
||||||
WaitingServerMsg::UserCanceled => client.keyboard_input_recv.recv().unwrap(),
|
thread::sleep(time::Duration::from_millis(10));
|
||||||
|
{
|
||||||
|
let server_response_recv = &client.server_message_recv;
|
||||||
|
match server_response_recv.try_recv() {
|
||||||
|
Ok(msg) => {
|
||||||
|
response = msg;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
Err(TryRecvError::Disconnected) => println!("Error: server disconnected."),
|
||||||
|
Err(TryRecvError::Empty) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PromptedRoleResponse {
|
fn parse_position(string: &str) -> Chess {
|
||||||
Role(UserRole),
|
let setup: Fen = string.trim().parse().expect("Invalid message from server.");
|
||||||
Exit,
|
let position: Chess = setup.position().expect("Invalid message from server.");
|
||||||
Retry,
|
position
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt_user_for_role(client: &Client, stream: &mut UnixStream) -> PromptedRoleResponse {
|
fn fetch_initial_chess_position(client: &Client) -> Chess {
|
||||||
let response = send_request(client, stream, ClientRequest::FetchAvailableRoles);
|
parse_position(&fetch_message_from_server(client))
|
||||||
let available_roles = roles_from_str(&response).unwrap();
|
|
||||||
let mut prompt = String::new();
|
|
||||||
if !available_roles.contains(&UserRole::White) && !available_roles.contains(&UserRole::Black) {
|
|
||||||
prompt.push_str("You can only spectate this game. Press enter to start spectating.");
|
|
||||||
} else if available_roles.contains(&UserRole::White) {
|
|
||||||
prompt = String::from("Do you want to play as White (W)");
|
|
||||||
if available_roles.contains(&UserRole::Black) {
|
|
||||||
prompt.push_str(", Black (B)");
|
|
||||||
}
|
|
||||||
prompt.push_str(" or Spectate (S)?");
|
|
||||||
} else {
|
|
||||||
prompt = String::from("Do you want to play as Black (B) or Spectate (S)?");
|
|
||||||
}
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Information(prompt))
|
|
||||||
.unwrap();
|
|
||||||
let mut input = client.keyboard_input_recv.recv().unwrap();
|
|
||||||
input = String::from(input.trim());
|
|
||||||
if input == EXIT_MSG {
|
|
||||||
client.waiting_server_msg_receiver.recv().unwrap();
|
|
||||||
return PromptedRoleResponse::Exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !available_roles.contains(&UserRole::White) && !available_roles.contains(&UserRole::Black) {
|
|
||||||
//we can only spectate
|
|
||||||
input = String::from("S");
|
|
||||||
}
|
|
||||||
match clichess::parse_to_role(&input) {
|
|
||||||
Ok(r) => {
|
|
||||||
if !available_roles.contains(&r) {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(String::from(
|
|
||||||
"Sorry, this side is not available.",
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
return PromptedRoleResponse::Retry;
|
|
||||||
}
|
|
||||||
return PromptedRoleResponse::Role(r);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
client
|
|
||||||
.display_sender
|
|
||||||
.send(DisplayMessage::Message(format!("{}", e)))
|
|
||||||
.unwrap();
|
|
||||||
return PromptedRoleResponse::Retry;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_initial_chess_position(client: &Client, stream: &mut UnixStream) -> Chess {
|
|
||||||
let response = send_request(client, stream, ClientRequest::GetGameInfo);
|
|
||||||
let game_info: GameInfo = serde_json::from_str(&response).unwrap();
|
|
||||||
clichess::parse_position(&game_info.game_fen)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn roles_from_str(s: &str) -> Result<Vec<UserRole>, String> {
|
|
||||||
s.split(',').map(clichess::parse_to_role).collect()
|
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
use clichess::ClientRequest;
|
|
||||||
use std::os::unix::net::UnixStream;
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
|
||||||
eprintln!("Running clichess fetch_game_info version {}", version);
|
|
||||||
let mut stream = match UnixStream::connect("/tmp/clichess.socket") {
|
|
||||||
Ok(sock) => sock,
|
|
||||||
Err(_) => {
|
|
||||||
eprintln!("clichess daemon is not running.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
clichess::write_to_stream(&mut stream, serde_json::to_string(&ClientRequest::GetGameInfo).unwrap()).unwrap();
|
|
||||||
let response = clichess::read_line_from_stream(&stream).expect("Error message from server");
|
|
||||||
clichess::write_to_stream(&mut stream, serde_json::to_string(&ClientRequest::Exit).unwrap()).unwrap();
|
|
||||||
clichess::read_line_from_stream(&stream).expect("Error message from server");
|
|
||||||
println!("{}", response);
|
|
||||||
}
|
|
@ -1,30 +1,26 @@
|
|||||||
use clichess;
|
use clichess;
|
||||||
use clichess::{ClientRequest, GameInfo, Player, RecvPositionError, UserRole};
|
use clichess::{Player, RecvPositionError, UserRole, EXIT_MSG, PlayingStatus};
|
||||||
use shakmaty::{fen, Chess, Setup};
|
use serde_json::Value;
|
||||||
|
use shakmaty::{fen, Chess, Color, Setup};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::unix::net::{UnixListener, UnixStream};
|
use std::os::unix::net::{UnixListener, UnixStream};
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
|
use std::sync::mpsc::{channel, Receiver, TryRecvError};
|
||||||
use std::sync::{Arc, Condvar, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::time;
|
||||||
|
|
||||||
struct Server {
|
struct Server {
|
||||||
id: usize,
|
id: usize,
|
||||||
chess_position: Arc<Mutex<Chess>>,
|
chess_position: Arc<Mutex<Chess>>,
|
||||||
players: Arc<Mutex<HashMap<usize, (Player, Sender<GameInfo>, Arc<(Mutex<bool>, Condvar)>)>>>,
|
players: Arc<Mutex<HashMap<usize, (Player, Arc<Mutex<String>>)>>>,
|
||||||
others_serv_msg_recv: Receiver<GameInfo>,
|
others_serv_msg_buf: Arc<Mutex<String>>,
|
||||||
client_message_recv: Receiver<String>,
|
client_message_recv: Receiver<String>,
|
||||||
cvar: Arc<(Mutex<bool>, Condvar)>,
|
|
||||||
opponent_name: Arc<Mutex<Option<String>>>,
|
|
||||||
last_move: Arc<Mutex<Option<String>>>,
|
|
||||||
confirm_reset: Arc<(Mutex<bool>, Condvar)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
|
||||||
println!("Running clichess version {}", version);
|
|
||||||
let chess = Arc::new(Mutex::new(Chess::default()));
|
let chess = Arc::new(Mutex::new(Chess::default()));
|
||||||
let players = Arc::new(Mutex::new(HashMap::new()));
|
let players = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
@ -32,30 +28,20 @@ fn main() {
|
|||||||
|
|
||||||
let listener = UnixListener::bind("/tmp/clichess.socket").unwrap();
|
let listener = UnixListener::bind("/tmp/clichess.socket").unwrap();
|
||||||
|
|
||||||
//boolean shared amongst all server instances
|
|
||||||
let confirm_reset = Arc::new((Mutex::new(false), Condvar::new()));
|
|
||||||
|
|
||||||
// accept connections and process them, spawning a new thread for each one
|
// accept connections and process them, spawning a new thread for each one
|
||||||
for stream in listener.incoming() {
|
for stream in listener.incoming() {
|
||||||
match stream {
|
match stream {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
let condvar_pair = Arc::new((Mutex::new(false), Condvar::new()));
|
let client_message_recv = setup_client_message_recv(&stream).unwrap();
|
||||||
let client_message_recv =
|
let server = Server {
|
||||||
setup_client_message_recv(&stream, condvar_pair.clone()).unwrap();
|
|
||||||
let (others_serv_msg_sender, others_serv_msg_recv) = channel();
|
|
||||||
let mut server = Server {
|
|
||||||
id: counter,
|
id: counter,
|
||||||
chess_position: chess.clone(),
|
chess_position: chess.clone(),
|
||||||
players: players.clone(),
|
players: players.clone(),
|
||||||
others_serv_msg_recv,
|
others_serv_msg_buf: Arc::new(Mutex::new(String::new())),
|
||||||
client_message_recv,
|
client_message_recv,
|
||||||
cvar: condvar_pair,
|
|
||||||
opponent_name: Arc::new(Mutex::new(Option::None)),
|
|
||||||
last_move: Arc::new(Mutex::new(Option::None)),
|
|
||||||
confirm_reset: confirm_reset.clone(),
|
|
||||||
};
|
};
|
||||||
/* connection succeeded */
|
/* connection succeeded */
|
||||||
thread::spawn(move || handle_player(stream, &mut server, others_serv_msg_sender));
|
thread::spawn(move || handle_player(stream, server));
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -66,68 +52,219 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_player(
|
fn handle_player(mut stream: UnixStream, server: Server) {
|
||||||
mut stream: UnixStream,
|
match initialize_client(&mut stream, &server) {
|
||||||
server: &mut Server,
|
Ok((player, player_turn)) => main_loop(&mut stream, &server, player, player_turn),
|
||||||
others_serv_msg_sender: Sender<GameInfo>,
|
Err(e) => println!("User id {} could not be initialized: {}", server.id, e),
|
||||||
) {
|
};
|
||||||
loop {
|
|
||||||
//wait for input
|
|
||||||
let input = server.client_message_recv.recv().unwrap();
|
|
||||||
println!("got input: {}", input);
|
|
||||||
//route to right function
|
|
||||||
match serde_json::from_str(&input) {
|
|
||||||
Ok(ClientRequest::FetchAvailableRoles) => fetch_available_roles(&mut stream, server),
|
|
||||||
Ok(ClientRequest::Login {
|
|
||||||
username,
|
|
||||||
pubkey,
|
|
||||||
role,
|
|
||||||
}) => login(
|
|
||||||
&mut stream,
|
|
||||||
server,
|
|
||||||
username,
|
|
||||||
pubkey,
|
|
||||||
role,
|
|
||||||
others_serv_msg_sender.clone(),
|
|
||||||
),
|
|
||||||
Ok(ClientRequest::GetGameInfo) => send_game_info(&mut stream, server),
|
|
||||||
Ok(ClientRequest::WaitForNextMove) => {
|
|
||||||
//return true if user exited.
|
|
||||||
if wait_for_next_move(&mut stream, server) {
|
|
||||||
send_ok(&mut stream);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(ClientRequest::Play(movestr)) => play(&mut stream, server, movestr),
|
|
||||||
Ok(ClientRequest::Reset) => reset(&mut stream, server),
|
|
||||||
Ok(ClientRequest::Exit) => {
|
|
||||||
send_ok(&mut stream);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Bad request: {}", e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
player_disconnected(&server);
|
player_disconnected(&server);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn login(
|
fn initialize_client(
|
||||||
stream: &mut UnixStream,
|
stream: &mut UnixStream,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
username: String,
|
) -> Result<(Player, Color), RecvPositionError> {
|
||||||
pubkey: String,
|
//create player
|
||||||
role: UserRole,
|
let player = create_player(server, stream)?;
|
||||||
others_serv_msg_sender: Sender<GameInfo>,
|
let player_turn: Color;
|
||||||
) {
|
//send its id to the player
|
||||||
println!("server {}, player {} is logging in..", server.id, username);
|
println!(
|
||||||
//check_role
|
"server {}, send current id: {} to the player...",
|
||||||
|
server.id,
|
||||||
|
player.id
|
||||||
|
);
|
||||||
|
clichess::write_to_stream(stream, player.id.to_string()).unwrap();
|
||||||
|
println!("entering lobby...");
|
||||||
|
lobby_loop(stream, server, &player);
|
||||||
|
//send current position to the player
|
||||||
|
println!(
|
||||||
|
"server {}, send current position to the player...",
|
||||||
|
server.id
|
||||||
|
);
|
||||||
|
{
|
||||||
|
let chess = server.chess_position.lock().unwrap();
|
||||||
|
player_turn = chess.turn();
|
||||||
|
clichess::write_to_stream(stream, fen::fen(&*chess)).unwrap();
|
||||||
|
}
|
||||||
|
println!("server {}, current position to the player sent", server.id);
|
||||||
|
Ok((player, player_turn))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lobby_loop(stream: &mut UnixStream, server: &Server, player: &Player) {
|
||||||
|
loop {
|
||||||
|
let input;
|
||||||
|
match server.client_message_recv.recv() {
|
||||||
|
Ok(i) => input = i,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error while getting user input: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("got user input: {}", input);
|
||||||
|
if input == EXIT_MSG {
|
||||||
|
break;
|
||||||
|
} else if input == "refresh" {
|
||||||
|
send_players_to_client(stream, server);
|
||||||
|
} else {
|
||||||
|
clichess::write_to_stream(stream, format!("You challenged user {}", input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_players_to_client(stream: &mut UnixStream, server: &Server) {
|
||||||
|
println!("Sending players to client...");
|
||||||
|
let players = server.players.lock().unwrap();
|
||||||
|
let mut players_vec = Vec::new();
|
||||||
|
for (_, (player, _)) in players.iter() {
|
||||||
|
players_vec.push(player);
|
||||||
|
}
|
||||||
|
let json_msg = serde_json::to_string(&players_vec).unwrap();
|
||||||
|
clichess::write_to_stream(stream, json_msg).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_loop(stream: &mut UnixStream, server: &Server, player: Player, mut player_turn: Color) {
|
||||||
|
loop {
|
||||||
|
if clichess::is_player_turn(&player, player_turn) {
|
||||||
|
//let go of the lock while waiting for user input.
|
||||||
|
println!("server {}, waiting for player move..", server.id);
|
||||||
|
let input;
|
||||||
|
match server.client_message_recv.recv() {
|
||||||
|
Ok(i) => input = i,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error while getting user input: {}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if input == EXIT_MSG {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut chess = server.chess_position.lock().unwrap();
|
||||||
|
let players = server.players.lock().unwrap();
|
||||||
|
println!("User tried to play {}", input);
|
||||||
|
match clichess::try_to_play_move(&chess, input) {
|
||||||
|
Ok(played_chess) => *chess = played_chess,
|
||||||
|
Err(e) => println!("Error: {}", e),
|
||||||
|
};
|
||||||
|
let chessfen = fen::fen(&*chess);
|
||||||
|
for (id, (_, others_serv_msg_buf)) in players.iter() {
|
||||||
|
if server.id != *id {
|
||||||
|
others_serv_msg_buf.lock().unwrap().push_str(&chessfen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clichess::write_to_stream(stream, chessfen).unwrap();
|
||||||
|
player_turn = chess.turn();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let position_as_fen_result = wait_for_opponent_move(server);
|
||||||
|
if position_as_fen_result.is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let position_as_fen = position_as_fen_result.unwrap();
|
||||||
|
println!("server id: {}, sending {}", server.id, position_as_fen);
|
||||||
|
clichess::write_to_stream(stream, position_as_fen.clone()).unwrap();
|
||||||
|
let chess = server.chess_position.lock().unwrap();
|
||||||
|
player_turn = chess.turn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_opponent_move(server: &Server) -> Result<String, RecvPositionError> {
|
||||||
|
println!("server id: {}, waiting for move to send...", server.id);
|
||||||
|
//wait: either we receive next position from other server threads, or we receive
|
||||||
|
//"exit" from the client.
|
||||||
|
let returned_result: Result<String, RecvPositionError>;
|
||||||
|
loop {
|
||||||
|
{
|
||||||
|
let mut others_serv_msg_buf = server.others_serv_msg_buf.lock().unwrap();
|
||||||
|
if !others_serv_msg_buf.is_empty() {
|
||||||
|
returned_result = Ok(others_serv_msg_buf.clone());
|
||||||
|
others_serv_msg_buf.clear();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
match server.client_message_recv.try_recv() {
|
||||||
|
Ok(msg) => {
|
||||||
|
if msg == EXIT_MSG {
|
||||||
|
returned_result = Err(RecvPositionError::UserCanceledError);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"Client sent message while it's not its turn, this is an error."
|
||||||
|
);
|
||||||
|
println!("Here is the message: {}", msg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(TryRecvError::Disconnected) => println!("Error: client disconnected."),
|
||||||
|
Err(TryRecvError::Empty) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thread::sleep(time::Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
returned_result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_player(server: &Server, stream: &mut UnixStream) -> Result<Player, RecvPositionError> {
|
||||||
|
println!("Creating player {}...", server.id);
|
||||||
|
//get player name and pubkey
|
||||||
|
let username_pubkey_json = server
|
||||||
|
.client_message_recv
|
||||||
|
.recv()
|
||||||
|
.expect("Player closed connection.");
|
||||||
|
let username_pubkey_value: Value = serde_json::from_str(&username_pubkey_json).unwrap();
|
||||||
|
let username = username_pubkey_value["username"].to_string();
|
||||||
|
let public_key = username_pubkey_value["pubkey"].to_string();
|
||||||
|
println!("got username: {}", username);
|
||||||
|
println!("got pubkey: {}", public_key);
|
||||||
|
let role = UserRole::Spectator;
|
||||||
|
let mut players = server.players.lock().unwrap();
|
||||||
|
let player = Player {
|
||||||
|
id: server.id,
|
||||||
|
username,
|
||||||
|
public_key,
|
||||||
|
playing_status: PlayingStatus::WaitingInLobby,
|
||||||
|
role
|
||||||
|
};
|
||||||
|
players.insert(
|
||||||
|
server.id,
|
||||||
|
(player.clone(), server.others_serv_msg_buf.clone()),
|
||||||
|
);
|
||||||
|
println!("Created player {}", server.id);
|
||||||
|
Ok(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_user_role(
|
||||||
|
server: &Server,
|
||||||
|
stream: &mut UnixStream,
|
||||||
|
) -> Result<UserRole, RecvPositionError> {
|
||||||
|
let mut chosen_role: UserRole;
|
||||||
|
loop {
|
||||||
|
//send available roles
|
||||||
|
let available_roles = compute_available_roles_to_str(server);
|
||||||
|
println!("Computed available_roles as str: {}", available_roles);
|
||||||
|
clichess::write_to_stream(stream, available_roles.clone()).unwrap();
|
||||||
|
//receive chosen role
|
||||||
|
let chosen_role_str = server
|
||||||
|
.client_message_recv
|
||||||
|
.recv()
|
||||||
|
.expect("Player closed connection.");
|
||||||
|
if chosen_role_str.trim() == EXIT_MSG {
|
||||||
|
return Err(RecvPositionError::UserCanceledError);
|
||||||
|
}
|
||||||
|
println!("Client id {} has chosen {}", server.id, chosen_role_str);
|
||||||
|
let chosen_role_parse = clichess::parse_to_role(&chosen_role_str);
|
||||||
|
|
||||||
|
if chosen_role_parse.is_err() {
|
||||||
|
return Err(RecvPositionError::ParsePositionError);
|
||||||
|
}
|
||||||
|
chosen_role = chosen_role_parse.unwrap();
|
||||||
|
//check if role is still available after the choice
|
||||||
|
{
|
||||||
let mut available_roles_after_choice =
|
let mut available_roles_after_choice =
|
||||||
vec![UserRole::White, UserRole::Black, UserRole::Spectator];
|
vec![UserRole::White, UserRole::Black, UserRole::Spectator];
|
||||||
{
|
|
||||||
let players = server.players.lock().unwrap();
|
let players = server.players.lock().unwrap();
|
||||||
for (_, (player, _, _)) in players.iter() {
|
for (_, (player, _)) in players.iter() {
|
||||||
match available_roles_after_choice
|
match available_roles_after_choice
|
||||||
.iter()
|
.iter()
|
||||||
.position(|r| *r == player.role)
|
.position(|r| *r == player.role)
|
||||||
@ -136,223 +273,32 @@ fn login(
|
|||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
if available_roles_after_choice.contains(&chosen_role) {
|
||||||
if available_roles_after_choice.contains(&role) {
|
println!("OK");
|
||||||
create_player(server, username, pubkey, role, others_serv_msg_sender);
|
|
||||||
send_ok(stream);
|
|
||||||
} else {
|
|
||||||
send_ko(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_available_roles(stream: &mut UnixStream, server: &Server) {
|
|
||||||
println!(
|
|
||||||
"server {}, sending available role to the player...",
|
|
||||||
server.id
|
|
||||||
);
|
|
||||||
let available_roles = compute_available_roles_to_str(server);
|
|
||||||
println!("Computed available_roles as str: {}", available_roles);
|
|
||||||
clichess::write_to_stream(stream, available_roles.clone()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_game_info(stream: &mut UnixStream, server: &Server) {
|
|
||||||
println!("server {}, sending game info...", server.id);
|
|
||||||
let chess = server.chess_position.lock().unwrap();
|
|
||||||
let opponent_name = server
|
|
||||||
.opponent_name
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_default();
|
|
||||||
let last_move = server.last_move.lock().unwrap().clone().unwrap_or_default();
|
|
||||||
let game_info = GameInfo {
|
|
||||||
game_fen: fen::fen(&*chess),
|
|
||||||
opponent_name,
|
|
||||||
last_move,
|
|
||||||
};
|
|
||||||
clichess::write_to_stream(stream, serde_json::to_string(&game_info).unwrap()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_for_next_move(stream: &mut UnixStream, server: &mut Server) -> bool {
|
|
||||||
println!("server {}, waiting for opponent move...", server.id);
|
|
||||||
//acknowledge the request to the client
|
|
||||||
send_ok(stream);
|
|
||||||
//wait either for the other server, or the client exit.
|
|
||||||
match wait_for_opponent_move(server) {
|
|
||||||
Ok(game_info) => {
|
|
||||||
update_from_game_info(server, &game_info);
|
|
||||||
let game_info_serialized = serde_json::to_string(&game_info).unwrap();
|
|
||||||
//send game_info to client
|
|
||||||
clichess::write_to_stream(stream, game_info_serialized).unwrap();
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_for_opponent_move(server: &Server) -> Result<GameInfo, RecvPositionError> {
|
|
||||||
println!("server id: {}, waiting for move to send...", server.id);
|
|
||||||
//wait: either we receive next position from other server threads, or we receive
|
|
||||||
//"exit" from the client.
|
|
||||||
let mut returned_result = Err(RecvPositionError::UserCanceledError);
|
|
||||||
let (lock, cvar) = &*server.cvar;
|
|
||||||
let mut message_sent = lock.lock().unwrap();
|
|
||||||
*message_sent = false;
|
|
||||||
while !*message_sent {
|
|
||||||
message_sent = cvar.wait(message_sent).unwrap();
|
|
||||||
}
|
|
||||||
*message_sent = false;
|
|
||||||
match server.others_serv_msg_recv.try_recv() {
|
|
||||||
Ok(msg) => {
|
|
||||||
returned_result = Ok(msg);
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Disconnected) => {
|
|
||||||
println!("Error: other server thread disconnected while sending move.")
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Empty) => { /*nothing to do*/ }
|
|
||||||
}
|
|
||||||
match server.client_message_recv.try_recv() {
|
|
||||||
Ok(_) => {
|
|
||||||
println!("User exited.");
|
|
||||||
returned_result = Err(RecvPositionError::UserCanceledError);
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Disconnected) => println!("Error: client disconnected."),
|
|
||||||
Err(TryRecvError::Empty) => { /*nothing to do*/ }
|
|
||||||
}
|
|
||||||
returned_result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_from_game_info(server: &mut Server, game_info: &GameInfo) {
|
|
||||||
let mut chess_position = server.chess_position.lock().unwrap();
|
|
||||||
let mut opponent_name = server.opponent_name.lock().unwrap();
|
|
||||||
let mut last_move = server.last_move.lock().unwrap();
|
|
||||||
*chess_position = clichess::parse_position(&game_info.game_fen);
|
|
||||||
if game_info.opponent_name == "" {
|
|
||||||
*opponent_name = None;
|
|
||||||
} else {
|
|
||||||
*opponent_name = Some(game_info.opponent_name.clone());
|
|
||||||
}
|
|
||||||
if game_info.last_move == "" {
|
|
||||||
*last_move = None;
|
|
||||||
} else {
|
|
||||||
*last_move = Some(game_info.last_move.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn play(stream: &mut UnixStream, server: &Server, movestr: String) {
|
|
||||||
println!("server {}, user wants to play...", server.id);
|
|
||||||
let mut chess = server.chess_position.lock().unwrap();
|
|
||||||
let players = server.players.lock().unwrap();
|
|
||||||
let player = players.get(&server.id);
|
|
||||||
if player.is_none() || !clichess::is_player_turn(&player.unwrap().0, chess.turn()) {
|
|
||||||
send_ko(stream);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
println!("User tried to play {}", movestr.clone());
|
|
||||||
match clichess::try_to_play_move(&chess, movestr.clone()) {
|
|
||||||
Ok(played_chess) => *chess = played_chess,
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {}", e);
|
|
||||||
send_ko(stream);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let player_name = player
|
|
||||||
.expect("current server is in the server list")
|
|
||||||
.0
|
|
||||||
.username
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
//send play to other servers
|
|
||||||
let game_info_to_send = GameInfo {
|
|
||||||
game_fen: fen::fen(&*chess),
|
|
||||||
opponent_name: player_name,
|
|
||||||
last_move: movestr.clone(),
|
|
||||||
};
|
|
||||||
for (id, (_, others_serv_msg_sender, cvar_pair)) in players.iter() {
|
|
||||||
if server.id != *id {
|
|
||||||
others_serv_msg_sender
|
|
||||||
.send(game_info_to_send.clone())
|
|
||||||
.unwrap();
|
|
||||||
let (lock, cvar) = &**cvar_pair;
|
|
||||||
let mut message_sent = lock.lock().unwrap();
|
|
||||||
*message_sent = true;
|
|
||||||
cvar.notify_one();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let opponent_name = server.opponent_name.lock().unwrap();
|
|
||||||
let last_move = server.last_move.lock().unwrap();
|
|
||||||
//send updated info to client
|
|
||||||
clichess::write_to_stream(
|
|
||||||
stream,
|
|
||||||
serde_json::to_string(&GameInfo {
|
|
||||||
game_fen: fen::fen(&*chess),
|
|
||||||
opponent_name: opponent_name.clone().unwrap_or_default(),
|
|
||||||
last_move: last_move.clone().unwrap_or_default(),
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(stream: &mut UnixStream, server: &Server) {
|
|
||||||
//wait for the other server.
|
|
||||||
println!("client {} wants to restart game.", server.id);
|
|
||||||
let (lock, cvar) = &*server.confirm_reset;
|
|
||||||
let mut reset_confirmed = lock.lock().unwrap();
|
|
||||||
if *reset_confirmed {
|
|
||||||
println!("restarting game.");
|
|
||||||
{
|
|
||||||
let mut chess = server.chess_position.lock().unwrap();
|
|
||||||
*chess = Chess::default();
|
|
||||||
}
|
|
||||||
*reset_confirmed = false;
|
|
||||||
cvar.notify_one();
|
|
||||||
} else {
|
|
||||||
//wait for other server
|
|
||||||
println!("client {} is waiting.", server.id);
|
|
||||||
*reset_confirmed = true;
|
|
||||||
while *reset_confirmed {
|
|
||||||
reset_confirmed = cvar.wait(reset_confirmed).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
send_ok(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_ok(stream: &mut UnixStream) {
|
|
||||||
clichess::write_to_stream(stream, String::from("OK")).unwrap();
|
clichess::write_to_stream(stream, String::from("OK")).unwrap();
|
||||||
}
|
server
|
||||||
|
.client_message_recv
|
||||||
fn send_ko(stream: &mut UnixStream) {
|
.recv()
|
||||||
|
.expect("Player closed connection.");
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
println!("KO");
|
||||||
clichess::write_to_stream(stream, String::from("KO")).unwrap();
|
clichess::write_to_stream(stream, String::from("KO")).unwrap();
|
||||||
|
server
|
||||||
|
.client_message_recv
|
||||||
|
.recv()
|
||||||
|
.expect("Player closed connection.");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn create_player(
|
}
|
||||||
server: &Server,
|
Ok(chosen_role)
|
||||||
username: String,
|
|
||||||
public_key: String,
|
|
||||||
role: UserRole,
|
|
||||||
others_serv_msg_sender: Sender<GameInfo>,
|
|
||||||
) {
|
|
||||||
let mut players = server.players.lock().unwrap();
|
|
||||||
let player = Player {
|
|
||||||
role,
|
|
||||||
username,
|
|
||||||
public_key,
|
|
||||||
};
|
|
||||||
players.insert(
|
|
||||||
server.id,
|
|
||||||
(player.clone(), others_serv_msg_sender, server.cvar.clone()),
|
|
||||||
);
|
|
||||||
println!("Created player {}", server.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_available_roles_to_str(server: &Server) -> String {
|
fn compute_available_roles_to_str(server: &Server) -> String {
|
||||||
let mut available_roles = vec![UserRole::White, UserRole::Black, UserRole::Spectator];
|
let mut available_roles = vec![UserRole::White, UserRole::Black, UserRole::Spectator];
|
||||||
let players = server.players.lock().unwrap();
|
let players = server.players.lock().unwrap();
|
||||||
for (_, (player, _, _)) in players.iter() {
|
for (_, (player, _)) in players.iter() {
|
||||||
match available_roles.iter().position(|r| *r == player.role) {
|
match available_roles.iter().position(|r| *r == player.role) {
|
||||||
Some(index) => available_roles.remove(index),
|
Some(index) => available_roles.remove(index),
|
||||||
None => continue,
|
None => continue,
|
||||||
@ -369,16 +315,13 @@ fn player_disconnected(server: &Server) {
|
|||||||
let mut players = server.players.lock().unwrap();
|
let mut players = server.players.lock().unwrap();
|
||||||
let player_and_receiving_buf = players.get(&server.id);
|
let player_and_receiving_buf = players.get(&server.id);
|
||||||
if player_and_receiving_buf.is_some() {
|
if player_and_receiving_buf.is_some() {
|
||||||
let (player, _, _) = player_and_receiving_buf.expect("is some");
|
let (player, _) = player_and_receiving_buf.expect("is some");
|
||||||
println!("Player {} disconnected.", player.username);
|
println!("Player {} disconnected.", player.username);
|
||||||
}
|
}
|
||||||
players.remove(&server.id);
|
players.remove(&server.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_client_message_recv(
|
fn setup_client_message_recv(stream: &UnixStream) -> io::Result<Receiver<String>> {
|
||||||
stream: &UnixStream,
|
|
||||||
cvar_pair: Arc<(Mutex<bool>, Condvar)>,
|
|
||||||
) -> io::Result<Receiver<String>> {
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
let thread_stream = stream.try_clone()?;
|
let thread_stream = stream.try_clone()?;
|
||||||
|
|
||||||
@ -387,11 +330,6 @@ fn setup_client_message_recv(
|
|||||||
//wait for client message
|
//wait for client message
|
||||||
let buffer =
|
let buffer =
|
||||||
clichess::read_line_from_stream(&thread_stream).expect("Error message from server");
|
clichess::read_line_from_stream(&thread_stream).expect("Error message from server");
|
||||||
//notify the server if waiting
|
|
||||||
let (lock, cvar) = &*cvar_pair;
|
|
||||||
let mut message_sent = lock.lock().unwrap();
|
|
||||||
*message_sent = true;
|
|
||||||
cvar.notify_one();
|
|
||||||
let send_result = sender.send(buffer);
|
let send_result = sender.send(buffer);
|
||||||
// stop the thread during a disconnection
|
// stop the thread during a disconnection
|
||||||
if send_result.is_err() {
|
if send_result.is_err() {
|
||||||
|
131
src/lib.rs
131
src/lib.rs
@ -1,5 +1,4 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use colored::Colorize;
|
||||||
use shakmaty::fen::Fen;
|
|
||||||
use shakmaty::san::ParseSanError;
|
use shakmaty::san::ParseSanError;
|
||||||
use shakmaty::san::San;
|
use shakmaty::san::San;
|
||||||
use shakmaty::san::SanError;
|
use shakmaty::san::SanError;
|
||||||
@ -10,47 +9,34 @@ use std::io;
|
|||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{BufReader, Write};
|
use std::io::{BufReader, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use termion::raw::RawTerminal;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
//message to send to the server to signal we disconnected.
|
//message to send to the server to signal we disconnected.
|
||||||
pub const EXIT_MSG: &str = "exit";
|
pub const EXIT_MSG: &str = "exit";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub enum PlayingStatus {
|
||||||
|
WaitingInLobby,
|
||||||
|
Challenging(usize),
|
||||||
|
PlayingAgainst(usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
pub role: UserRole,
|
pub id: usize,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
|
pub playing_status: PlayingStatus,
|
||||||
|
pub role: UserRole
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
pub struct GameInfo {
|
|
||||||
pub game_fen: String,
|
|
||||||
pub opponent_name: String,
|
|
||||||
pub last_move: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Copy, Clone)]
|
|
||||||
pub enum UserRole {
|
pub enum UserRole {
|
||||||
White,
|
White,
|
||||||
Black,
|
Black,
|
||||||
Spectator,
|
Spectator,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
|
||||||
pub enum ClientRequest {
|
|
||||||
FetchAvailableRoles,
|
|
||||||
Login {
|
|
||||||
username: String,
|
|
||||||
pubkey: String,
|
|
||||||
role: UserRole,
|
|
||||||
},
|
|
||||||
GetGameInfo,
|
|
||||||
WaitForNextMove,
|
|
||||||
Play(String),
|
|
||||||
Reset,
|
|
||||||
Exit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for UserRole {
|
impl fmt::Display for UserRole {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
@ -130,27 +116,25 @@ impl fmt::Display for MoveInputError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_board_representation(
|
pub fn board_representation(chess: &Chess, side: Color) -> String {
|
||||||
chess: &Chess,
|
let mut s = String::from("");
|
||||||
side: Color,
|
|
||||||
stdout: &mut RawTerminal<io::StdoutLock>,
|
|
||||||
ascii_mode: bool,
|
|
||||||
) {
|
|
||||||
let mut rank_numbers;
|
let mut rank_numbers;
|
||||||
if side == Color::White {
|
if side == Color::White {
|
||||||
rank_numbers = 8;
|
rank_numbers = 8;
|
||||||
} else {
|
} else {
|
||||||
rank_numbers = 1;
|
rank_numbers = 1;
|
||||||
}
|
}
|
||||||
let mut linenb = 6;
|
for v in board_string_representation(chess, side) {
|
||||||
for v in board_string_representation(chess, side, ascii_mode) {
|
|
||||||
write!(stdout, "{}", termion::cursor::Goto(10, linenb)).unwrap();
|
|
||||||
for square_to_print in v {
|
for square_to_print in v {
|
||||||
print_square(&square_to_print, stdout, ascii_mode);
|
s.push_str(&format!(
|
||||||
|
"{}",
|
||||||
|
square_to_print
|
||||||
|
.square_representation
|
||||||
|
.color(square_to_print.color)
|
||||||
|
.on_color(square_to_print.background_color)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
linenb += 1;
|
s.push_str(&format!("{}\n\r", rank_numbers));
|
||||||
resetcolors(stdout);
|
|
||||||
write!(stdout, "{}", rank_numbers).unwrap();
|
|
||||||
if side == Color::White {
|
if side == Color::White {
|
||||||
rank_numbers -= 1;
|
rank_numbers -= 1;
|
||||||
} else {
|
} else {
|
||||||
@ -161,50 +145,14 @@ pub fn print_board_representation(
|
|||||||
if side == Color::Black {
|
if side == Color::Black {
|
||||||
files.reverse();
|
files.reverse();
|
||||||
}
|
}
|
||||||
resetcolors(stdout);
|
|
||||||
|
|
||||||
write!(stdout, "{}", termion::cursor::Goto(10, linenb)).unwrap();
|
|
||||||
for file in files {
|
for file in files {
|
||||||
write!(stdout, "{} ", file).unwrap();
|
s.push_str(&format!("{} ", file));
|
||||||
}
|
}
|
||||||
write!(stdout, "{}", termion::cursor::Goto(5, 15)).unwrap();
|
s.push_str("\n\r");
|
||||||
stdout.flush().unwrap();
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_square(
|
fn board_string_representation(chess: &Chess, side: Color) -> Vec<Vec<SquareToPrint>> {
|
||||||
square_to_print: &SquareToPrint,
|
|
||||||
stdout: &mut RawTerminal<io::StdoutLock>,
|
|
||||||
ascii_mode: bool,
|
|
||||||
) {
|
|
||||||
match square_to_print.color.as_ref() {
|
|
||||||
"black" => write!(stdout, "{}", termion::color::Fg(termion::color::Black)),
|
|
||||||
_ => write!(stdout, "{}", termion::color::Fg(termion::color::Reset)),
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match square_to_print.background_color.as_ref() {
|
|
||||||
"white" => write!(stdout, "{}", termion::color::Bg(termion::color::White)),
|
|
||||||
"green" => write!(stdout, "{}", termion::color::Bg(termion::color::Green)),
|
|
||||||
_ => write!(stdout, "{}", termion::color::Bg(termion::color::Reset)),
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
if ascii_mode {
|
|
||||||
write!(stdout, "{}", termion::color::Fg(termion::color::Reset)).unwrap();
|
|
||||||
write!(stdout, "{}", termion::color::Bg(termion::color::Reset)).unwrap();
|
|
||||||
}
|
|
||||||
write!(stdout, "{}", square_to_print.square_representation).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resetcolors(stdout: &mut RawTerminal<io::StdoutLock>) {
|
|
||||||
write!(stdout, "{}", termion::color::Fg(termion::color::Reset)).unwrap();
|
|
||||||
write!(stdout, "{}", termion::color::Bg(termion::color::Reset)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn board_string_representation(
|
|
||||||
chess: &Chess,
|
|
||||||
side: Color,
|
|
||||||
ascii_mode: bool,
|
|
||||||
) -> Vec<Vec<SquareToPrint>> {
|
|
||||||
let mut full_board_to_print = Vec::new();
|
let mut full_board_to_print = Vec::new();
|
||||||
for _ in 0..8 {
|
for _ in 0..8 {
|
||||||
full_board_to_print.push(Vec::new())
|
full_board_to_print.push(Vec::new())
|
||||||
@ -219,7 +167,7 @@ fn board_string_representation(
|
|||||||
} else {
|
} else {
|
||||||
"green".to_string()
|
"green".to_string()
|
||||||
},
|
},
|
||||||
square_representation: get_square_representation(&square, chess, ascii_mode),
|
square_representation: get_square_representation(&square, chess),
|
||||||
};
|
};
|
||||||
full_board_to_print[j as usize].push(square_to_print);
|
full_board_to_print[j as usize].push(square_to_print);
|
||||||
}
|
}
|
||||||
@ -243,18 +191,11 @@ pub fn try_to_play_move(chess: &Chess, movestr: String) -> Result<Chess, MoveInp
|
|||||||
Ok((*chess).clone().play(&mv)?)
|
Ok((*chess).clone().play(&mv)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_square_representation(square: &Square, chess: &Chess, ascii_mode: bool) -> String {
|
fn get_square_representation(square: &Square, chess: &Chess) -> String {
|
||||||
let board = (*chess).board();
|
let board = (*chess).board();
|
||||||
match board.piece_at(*square) {
|
match board.piece_at(*square) {
|
||||||
Some(piece) => format!(
|
Some(piece) => format!("{} ", piece_char_to_utf8(piece.char())),
|
||||||
"{} ",
|
None => " ".to_string(),
|
||||||
if ascii_mode {
|
|
||||||
piece.char()
|
|
||||||
} else {
|
|
||||||
piece_char_to_utf8(piece.char())
|
|
||||||
}
|
|
||||||
),
|
|
||||||
None => if ascii_mode { ". " } else { " " }.to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,9 +259,3 @@ pub fn is_player_turn(player: &Player, playing_side: Color) -> bool {
|
|||||||
(playing_side == Color::White && player.role == UserRole::White)
|
(playing_side == Color::White && player.role == UserRole::White)
|
||||||
|| (playing_side == Color::Black && player.role == UserRole::Black)
|
|| (playing_side == Color::Black && player.role == UserRole::Black)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_position(string: &str) -> Chess {
|
|
||||||
let setup: Fen = string.trim().parse().expect("Invalid message from server.");
|
|
||||||
let position: Chess = setup.position().expect("Invalid message from server.");
|
|
||||||
position
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user