local
This commit is contained in:
Generated
+449
@@ -0,0 +1,449 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"libc",
|
||||||
|
"mio 0.8.11",
|
||||||
|
"parking_lot",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.184"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio 0.8.11",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.117"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tetris"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"crossterm",
|
||||||
|
"rand",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.51.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"libc",
|
||||||
|
"mio 1.2.0",
|
||||||
|
"parking_lot",
|
||||||
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[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_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[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_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
@@ -4,3 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
crossterm = "0.27"
|
||||||
|
rand = "0.8"
|
||||||
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
use crate::piece::Piece;
|
||||||
|
|
||||||
|
/// Размеры игрового поля
|
||||||
|
pub const BOARD_WIDTH: i32 = 10;
|
||||||
|
pub const BOARD_HEIGHT: i32 = 20;
|
||||||
|
|
||||||
|
/// Тип клетки на доске: пустая или цветная
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub enum Cell {
|
||||||
|
Empty,
|
||||||
|
Occupied(u8), // ANSI цвет
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Игровое поле
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Board {
|
||||||
|
pub grid: Vec<Vec<Cell>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Board {
|
||||||
|
/// Создает новую пустую доску
|
||||||
|
pub fn new() -> Board {
|
||||||
|
let grid = vec![
|
||||||
|
vec![Cell::Empty; BOARD_WIDTH as usize];
|
||||||
|
BOARD_HEIGHT as usize
|
||||||
|
];
|
||||||
|
Board { grid }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверяет, можно ли разместить фигуру на доске
|
||||||
|
pub fn can_place(&self, piece: &Piece) -> bool {
|
||||||
|
for (x, y) in piece.positions() {
|
||||||
|
if x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !self.is_empty((x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверяет, пуста ли клетка
|
||||||
|
pub fn is_empty(&self, pos: (i32, i32)) -> bool {
|
||||||
|
let (x, y) = pos;
|
||||||
|
if x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
matches!(self.grid[y as usize][x as usize], Cell::Empty)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Фиксирует фигуру на доске
|
||||||
|
pub fn lock_piece(&mut self, piece: &Piece) {
|
||||||
|
let color = piece.piece_type.color();
|
||||||
|
for (x, y) in piece.positions() {
|
||||||
|
if y >= 0 && y < BOARD_HEIGHT && x >= 0 && x < BOARD_WIDTH {
|
||||||
|
self.grid[y as usize][x as usize] = Cell::Occupied(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Удаляет заполненные линии и возвращает количество очков
|
||||||
|
pub fn clear_lines(&mut self) -> usize {
|
||||||
|
let mut lines_cleared = 0;
|
||||||
|
let mut new_grid = vec![vec![Cell::Empty; BOARD_WIDTH as usize]; BOARD_HEIGHT as usize];
|
||||||
|
let mut new_y = (BOARD_HEIGHT - 1) as usize;
|
||||||
|
|
||||||
|
for y in (0..BOARD_HEIGHT as usize).rev() {
|
||||||
|
if self.grid[y].iter().all(|c| !matches!(c, Cell::Empty)) {
|
||||||
|
lines_cleared += 1;
|
||||||
|
} else {
|
||||||
|
new_grid[new_y] = self.grid[y].clone();
|
||||||
|
new_y -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.grid = new_grid;
|
||||||
|
lines_cleared
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверяет, закончилась ли игра (фигура не может появиться)
|
||||||
|
pub fn is_game_over(&self) -> bool {
|
||||||
|
!self.grid[0].iter().all(|c| matches!(c, Cell::Empty))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Board {
|
||||||
|
fn default() -> Self {
|
||||||
|
Board::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
+332
@@ -0,0 +1,332 @@
|
|||||||
|
use crate::board::{self, Board};
|
||||||
|
use crate::input::{self, Input};
|
||||||
|
use crate::piece::{Piece, PieceType};
|
||||||
|
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture};
|
||||||
|
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
|
||||||
|
use crossterm::{cursor, execute};
|
||||||
|
use std::io::{self, Stdout, Write};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
/// Количество кадров в секунду для рендеринга
|
||||||
|
const FPS: u64 = 60;
|
||||||
|
/// Базовое время падения фигуры (в миллисекундах)
|
||||||
|
const DROP_INTERVAL: u64 = 1000;
|
||||||
|
|
||||||
|
/// Состояние игры
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub enum GameStatus {
|
||||||
|
Running,
|
||||||
|
Paused,
|
||||||
|
GameOver,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Игра Тетрис
|
||||||
|
pub struct Game {
|
||||||
|
board: Board,
|
||||||
|
current_piece: Piece,
|
||||||
|
next_piece: Piece,
|
||||||
|
score: usize,
|
||||||
|
lines_cleared: usize,
|
||||||
|
level: usize,
|
||||||
|
drop_interval: u64,
|
||||||
|
last_drop: u64,
|
||||||
|
status: GameStatus,
|
||||||
|
input_rx: mpsc::Receiver<Input>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Game {
|
||||||
|
/// Создает новую игру
|
||||||
|
pub fn new() -> io::Result<Game> {
|
||||||
|
let current_piece = Piece::new(PieceType::random());
|
||||||
|
let next_piece = Piece::new(PieceType::random());
|
||||||
|
let (_, input_rx) = input::InputHandler::new();
|
||||||
|
|
||||||
|
Ok(Game {
|
||||||
|
board: Board::new(),
|
||||||
|
current_piece,
|
||||||
|
next_piece,
|
||||||
|
score: 0,
|
||||||
|
lines_cleared: 0,
|
||||||
|
level: 1,
|
||||||
|
drop_interval: DROP_INTERVAL,
|
||||||
|
last_drop: 0,
|
||||||
|
status: GameStatus::Running,
|
||||||
|
input_rx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Запускает игровой цикл
|
||||||
|
pub fn run(&mut self) -> io::Result<()> {
|
||||||
|
let mut stdout = io::stdout();
|
||||||
|
self.setup_terminal(&mut stdout)?;
|
||||||
|
|
||||||
|
let mut last_tick = Instant::now();
|
||||||
|
|
||||||
|
while self.status == GameStatus::Running {
|
||||||
|
// Обработка ввода
|
||||||
|
self.handle_input();
|
||||||
|
|
||||||
|
// Проверка состояния игры
|
||||||
|
if self.status == GameStatus::GameOver {
|
||||||
|
self.render_game_over(&mut stdout)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.status == GameStatus::Paused {
|
||||||
|
self.render_pause(&mut stdout)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление игры
|
||||||
|
let now = Instant::now();
|
||||||
|
let delta = now.duration_since(last_tick).as_millis() as u64;
|
||||||
|
last_tick = now;
|
||||||
|
|
||||||
|
self.update(delta)?;
|
||||||
|
self.render(&mut stdout)?;
|
||||||
|
|
||||||
|
// Небольшая пауза для контроля FPS
|
||||||
|
let elapsed = last_tick.duration_since(now).as_millis() as u64;
|
||||||
|
if elapsed < 1000 / FPS {
|
||||||
|
std::thread::sleep(Duration::from_millis(1000 / FPS - elapsed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.restore_terminal(&mut stdout)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Настройка терминала для игры
|
||||||
|
fn setup_terminal(&self, stdout: &mut Stdout) -> io::Result<()> {
|
||||||
|
enable_raw_mode()?;
|
||||||
|
execute!(
|
||||||
|
stdout,
|
||||||
|
EnterAlternateScreen,
|
||||||
|
EnableMouseCapture,
|
||||||
|
cursor::Hide
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Восстановление терминала
|
||||||
|
fn restore_terminal(&self, stdout: &mut Stdout) -> io::Result<()> {
|
||||||
|
disable_raw_mode()?;
|
||||||
|
execute!(
|
||||||
|
stdout,
|
||||||
|
LeaveAlternateScreen,
|
||||||
|
DisableMouseCapture,
|
||||||
|
cursor::Show
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Обработка ввода
|
||||||
|
fn handle_input(&mut self) {
|
||||||
|
while let Ok(input) = self.input_rx.try_recv() {
|
||||||
|
match input {
|
||||||
|
Input::Left => self.move_left(),
|
||||||
|
Input::Right => self.move_right(),
|
||||||
|
Input::Down => {
|
||||||
|
self.try_move_down();
|
||||||
|
self.last_drop = 0;
|
||||||
|
}
|
||||||
|
Input::Rotate => self.rotate(),
|
||||||
|
Input::Drop => {
|
||||||
|
self.drop();
|
||||||
|
self.last_drop = 0;
|
||||||
|
}
|
||||||
|
Input::Quit => {
|
||||||
|
self.status = GameStatus::GameOver;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Input::None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Обновление состояния игры
|
||||||
|
fn update(&mut self, delta: u64) -> io::Result<()> {
|
||||||
|
self.last_drop += delta;
|
||||||
|
|
||||||
|
if self.last_drop >= self.drop_interval {
|
||||||
|
self.last_drop = 0;
|
||||||
|
self.try_move_down();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Попытка переместить фигуру вниз
|
||||||
|
fn try_move_down(&mut self) {
|
||||||
|
let mut new_piece = self.current_piece.clone();
|
||||||
|
new_piece.move_down();
|
||||||
|
|
||||||
|
if self.board.can_place(&new_piece) {
|
||||||
|
self.current_piece = new_piece;
|
||||||
|
} else {
|
||||||
|
self.board.lock_piece(&self.current_piece);
|
||||||
|
self.spawn_new_piece();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Спавн новой фигуры
|
||||||
|
fn spawn_new_piece(&mut self) {
|
||||||
|
// Проверка на заполнение - конец игры
|
||||||
|
if !self.board.can_place(&self.next_piece) {
|
||||||
|
self.status = GameStatus::GameOver;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.current_piece = self.next_piece.clone();
|
||||||
|
self.next_piece = Piece::new(PieceType::random());
|
||||||
|
|
||||||
|
// Очистка линий
|
||||||
|
let lines = self.board.clear_lines();
|
||||||
|
if lines > 0 {
|
||||||
|
self.score += lines * lines * 100;
|
||||||
|
self.lines_cleared += lines;
|
||||||
|
self.level = self.lines_cleared / 10 + 1;
|
||||||
|
// Ускорение игры с уровнем
|
||||||
|
self.drop_interval = DROP_INTERVAL.saturating_sub((self.level - 1) as u64 * 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Попытка переместить фигуру влево
|
||||||
|
pub fn move_left(&mut self) {
|
||||||
|
let mut new_piece = self.current_piece.clone();
|
||||||
|
new_piece.move_left();
|
||||||
|
if self.board.can_place(&new_piece) {
|
||||||
|
self.current_piece = new_piece;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Попытка переместить фигуру вправо
|
||||||
|
pub fn move_right(&mut self) {
|
||||||
|
let mut new_piece = self.current_piece.clone();
|
||||||
|
new_piece.move_right();
|
||||||
|
if self.board.can_place(&new_piece) {
|
||||||
|
self.current_piece = new_piece;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Попытка повернуть фигуру
|
||||||
|
pub fn rotate(&mut self) {
|
||||||
|
let rotated = self.current_piece.rotated();
|
||||||
|
if self.board.can_place(&rotated) {
|
||||||
|
self.current_piece = rotated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Быстрый сброс фигуры
|
||||||
|
pub fn drop(&mut self) {
|
||||||
|
loop {
|
||||||
|
let mut new_piece = self.current_piece.clone();
|
||||||
|
new_piece.move_down();
|
||||||
|
if !self.board.can_place(&new_piece) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.current_piece = new_piece;
|
||||||
|
}
|
||||||
|
// Фиксируем фигуру
|
||||||
|
self.board.lock_piece(&self.current_piece);
|
||||||
|
self.spawn_new_piece();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Рендер игры
|
||||||
|
fn render(&self, stdout: &mut Stdout) -> io::Result<()> {
|
||||||
|
stdout.write_all(b"\x1B[2J")?; // Очистка экрана
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
writeln!(
|
||||||
|
stdout,
|
||||||
|
"Tetris - Score: {} - Lines: {} - Level: {}",
|
||||||
|
self.score, self.lines_cleared, self.level
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Отрисовка доски
|
||||||
|
for y in 0..board::BOARD_HEIGHT {
|
||||||
|
stdout.write_all(b"|")?;
|
||||||
|
for x in 0..board::BOARD_WIDTH {
|
||||||
|
let cell = self.board.grid[y as usize][x as usize];
|
||||||
|
match cell {
|
||||||
|
board::Cell::Empty => {
|
||||||
|
// Проверяем, есть ли там текущая фигура
|
||||||
|
if self.current_piece.positions().contains(&(x as i32, y as i32)) {
|
||||||
|
let color = self.current_piece.piece_type.color();
|
||||||
|
write!(stdout, "\x1B[{}m#\x1B[0m", color)?;
|
||||||
|
} else {
|
||||||
|
stdout.write_all(b" ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
board::Cell::Occupied(color) => {
|
||||||
|
write!(stdout, "\x1B[{}m#\x1B[0m", color)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout.write_all(b"|\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нижняя граница
|
||||||
|
stdout.write_all(b"+")?;
|
||||||
|
for _ in 0..board::BOARD_WIDTH {
|
||||||
|
stdout.write_all(b"-")?;
|
||||||
|
}
|
||||||
|
stdout.write_all(b"+\n")?;
|
||||||
|
|
||||||
|
// Следующая фигура
|
||||||
|
writeln!(stdout, "Next:")?;
|
||||||
|
self.render_preview(stdout, &self.next_piece)?;
|
||||||
|
|
||||||
|
// Управление
|
||||||
|
writeln!(stdout, "Controls: Arrow Keys - Move | Space - Drop | Q - Quit")?;
|
||||||
|
|
||||||
|
stdout.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Рендер превью фигуры
|
||||||
|
fn render_preview(&self, stdout: &mut Stdout, piece: &Piece) -> io::Result<()> {
|
||||||
|
let mut preview_grid = vec![vec![' '; 4]; 4];
|
||||||
|
for block in &piece.blocks {
|
||||||
|
let x = (block.x + 1) as usize;
|
||||||
|
let y = (block.y + 1) as usize;
|
||||||
|
if x < 4 && y < 4 {
|
||||||
|
preview_grid[y][x] = '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = piece.piece_type.color();
|
||||||
|
for row in preview_grid {
|
||||||
|
for c in row {
|
||||||
|
if c == '#' {
|
||||||
|
write!(stdout, "\x1B[{}m#\x1B[0m", color)?;
|
||||||
|
} else {
|
||||||
|
stdout.write_all(b" ")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout.write_all(b"\n")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Рендер экрана Game Over
|
||||||
|
fn render_game_over(&self, stdout: &mut Stdout) -> io::Result<()> {
|
||||||
|
writeln!(stdout, "Game Over! Final Score: {}", self.score)?;
|
||||||
|
writeln!(stdout, "Press any key to exit...")?;
|
||||||
|
stdout.flush()?;
|
||||||
|
// Ожидание нажатия клавиши
|
||||||
|
let _ = event::read();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Рендер экрана паузы
|
||||||
|
fn render_pause(&mut self, stdout: &mut Stdout) -> io::Result<()> {
|
||||||
|
writeln!(stdout, "Paused - Press any key to resume...")?;
|
||||||
|
stdout.flush()?;
|
||||||
|
// Ожидание нажатия клавиши
|
||||||
|
let _ = event::read();
|
||||||
|
self.status = GameStatus::Running;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Типы команд ввода
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub enum Input {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Down,
|
||||||
|
Rotate,
|
||||||
|
Drop,
|
||||||
|
Quit,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Обработчик ввода
|
||||||
|
pub struct InputHandler {
|
||||||
|
tx: mpsc::Sender<Input>,
|
||||||
|
running: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputHandler {
|
||||||
|
/// Создает новый обработчик ввода
|
||||||
|
pub fn new() -> (InputHandler, mpsc::Receiver<Input>) {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let running = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
||||||
|
|
||||||
|
let running_clone = running.clone();
|
||||||
|
let tx_clone = tx.clone();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
while running_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
|
if event::poll(Duration::from_millis(50)).unwrap() {
|
||||||
|
if let Event::Key(key) = event::read().unwrap() {
|
||||||
|
let input = match key {
|
||||||
|
_ if key.code == KeyCode::Left => Input::Left,
|
||||||
|
_ if key.code == KeyCode::Right => Input::Right,
|
||||||
|
_ if key.code == KeyCode::Down => Input::Down,
|
||||||
|
_ if key.code == KeyCode::Up => Input::Rotate,
|
||||||
|
_ if key.code == KeyCode::Char(' ') => Input::Drop,
|
||||||
|
_ if key.code == KeyCode::Char('q') || key.code == KeyCode::Char('Q') => Input::Quit,
|
||||||
|
_ => Input::None,
|
||||||
|
};
|
||||||
|
// Игнорируем модификаторы для основных клавиш
|
||||||
|
if key.modifiers == KeyModifiers::NONE {
|
||||||
|
let _ = tx_clone.send(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(InputHandler { tx, running }, rx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Останавливает обработчик ввода
|
||||||
|
pub fn stop(&self) {
|
||||||
|
self.running.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InputHandler {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-2
@@ -1,3 +1,13 @@
|
|||||||
fn main() {
|
mod board;
|
||||||
println!("Hello, world!");
|
mod game;
|
||||||
|
mod input;
|
||||||
|
mod piece;
|
||||||
|
|
||||||
|
use game::Game;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let mut game = Game::new()?;
|
||||||
|
game.run()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
/// Тип фигуры тетриса
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub enum PieceType {
|
||||||
|
I,
|
||||||
|
O,
|
||||||
|
T,
|
||||||
|
S,
|
||||||
|
Z,
|
||||||
|
J,
|
||||||
|
L,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PieceType {
|
||||||
|
/// Возвращает все возможные типы фигур
|
||||||
|
pub fn all() -> [PieceType; 7] {
|
||||||
|
[PieceType::I, PieceType::O, PieceType::T, PieceType::S, PieceType::Z, PieceType::J, PieceType::L]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Генерирует случайную фигуру
|
||||||
|
pub fn random() -> PieceType {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let idx: u8 = rng.gen_range(0..7);
|
||||||
|
match idx {
|
||||||
|
0 => PieceType::I,
|
||||||
|
1 => PieceType::O,
|
||||||
|
2 => PieceType::T,
|
||||||
|
3 => PieceType::S,
|
||||||
|
4 => PieceType::Z,
|
||||||
|
5 => PieceType::J,
|
||||||
|
6 => PieceType::L,
|
||||||
|
_ => PieceType::I,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Возвращает цвет (ANSI код) для фигуры
|
||||||
|
pub fn color(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
PieceType::I => 36, // Cyan
|
||||||
|
PieceType::O => 33, // Yellow
|
||||||
|
PieceType::T => 35, // Magenta
|
||||||
|
PieceType::S => 32, // Green
|
||||||
|
PieceType::Z => 31, // Red
|
||||||
|
PieceType::J => 34, // Blue
|
||||||
|
PieceType::L => 37, // White
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Блок фигуры - относительная координата
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Block {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Фигура тетриса с позицией
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Piece {
|
||||||
|
pub piece_type: PieceType,
|
||||||
|
pub blocks: [Block; 4],
|
||||||
|
pub center: (i32, i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Piece {
|
||||||
|
/// Создает новую фигуру заданного типа в центре сверху
|
||||||
|
pub fn new(piece_type: PieceType) -> Piece {
|
||||||
|
let blocks = match piece_type {
|
||||||
|
PieceType::I => [
|
||||||
|
Block { x: -1, y: 0 },
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 1, y: 0 },
|
||||||
|
Block { x: 2, y: 0 },
|
||||||
|
],
|
||||||
|
PieceType::O => [
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 1, y: 0 },
|
||||||
|
Block { x: 0, y: 1 },
|
||||||
|
Block { x: 1, y: 1 },
|
||||||
|
],
|
||||||
|
PieceType::T => [
|
||||||
|
Block { x: -1, y: 0 },
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 1, y: 0 },
|
||||||
|
Block { x: 0, y: 1 },
|
||||||
|
],
|
||||||
|
PieceType::S => [
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 1, y: 0 },
|
||||||
|
Block { x: -1, y: 1 },
|
||||||
|
Block { x: 0, y: 1 },
|
||||||
|
],
|
||||||
|
PieceType::Z => [
|
||||||
|
Block { x: -1, y: 0 },
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 0, y: 1 },
|
||||||
|
Block { x: 1, y: 1 },
|
||||||
|
],
|
||||||
|
PieceType::J => [
|
||||||
|
Block { x: -1, y: 0 },
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 1, y: 0 },
|
||||||
|
Block { x: -1, y: 1 },
|
||||||
|
],
|
||||||
|
PieceType::L => [
|
||||||
|
Block { x: -1, y: 0 },
|
||||||
|
Block { x: 0, y: 0 },
|
||||||
|
Block { x: 1, y: 0 },
|
||||||
|
Block { x: 1, y: 1 },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
Piece {
|
||||||
|
piece_type,
|
||||||
|
blocks,
|
||||||
|
center: (5, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Возвращает позиции всех блоков в абсолютных координатах
|
||||||
|
pub fn positions(&self) -> Vec<(i32, i32)> {
|
||||||
|
self.blocks
|
||||||
|
.iter()
|
||||||
|
.map(|b| (self.center.0 + b.x, self.center.1 + b.y))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Поворачивает фигуру на 90 градусов по часовой стрелке
|
||||||
|
pub fn rotate(&mut self) {
|
||||||
|
for block in &mut self.blocks {
|
||||||
|
let (x, y) = (block.x, block.y);
|
||||||
|
block.x = -y;
|
||||||
|
block.y = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Возвращает новую фигуру с тем же типом, но повернутую
|
||||||
|
pub fn rotated(&self) -> Piece {
|
||||||
|
let mut new = self.clone();
|
||||||
|
new.rotate();
|
||||||
|
new
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Двигает фигуру влево
|
||||||
|
pub fn move_left(&mut self) {
|
||||||
|
self.center.0 -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Двигает фигуру вправо
|
||||||
|
pub fn move_right(&mut self) {
|
||||||
|
self.center.0 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Двигает фигуру вниз
|
||||||
|
pub fn move_down(&mut self) {
|
||||||
|
self.center.1 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Проверяет, находится ли фигура в допустимой области
|
||||||
|
pub fn is_within_bounds(&self, width: i32, height: i32) -> bool {
|
||||||
|
for block in &self.blocks {
|
||||||
|
let (x, y) = (self.center.0 + block.x, self.center.1 + block.y);
|
||||||
|
if x < 0 || x >= width || y >= height {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user