Rhai
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
.DS_Store
|
||||||
277
Cargo.lock
generated
277
Cargo.lock
generated
@@ -1,6 +1,26 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"const-random",
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
@@ -56,6 +76,12 @@ version = "1.0.80"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
@@ -120,6 +146,35 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-random"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
|
||||||
|
dependencies = [
|
||||||
|
"const-random-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-random-macro"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.12",
|
||||||
|
"once_cell",
|
||||||
|
"tiny-keccak",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-error"
|
||||||
|
version = "0.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efcdb2972eb64230b4c50646d8498ff73f5128d196a90c7236eec4cbe8619b8f"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -145,12 +200,30 @@ version = "0.8.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
@@ -162,12 +235,44 @@ dependencies = [
|
|||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||||
|
dependencies = [
|
||||||
|
"allocator-api2",
|
||||||
|
"equivalent",
|
||||||
|
"foldhash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
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 = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@@ -179,9 +284,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.153"
|
version = "0.2.177"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
@@ -203,15 +314,50 @@ dependencies = [
|
|||||||
"itertools",
|
"itertools",
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"rhai",
|
||||||
"termion",
|
"termion",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "no-std-compat"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numtoa"
|
name = "numtoa"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -236,6 +382,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -263,7 +415,7 @@ version = "0.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -301,6 +453,67 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rhai"
|
||||||
|
version = "1.23.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "527390cc333a8d2cd8237890e15c36518c26f8b54c903d86fc59f42f08d25594"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"core-error",
|
||||||
|
"hashbrown",
|
||||||
|
"instant",
|
||||||
|
"libm",
|
||||||
|
"no-std-compat",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"rhai_codegen",
|
||||||
|
"smallvec",
|
||||||
|
"smartstring",
|
||||||
|
"thin-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rhai_codegen"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smartstring"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"static_assertions",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -330,6 +543,21 @@ dependencies = [
|
|||||||
"redox_termios",
|
"redox_termios",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thin-vec"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
@@ -342,12 +570,27 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.1+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -413,3 +656,29 @@ name = "windows_x86_64_msvc"
|
|||||||
version = "0.52.4"
|
version = "0.52.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|||||||
@@ -9,4 +9,13 @@ clap = { version = "4.5.1", features = ["derive"] }
|
|||||||
itertools = "0.12.1"
|
itertools = "0.12.1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rayon = "1.9.0"
|
rayon = "1.9.0"
|
||||||
|
rhai = { version = "1.23.4", features = [
|
||||||
|
"sync",
|
||||||
|
"no_time",
|
||||||
|
"no_module",
|
||||||
|
"no_custom_syntax",
|
||||||
|
"no_std",
|
||||||
|
"only_i64",
|
||||||
|
"f32_float",
|
||||||
|
] }
|
||||||
termion = "3.0.0"
|
termion = "3.0.0"
|
||||||
|
|||||||
133
src/agents.rhai/chase.rhai
Normal file
133
src/agents.rhai/chase.rhai
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
fn random_action(board) {
|
||||||
|
let symb = rand_symb();
|
||||||
|
let pos = rand_int(0, 10);
|
||||||
|
let action = Action(symb, pos);
|
||||||
|
|
||||||
|
while !board.can_play(action) {
|
||||||
|
let symb = rand_symb();
|
||||||
|
let pos = rand_int(0, 10);
|
||||||
|
action = Action(symb, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Returns an array of (idx, influence) for each empty slot in the board.
|
||||||
|
/// - idx is the index of this slot
|
||||||
|
/// - f32 is the influence of this slot
|
||||||
|
fn free_slots_by_influence(board) {
|
||||||
|
// TODO: EDGE CASE
|
||||||
|
// We fail if we have ___/-_ (div by zero)
|
||||||
|
|
||||||
|
// Fill all empty slots with fives and compute starting value
|
||||||
|
//
|
||||||
|
// This should always result in an evaluatable expression,
|
||||||
|
// since parenthesis do not exist.
|
||||||
|
// The only way to divide by zero is by doing something like /(5-2+3).
|
||||||
|
let filled = board;
|
||||||
|
for i in filled.free_spots_idx() {
|
||||||
|
filled[i] = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = filled.evaluate();
|
||||||
|
|
||||||
|
// Test each slot:
|
||||||
|
// Increase its value by 1, and record the effect on the
|
||||||
|
// expression's total value.
|
||||||
|
// This isn't a perfect metric, but it's pretty good.
|
||||||
|
let slots = [];
|
||||||
|
for i in 0..board.size() {
|
||||||
|
let slot = board[i];
|
||||||
|
if slot != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = filled;
|
||||||
|
b[i] = 6;
|
||||||
|
|
||||||
|
if b.evaluate() == () {
|
||||||
|
print(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
slots.push([i, b.evaluate() - base]);
|
||||||
|
}
|
||||||
|
|
||||||
|
slots.sort(|a, b| b[0].abs() - a[0].abs());
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main step function (shared between min and max)
|
||||||
|
fn chase_step(board, minimize) {
|
||||||
|
let available_numbers = {
|
||||||
|
let available = [];
|
||||||
|
for i in 0..10 {
|
||||||
|
if !board.contains(i) {
|
||||||
|
available.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
available
|
||||||
|
};
|
||||||
|
|
||||||
|
// For the code below, we must guarantee that
|
||||||
|
// min_slots + max_slots <= available_numbers.len
|
||||||
|
let n_free = board.free_spots();
|
||||||
|
if available_numbers.len() < n_free || n_free >= 10 {
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
let slots = free_slots_by_influence(board);
|
||||||
|
if slots.len() == 0 {
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get the most influential position
|
||||||
|
let pos = slots[0][0];
|
||||||
|
let val = slots[0][1];
|
||||||
|
|
||||||
|
// Choose next number if we can't make the move.
|
||||||
|
// Prevents division by zero.
|
||||||
|
// This isn't perfect, and may fail if we run out of numbers
|
||||||
|
// (This is, however, very unlikely)
|
||||||
|
let selected_symbol = ();
|
||||||
|
let offset = 0;
|
||||||
|
while selected_symbol == () || offset < available_numbers.len() {
|
||||||
|
selected_symbol = {
|
||||||
|
if minimize {
|
||||||
|
if val >= 0.0 {
|
||||||
|
available_numbers[offset]
|
||||||
|
} else {
|
||||||
|
available_numbers[available_numbers.len() - 1 - offset]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if val <= 0.0 {
|
||||||
|
available_numbers[offset]
|
||||||
|
} else {
|
||||||
|
available_numbers[available_numbers.len() - 1 - offset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let action = Action(selected_symbol, pos);
|
||||||
|
if board.can_play(action) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to random if we can't find a valid move
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Minimizer step
|
||||||
|
fn step_min(board) {
|
||||||
|
chase_step(board, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximizer step
|
||||||
|
fn step_max(board) {
|
||||||
|
chase_step(board, false)
|
||||||
|
}
|
||||||
109
src/agents.rhai/diffuse.rhai
Normal file
109
src/agents.rhai/diffuse.rhai
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
fn random_action(board) {
|
||||||
|
let symb = rand_symb();
|
||||||
|
let pos = rand_int(0, 10);
|
||||||
|
let action = Action(symb, pos);
|
||||||
|
|
||||||
|
while !board.can_play(action) {
|
||||||
|
let symb = rand_symb();
|
||||||
|
let pos = rand_int(0, 10);
|
||||||
|
action = Action(symb, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_symb(board, symb) {
|
||||||
|
if board.contains(symb) {
|
||||||
|
print("ERROR: called `step_symb` with a symbol that's already on the board!");
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
let board_size = board.size();
|
||||||
|
let dist = [];
|
||||||
|
|
||||||
|
// Initialize distance array with large values (board size + 1)
|
||||||
|
for i in 0..11 {
|
||||||
|
dist.push(board_size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set boundary conditions
|
||||||
|
dist[0] = 1;
|
||||||
|
dist[10] = 1;
|
||||||
|
|
||||||
|
// Set distances to 0 for positions with operators
|
||||||
|
for i in 0..11 {
|
||||||
|
let cell = board[i];
|
||||||
|
if cell != () && cell.is_op() {
|
||||||
|
dist[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diffusion algorithm - propagate distances
|
||||||
|
let did_something = true;
|
||||||
|
while did_something {
|
||||||
|
did_something = false;
|
||||||
|
for i in 1..10 {
|
||||||
|
let left_dist = dist[i - 1];
|
||||||
|
let right_dist = dist[i + 1];
|
||||||
|
let new_dist = min(left_dist + 1, right_dist + 1);
|
||||||
|
|
||||||
|
if new_dist < dist[i] {
|
||||||
|
did_something = true;
|
||||||
|
dist[i] = new_dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find maximum distance
|
||||||
|
let max_dist = 0;
|
||||||
|
for d in dist {
|
||||||
|
if d > max_dist {
|
||||||
|
max_dist = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to place at positions with maximum distance
|
||||||
|
loop {
|
||||||
|
for pos in 0..11 {
|
||||||
|
if dist[pos] >= max_dist {
|
||||||
|
let action = Action(symb, pos);
|
||||||
|
if board.can_play(action) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if max_dist == 0 {
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
max_dist -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_min(board) {
|
||||||
|
let operators = ["+", "-", "*", "/"];
|
||||||
|
operators = rand_shuffle(operators);
|
||||||
|
|
||||||
|
for op in operators {
|
||||||
|
if !board.contains(op) {
|
||||||
|
return step_symb(board, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_max(board) {
|
||||||
|
let operators = ["+", "-", "*", "/"];
|
||||||
|
operators = rand_shuffle(operators);
|
||||||
|
|
||||||
|
for op in operators {
|
||||||
|
if !board.contains(op) {
|
||||||
|
return step_symb(board, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: chase
|
||||||
|
return random_action(board);
|
||||||
|
}
|
||||||
21
src/agents.rhai/random.rhai
Normal file
21
src/agents.rhai/random.rhai
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
fn random_action(board) {
|
||||||
|
let symb = rand_symb();
|
||||||
|
let pos = rand_int(0, 10);
|
||||||
|
let action = Action(symb, pos);
|
||||||
|
|
||||||
|
while !board.can_play(action) {
|
||||||
|
let symb = rand_symb();
|
||||||
|
let pos = rand_int(0, 10);
|
||||||
|
action = Action(symb, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_min(board) {
|
||||||
|
random_action(board)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_max(board) {
|
||||||
|
random_action(board)
|
||||||
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
use std::{cmp::Ordering, iter};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use rayon::iter::{ParallelBridge, ParallelIterator};
|
use rayon::iter::{ParallelBridge, ParallelIterator};
|
||||||
|
use std::{cmp::Ordering, iter};
|
||||||
|
|
||||||
use super::{Agent, Chase, MaximizerAgent, MinimizerAgent};
|
use super::{Agent, MaximizerAgent, MinimizerAgent};
|
||||||
use crate::{
|
use crate::{
|
||||||
agents::{util::best_board_noop, Diffuse},
|
agents::util::best_board_noop,
|
||||||
board::{Board, PlayerAction},
|
board::{Board, PlayerAction},
|
||||||
util::{Player, Symb},
|
util::{Player, Symb},
|
||||||
};
|
};
|
||||||
@@ -28,11 +27,8 @@ impl Brutus {
|
|||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
if symbols.is_empty() {
|
if symbols.is_empty() {
|
||||||
return if minimize {
|
// TODO: only valid (chase)
|
||||||
Chase::new(self.player).step_min(board)
|
return Ok(PlayerAction::new_random(&mut rand::thread_rng(), board));
|
||||||
} else {
|
|
||||||
Chase::new(self.player).step_max(board)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of free slots
|
// Number of free slots
|
||||||
@@ -86,11 +82,8 @@ impl Brutus {
|
|||||||
// TODO: why can `items` be empty?
|
// TODO: why can `items` be empty?
|
||||||
// We shouldn't need this escape hatch
|
// We shouldn't need this escape hatch
|
||||||
if items.is_empty() {
|
if items.is_empty() {
|
||||||
return if minimize {
|
// TODO: only valid (diffuse)
|
||||||
Diffuse::new(self.player).step_min(board)
|
return Ok(PlayerAction::new_random(&mut rand::thread_rng(), board));
|
||||||
} else {
|
|
||||||
Diffuse::new(self.player).step_max(board)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (t, _) = items.first().unwrap();
|
let (t, _) = items.first().unwrap();
|
||||||
@@ -111,11 +104,8 @@ impl Brutus {
|
|||||||
|
|
||||||
// Final escape hatch, if we didn't decide to place any symbols
|
// Final escape hatch, if we didn't decide to place any symbols
|
||||||
// (which is possible, since we add one to free_spots above!)
|
// (which is possible, since we add one to free_spots above!)
|
||||||
if minimize {
|
// TODO: only valid (chase)
|
||||||
Chase::new(self.player).step_min(board)
|
return Ok(PlayerAction::new_random(&mut rand::thread_rng(), board));
|
||||||
} else {
|
|
||||||
Chase::new(self.player).step_max(board)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
use anyhow::{bail, Result};
|
|
||||||
use std::num::NonZeroU8;
|
|
||||||
|
|
||||||
use super::{Agent, MaximizerAgent, MinimizerAgent, Random};
|
|
||||||
use crate::{
|
|
||||||
agents::util::free_slots_by_influence,
|
|
||||||
board::{Board, PlayerAction},
|
|
||||||
util::{Player, Symb},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Chase {
|
|
||||||
player: Player,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Chase {
|
|
||||||
pub fn new(player: Player) -> Self {
|
|
||||||
Self { player }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn step(&mut self, board: &Board, minimize: bool) -> Result<PlayerAction> {
|
|
||||||
let available_numbers = (0..=9)
|
|
||||||
.map(|x| match x {
|
|
||||||
0 => Symb::Zero,
|
|
||||||
x => Symb::Number(NonZeroU8::new(x).unwrap()),
|
|
||||||
})
|
|
||||||
.filter(|x| !board.contains(*x))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// For the code below, we must guarantee that
|
|
||||||
// min_slots + max_slots <= available_numbers.len
|
|
||||||
let n_free = board.get_board().iter().filter(|x| x.is_none()).count();
|
|
||||||
if available_numbers.len() < n_free || n_free >= 10 {
|
|
||||||
return Random::new(self.player).step_min(board);
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = free_slots_by_influence(board);
|
|
||||||
if t.is_none() {
|
|
||||||
bail!("could not compute next move!")
|
|
||||||
}
|
|
||||||
let t = t.unwrap();
|
|
||||||
|
|
||||||
if t.is_empty() {
|
|
||||||
return Random::new(self.player).step_min(board);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (pos, val) = t[0];
|
|
||||||
|
|
||||||
// Choose next number if we can't make the a move.
|
|
||||||
// Prevents division by zero.
|
|
||||||
// This isn't perfect, and may fail if we run out of numbers
|
|
||||||
// (This is, however, very unlikely)
|
|
||||||
let mut symb = None;
|
|
||||||
let mut offset = 0;
|
|
||||||
while symb.is_none()
|
|
||||||
|| !board.can_play(&PlayerAction {
|
|
||||||
symb: symb.unwrap(),
|
|
||||||
pos,
|
|
||||||
}) {
|
|
||||||
symb = Some({
|
|
||||||
if minimize {
|
|
||||||
if val >= 0.0 {
|
|
||||||
available_numbers[offset]
|
|
||||||
} else {
|
|
||||||
available_numbers[available_numbers.len() - 1 - offset]
|
|
||||||
}
|
|
||||||
} else if val <= 0.0 {
|
|
||||||
available_numbers[offset]
|
|
||||||
} else {
|
|
||||||
available_numbers[available_numbers.len() - 1 - offset]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
offset += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PlayerAction {
|
|
||||||
symb: symb.unwrap(),
|
|
||||||
pos,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Agent for Chase {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"Chase"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn player(&self) -> Player {
|
|
||||||
self.player
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MinimizerAgent for Chase {
|
|
||||||
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
||||||
self.step(board, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaximizerAgent for Chase {
|
|
||||||
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
||||||
self.step(board, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
|
||||||
|
|
||||||
use super::{Agent, Chase, MaximizerAgent, MinimizerAgent, Random};
|
|
||||||
use crate::{
|
|
||||||
board::{Board, PlayerAction},
|
|
||||||
util::{Player, Symb},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A simple "operator diffusion" MINIMIZER agent.
|
|
||||||
///
|
|
||||||
/// Tries to keep operators as far apart as possible, denying large numbers.
|
|
||||||
/// Places numbers using the same algorithm as chase.
|
|
||||||
pub struct Diffuse {
|
|
||||||
player: Player,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diffuse {
|
|
||||||
pub fn new(player: Player) -> Self {
|
|
||||||
Self { player }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Place a symbol on the board.
|
|
||||||
/// Assumes `symb` is not already on the board
|
|
||||||
fn step_symb(&self, board: &Board, symb: Symb) -> PlayerAction {
|
|
||||||
if board.contains(symb) {
|
|
||||||
panic!("Called `step_symb` with a symbol that's already on the board!")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill distance array with largest possible value
|
|
||||||
let mut dist = [board.size() + 1; 11];
|
|
||||||
|
|
||||||
// Set up initial distances
|
|
||||||
dist[0] = 1;
|
|
||||||
*dist.last_mut().unwrap() = 1;
|
|
||||||
for (i, o) in board.get_board().iter().enumerate() {
|
|
||||||
if let Some(s) = o {
|
|
||||||
if s.is_op() {
|
|
||||||
dist[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut did_something = true;
|
|
||||||
while did_something {
|
|
||||||
did_something = false;
|
|
||||||
for i in 1..(dist.len() - 1) {
|
|
||||||
let l = dist[i - 1];
|
|
||||||
let r = dist[i + 1];
|
|
||||||
|
|
||||||
let new = (l + 1).min(r + 1);
|
|
||||||
if new < dist[i] {
|
|
||||||
did_something = true;
|
|
||||||
dist[i] = new;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut max_dist = *dist.iter().max().unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
for (pos, d) in dist.iter().enumerate() {
|
|
||||||
if *d >= max_dist {
|
|
||||||
let action = PlayerAction { symb, pos };
|
|
||||||
if board.can_play(&action) {
|
|
||||||
return action;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if max_dist == 0 {
|
|
||||||
return Random::new(self.player).step_max(board).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
max_dist -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Agent for Diffuse {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"Diffuse"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn player(&self) -> Player {
|
|
||||||
self.player
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MinimizerAgent for Diffuse {
|
|
||||||
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
||||||
let mut x = [Symb::Minus, Symb::Times, Symb::Plus, Symb::Div];
|
|
||||||
x.shuffle(&mut thread_rng());
|
|
||||||
let symb = x.iter().find(|x| !board.contains(**x));
|
|
||||||
|
|
||||||
if let Some(symb) = symb {
|
|
||||||
Ok(self.step_symb(board, *symb))
|
|
||||||
} else {
|
|
||||||
// No symbols available, play a random number
|
|
||||||
Chase::new(self.player).step_min(board)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaximizerAgent for Diffuse {
|
|
||||||
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
||||||
let mut x = [Symb::Minus, Symb::Times, Symb::Plus, Symb::Div];
|
|
||||||
x.shuffle(&mut thread_rng());
|
|
||||||
let symb = x.iter().find(|x| !board.contains(**x));
|
|
||||||
|
|
||||||
if let Some(symb) = symb {
|
|
||||||
Ok(self.step_symb(board, *symb))
|
|
||||||
} else {
|
|
||||||
// No symbols available, play a random number
|
|
||||||
Chase::new(self.player).step_max(board)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -107,7 +107,7 @@ impl Human {
|
|||||||
// Cursor
|
// Cursor
|
||||||
" ".repeat(self.cursor),
|
" ".repeat(self.cursor),
|
||||||
color::Fg(self.player.color()),
|
color::Fg(self.player.color()),
|
||||||
if board.is_done() {
|
if board.is_full() {
|
||||||
' '
|
' '
|
||||||
} else {
|
} else {
|
||||||
self.symbol_selector.current()
|
self.symbol_selector.current()
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
mod brutus;
|
mod brutus;
|
||||||
mod chase;
|
|
||||||
mod diffuse;
|
|
||||||
mod human;
|
mod human;
|
||||||
mod random;
|
mod rhai;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub use brutus::Brutus;
|
pub use brutus::Brutus;
|
||||||
pub use chase::Chase;
|
|
||||||
pub use diffuse::Diffuse;
|
|
||||||
pub use human::Human;
|
pub use human::Human;
|
||||||
pub use random::Random;
|
pub use rhai::Rhai;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
board::{Board, PlayerAction},
|
board::{Board, PlayerAction},
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
board::{Board, PlayerAction},
|
|
||||||
util::{Player, Symb},
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use rand::Rng;
|
|
||||||
use std::num::NonZeroU8;
|
|
||||||
|
|
||||||
use super::{Agent, MaximizerAgent, MinimizerAgent};
|
|
||||||
|
|
||||||
pub struct Random {
|
|
||||||
player: Player,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Random {
|
|
||||||
pub fn new(player: Player) -> Self {
|
|
||||||
Self { player }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_action(&self, board: &Board) -> PlayerAction {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let n = board.size();
|
|
||||||
|
|
||||||
let pos = rng.gen_range(0..n);
|
|
||||||
let symb = match rng.gen_range(0..4) {
|
|
||||||
0 => {
|
|
||||||
let n = rng.gen_range(0..=9);
|
|
||||||
if n == 0 {
|
|
||||||
Symb::Zero
|
|
||||||
} else {
|
|
||||||
Symb::Number(NonZeroU8::new(n).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1 => Symb::Div,
|
|
||||||
2 => Symb::Minus,
|
|
||||||
3 => Symb::Plus,
|
|
||||||
4 => Symb::Times,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
PlayerAction { symb, pos }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Agent for Random {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"Random"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn player(&self) -> Player {
|
|
||||||
self.player
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MinimizerAgent for Random {
|
|
||||||
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
||||||
let mut action = self.random_action(board);
|
|
||||||
while !board.can_play(&action) {
|
|
||||||
action = self.random_action(board);
|
|
||||||
}
|
|
||||||
Ok(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MaximizerAgent for Random {
|
|
||||||
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
|
||||||
let mut action = self.random_action(board);
|
|
||||||
while !board.can_play(&action) {
|
|
||||||
action = self.random_action(board);
|
|
||||||
}
|
|
||||||
Ok(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
176
src/agents/rhai.rs
Normal file
176
src/agents/rhai.rs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use itertools::{Itertools, Permutations};
|
||||||
|
use rand::{seq::SliceRandom, Rng};
|
||||||
|
use rhai::{
|
||||||
|
packages::{
|
||||||
|
ArithmeticPackage, BasicArrayPackage, BasicFnPackage, BasicIteratorPackage,
|
||||||
|
BasicMathPackage, BasicStringPackage, LanguageCorePackage, LogicPackage, MoreStringPackage,
|
||||||
|
Package,
|
||||||
|
},
|
||||||
|
CustomType, Dynamic, Engine, EvalAltResult, Position, Scope, TypeBuilder, AST,
|
||||||
|
};
|
||||||
|
use std::{sync::Arc, vec::IntoIter};
|
||||||
|
|
||||||
|
use super::{Agent, MaximizerAgent, MinimizerAgent};
|
||||||
|
use crate::{
|
||||||
|
board::{Board, PlayerAction},
|
||||||
|
util::{Player, Symb},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RhaiPer<T: Clone, I: Iterator<Item = T>> {
|
||||||
|
inner: Arc<Permutations<I>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, I: Clone + Iterator<Item = T>> IntoIterator for RhaiPer<T, I> {
|
||||||
|
type Item = Vec<T>;
|
||||||
|
type IntoIter = Permutations<I>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
(*self.inner).clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, I: Iterator<Item = T>> Clone for RhaiPer<T, I> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone + Send + Sync + 'static, I: Clone + Iterator<Item = T> + Sync + Send + 'static>
|
||||||
|
CustomType for RhaiPer<T, I>
|
||||||
|
{
|
||||||
|
fn build(mut builder: TypeBuilder<Self>) {
|
||||||
|
builder
|
||||||
|
.with_name("Perutations")
|
||||||
|
.is_iterable()
|
||||||
|
.with_fn("to_string", |_s: &mut Self| "Permutation {}".to_owned())
|
||||||
|
.with_fn("to_debug", |_s: &mut Self| "Permutation {}".to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
pub struct Rhai {
|
||||||
|
player: Player,
|
||||||
|
engine: Engine,
|
||||||
|
script: AST,
|
||||||
|
scope: Scope<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rhai {
|
||||||
|
pub fn new(player: Player, script: &str) -> Self {
|
||||||
|
let engine = {
|
||||||
|
let mut engine = Engine::new_raw();
|
||||||
|
|
||||||
|
engine.set_max_expr_depths(100, 100);
|
||||||
|
engine.set_max_strings_interned(1024);
|
||||||
|
engine.on_print(|text| println!("{text}"));
|
||||||
|
engine.on_debug(|text, source, pos| match (source, pos) {
|
||||||
|
(Some(source), Position::NONE) => println!("{source} | {text}"),
|
||||||
|
(Some(source), pos) => println!("{source} @ {pos:?} | {text}"),
|
||||||
|
(None, Position::NONE) => println!("{text}"),
|
||||||
|
(None, pos) => println!("{pos:?} | {text}"),
|
||||||
|
});
|
||||||
|
|
||||||
|
LanguageCorePackage::new().register_into_engine(&mut engine);
|
||||||
|
ArithmeticPackage::new().register_into_engine(&mut engine);
|
||||||
|
BasicIteratorPackage::new().register_into_engine(&mut engine);
|
||||||
|
LogicPackage::new().register_into_engine(&mut engine);
|
||||||
|
BasicStringPackage::new().register_into_engine(&mut engine);
|
||||||
|
MoreStringPackage::new().register_into_engine(&mut engine);
|
||||||
|
BasicMathPackage::new().register_into_engine(&mut engine);
|
||||||
|
BasicArrayPackage::new().register_into_engine(&mut engine);
|
||||||
|
BasicFnPackage::new().register_into_engine(&mut engine);
|
||||||
|
|
||||||
|
engine
|
||||||
|
.register_fn("rand_int", |from: i64, to: i64| {
|
||||||
|
rand::thread_rng().gen_range(from..=to)
|
||||||
|
})
|
||||||
|
.register_fn("rand_bool", |p: f32| rand::thread_rng().gen_bool(p as f64))
|
||||||
|
.register_fn("rand_symb", || {
|
||||||
|
Symb::new_random(&mut rand::thread_rng()).to_string()
|
||||||
|
})
|
||||||
|
.register_fn("rand_action", |board: Board| {
|
||||||
|
PlayerAction::new_random(&mut rand::thread_rng(), &board)
|
||||||
|
})
|
||||||
|
.register_fn("rand_shuffle", |mut vec: Vec<Dynamic>| {
|
||||||
|
vec.shuffle(&mut rand::thread_rng());
|
||||||
|
vec
|
||||||
|
})
|
||||||
|
.register_fn("is_op", |s: &str| {
|
||||||
|
Symb::from_str(s).map(|x| x.is_op()).unwrap_or(false)
|
||||||
|
})
|
||||||
|
.register_fn(
|
||||||
|
"permutations",
|
||||||
|
|v: Vec<Dynamic>, size: i64| -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let size: usize = match size.try_into() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(format!("Invalid permutation size {size}").into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let per = RhaiPer {
|
||||||
|
inner: v.into_iter().permutations(size).into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Dynamic::from(per))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
engine
|
||||||
|
.build_type::<Board>()
|
||||||
|
.build_type::<PlayerAction>()
|
||||||
|
.build_type::<RhaiPer<Dynamic, IntoIter<Dynamic>>>();
|
||||||
|
engine
|
||||||
|
};
|
||||||
|
|
||||||
|
let script = engine.compile(script).unwrap();
|
||||||
|
let scope = Scope::new();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
player,
|
||||||
|
engine,
|
||||||
|
script,
|
||||||
|
scope,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Agent for Rhai {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Rhai"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player(&self) -> Player {
|
||||||
|
self.player
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MinimizerAgent for Rhai {
|
||||||
|
fn step_min(&mut self, board: &Board) -> Result<PlayerAction> {
|
||||||
|
let result = self
|
||||||
|
.engine
|
||||||
|
.call_fn::<PlayerAction>(&mut self.scope, &self.script, "step_min", (board.clone(),))
|
||||||
|
.map_err(|x| anyhow!(x.to_string()))
|
||||||
|
.context("while running rhai step_min")?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaximizerAgent for Rhai {
|
||||||
|
fn step_max(&mut self, board: &Board) -> Result<PlayerAction> {
|
||||||
|
let result = self
|
||||||
|
.engine
|
||||||
|
.call_fn::<PlayerAction>(&mut self.scope, &self.script, "step_max", (board.clone(),))
|
||||||
|
.map_err(|x| anyhow!(x.to_string()))
|
||||||
|
.context("while running rhai step_min")?;
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ use crate::{board::Board, util::Symb};
|
|||||||
/// - coords are the coordinate of this slot's partial
|
/// - coords are the coordinate of this slot's partial
|
||||||
/// - char_idx is the index of this slot in its partial
|
/// - char_idx is the index of this slot in its partial
|
||||||
/// - f32 is the influence of this slot
|
/// - f32 is the influence of this slot
|
||||||
pub fn free_slots_by_influence(board: &Board) -> Option<Vec<(usize, f32)>> {
|
fn free_slots_by_influence(board: &Board) -> Option<Vec<(usize, f32)>> {
|
||||||
// Fill all empty slots with fives and compute starting value
|
// Fill all empty slots with fives and compute starting value
|
||||||
let filled = {
|
let filled = {
|
||||||
// This should always result in an evaluatable expression,
|
// This should always result in an evaluatable expression,
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use rhai::Array;
|
||||||
|
use rhai::CustomType;
|
||||||
|
use rhai::Dynamic;
|
||||||
|
use rhai::EvalAltResult;
|
||||||
|
use rhai::Position;
|
||||||
|
use rhai::TypeBuilder;
|
||||||
use std::fmt::{Debug, Display, Write};
|
use std::fmt::{Debug, Display, Write};
|
||||||
use termion::color::{self, Color};
|
use termion::color::{self, Color};
|
||||||
|
|
||||||
@@ -117,7 +123,7 @@ impl Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_done(&self) -> bool {
|
pub fn is_full(&self) -> bool {
|
||||||
self.free_spots == 0
|
self.free_spots == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,3 +423,167 @@ impl Board {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Board {
|
||||||
|
type Item = String;
|
||||||
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.board
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.map(|x| x.to_string()).unwrap_or_default())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomType for Board {
|
||||||
|
fn build(mut builder: TypeBuilder<Self>) {
|
||||||
|
builder
|
||||||
|
.with_name("Board")
|
||||||
|
.is_iterable()
|
||||||
|
.with_fn("to_string", |s: &mut Self| format!("{}", s))
|
||||||
|
.with_fn("to_debug", |s: &mut Self| format!("{:?}", s))
|
||||||
|
.with_fn("size", |s: &mut Self| s.board.len() as i64)
|
||||||
|
.with_fn("len", |s: &mut Self| s.board.len() as i64)
|
||||||
|
.with_fn("is_full", |s: &mut Self| s.is_full())
|
||||||
|
.with_fn("free_spots", |s: &mut Self| s.free_spots)
|
||||||
|
.with_fn("play", |s: &mut Self, act: PlayerAction| {
|
||||||
|
s.play(act, Player::Red) // Player doesn't matter
|
||||||
|
})
|
||||||
|
.with_fn("ith_free_slot", |s: &mut Self, idx: usize| {
|
||||||
|
s.ith_empty_slot(idx).map(|x| x as i64).unwrap_or(-1)
|
||||||
|
})
|
||||||
|
.with_fn("can_play", |s: &mut Self, act: PlayerAction| {
|
||||||
|
s.can_play(&act)
|
||||||
|
})
|
||||||
|
.with_fn("contains", |s: &mut Self, sym: &str| {
|
||||||
|
match Symb::from_str(sym) {
|
||||||
|
None => false,
|
||||||
|
Some(x) => s.contains(x),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.with_fn("contains", |s: &mut Self, sym: i64| {
|
||||||
|
let sym = sym.to_string();
|
||||||
|
match Symb::from_str(&sym) {
|
||||||
|
None => false,
|
||||||
|
Some(x) => s.contains(x),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.with_fn("evaluate", |s: &mut Self| -> Dynamic {
|
||||||
|
s.evaluate().map(|x| x.into()).unwrap_or(().into())
|
||||||
|
})
|
||||||
|
.with_fn("free_spots_idx", |s: &mut Self| -> Array {
|
||||||
|
s.board
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, x)| x.is_none())
|
||||||
|
.map(|(i, _)| i as i64)
|
||||||
|
.map(|x| x.into())
|
||||||
|
.collect::<Vec<Dynamic>>()
|
||||||
|
})
|
||||||
|
.with_indexer_get(
|
||||||
|
|s: &mut Self, idx: i64| -> Result<String, Box<EvalAltResult>> {
|
||||||
|
if idx as usize >= s.board.len() {
|
||||||
|
return Err(
|
||||||
|
EvalAltResult::ErrorIndexNotFound(idx.into(), Position::NONE).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = idx as usize;
|
||||||
|
return Ok(s.board[idx].map(|x| x.to_string()).unwrap_or_default());
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_indexer_set(
|
||||||
|
|s: &mut Self, idx: i64, val: String| -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let idx: usize = match idx.try_into() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(EvalAltResult::ErrorIndexNotFound(
|
||||||
|
idx.into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if idx >= s.board.len() {
|
||||||
|
return Err(EvalAltResult::ErrorIndexNotFound(
|
||||||
|
(idx as i64).into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
match Symb::from_str(&val) {
|
||||||
|
None => return Err(format!("Invalid symbol {val}").into()),
|
||||||
|
Some(x) => {
|
||||||
|
s.board[idx] = Some(x);
|
||||||
|
s.placed_by[idx] = Some(Player::Red); // Arbitrary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_indexer_set(
|
||||||
|
|s: &mut Self, idx: i64, _val: ()| -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let idx: usize = match idx.try_into() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(EvalAltResult::ErrorIndexNotFound(
|
||||||
|
idx.into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if idx >= s.board.len() {
|
||||||
|
return Err(EvalAltResult::ErrorIndexNotFound(
|
||||||
|
(idx as i64).into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
s.board[idx] = None;
|
||||||
|
s.placed_by[idx] = None;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_indexer_set(
|
||||||
|
|s: &mut Self, idx: i64, val: i64| -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let idx: usize = match idx.try_into() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(EvalAltResult::ErrorIndexNotFound(
|
||||||
|
idx.into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if idx >= s.board.len() {
|
||||||
|
return Err(EvalAltResult::ErrorIndexNotFound(
|
||||||
|
(idx as i64).into(),
|
||||||
|
Position::NONE,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
match Symb::from_str(&val.to_string()) {
|
||||||
|
None => return Err(format!("Invalid symbol {val}").into()),
|
||||||
|
Some(x) => {
|
||||||
|
s.board[idx] = Some(x);
|
||||||
|
s.placed_by[idx] = Some(Player::Red); // Arbitrary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
mod board;
|
mod board;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
use rhai::{CustomType, EvalAltResult, TypeBuilder};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub use board::Board;
|
pub use board::Board;
|
||||||
@@ -20,3 +22,56 @@ impl Display for PlayerAction {
|
|||||||
write!(f, "{} at {}", self.symb, self.pos)
|
write!(f, "{} at {}", self.symb, self.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PlayerAction {
|
||||||
|
pub fn new_random<R: Rng>(rng: &mut R, board: &Board) -> Self {
|
||||||
|
let n = board.size();
|
||||||
|
let pos = rng.gen_range(0..n);
|
||||||
|
let symb = Symb::new_random(rng);
|
||||||
|
PlayerAction { symb, pos }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomType for PlayerAction {
|
||||||
|
fn build(mut builder: TypeBuilder<Self>) {
|
||||||
|
builder
|
||||||
|
.with_name("Action")
|
||||||
|
.with_fn(
|
||||||
|
"Action",
|
||||||
|
|symb: &str, pos: i64| -> Result<Self, Box<EvalAltResult>> {
|
||||||
|
let symb = match Symb::from_str(symb) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return Err(format!("Invalid symbol {symb:?}").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
symb,
|
||||||
|
pos: pos as usize,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_fn(
|
||||||
|
"Action",
|
||||||
|
|symb: i64, pos: i64| -> Result<Self, Box<EvalAltResult>> {
|
||||||
|
let symb = symb.to_string();
|
||||||
|
let symb = match Symb::from_str(&symb) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return Err(format!("Invalid symbol {symb:?}").into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
symb,
|
||||||
|
pos: pos as usize,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_fn("to_string", |s: &mut Self| -> String {
|
||||||
|
format!("Action {{{} at {}}}", s.symb, s.pos)
|
||||||
|
})
|
||||||
|
.with_fn("to_debug", |s: &mut Self| -> String {
|
||||||
|
format!("Action {{{} at {}}}", s.symb, s.pos)
|
||||||
|
})
|
||||||
|
.with_get("symb", |s: &mut Self| s.symb.to_string())
|
||||||
|
.with_get("pos", |s: &mut Self| s.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
17
src/cli.rs
17
src/cli.rs
@@ -1,9 +1,8 @@
|
|||||||
|
use clap::{Parser, ValueEnum};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use clap::{Parser, ValueEnum};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
agents::{Brutus, Chase, Diffuse, Human, MaximizerAgent, MinimizerAgent, Random},
|
agents::{Brutus, Human, MaximizerAgent, MinimizerAgent, Rhai},
|
||||||
util::Player,
|
util::Player,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,9 +42,9 @@ pub enum AgentSelector {
|
|||||||
impl AgentSelector {
|
impl AgentSelector {
|
||||||
pub fn get_maximizer(&self, player: Player) -> Box<dyn MaximizerAgent> {
|
pub fn get_maximizer(&self, player: Player) -> Box<dyn MaximizerAgent> {
|
||||||
match self {
|
match self {
|
||||||
Self::Random => Box::new(Random::new(player)),
|
Self::Random => Box::new(Rhai::new(player, include_str!("agents.rhai/random.rhai"))),
|
||||||
Self::Chase => Box::new(Chase::new(player)),
|
Self::Diffuse => Box::new(Rhai::new(player, include_str!("agents.rhai/diffuse.rhai"))),
|
||||||
Self::Diffuse => Box::new(Diffuse::new(player)),
|
Self::Chase => Box::new(Rhai::new(player, include_str!("agents.rhai/chase.rhai"))),
|
||||||
Self::Brutus => Box::new(Brutus::new(player)),
|
Self::Brutus => Box::new(Brutus::new(player)),
|
||||||
Self::Human => Box::new(Human::new(player)),
|
Self::Human => Box::new(Human::new(player)),
|
||||||
}
|
}
|
||||||
@@ -53,9 +52,9 @@ impl AgentSelector {
|
|||||||
|
|
||||||
pub fn get_minimizer(&self, player: Player) -> Box<dyn MinimizerAgent> {
|
pub fn get_minimizer(&self, player: Player) -> Box<dyn MinimizerAgent> {
|
||||||
match self {
|
match self {
|
||||||
Self::Random => Box::new(Random::new(player)),
|
Self::Random => Box::new(Rhai::new(player, include_str!("agents.rhai/random.rhai"))),
|
||||||
Self::Chase => Box::new(Chase::new(player)),
|
Self::Diffuse => Box::new(Rhai::new(player, include_str!("agents.rhai/diffuse.rhai"))),
|
||||||
Self::Diffuse => Box::new(Diffuse::new(player)),
|
Self::Chase => Box::new(Rhai::new(player, include_str!("agents.rhai/chase.rhai"))),
|
||||||
Self::Brutus => Box::new(Brutus::new(player)),
|
Self::Brutus => Box::new(Brutus::new(player)),
|
||||||
Self::Human => Box::new(Human::new(player)),
|
Self::Human => Box::new(Human::new(player)),
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main.rs
14
src/main.rs
@@ -22,7 +22,7 @@ fn play_silent(
|
|||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
let mut is_maxi_turn = true;
|
let mut is_maxi_turn = true;
|
||||||
|
|
||||||
while !board.is_done() {
|
while !board.is_full() {
|
||||||
// Take action
|
// Take action
|
||||||
let action = if is_maxi_turn {
|
let action = if is_maxi_turn {
|
||||||
maxi.step_max(&board)?
|
maxi.step_max(&board)?
|
||||||
@@ -70,7 +70,7 @@ fn play(
|
|||||||
color::Fg(color::Reset)
|
color::Fg(color::Reset)
|
||||||
);
|
);
|
||||||
|
|
||||||
while !board.is_done() {
|
while !board.is_full() {
|
||||||
// Print board
|
// Print board
|
||||||
println!(
|
println!(
|
||||||
"\r{}{}{}{}",
|
"\r{}{}{}{}",
|
||||||
@@ -136,6 +136,9 @@ fn main() -> Result<()> {
|
|||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {e}");
|
println!("Error: {e}");
|
||||||
|
for e in e.chain() {
|
||||||
|
println!("{e}");
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -146,6 +149,9 @@ fn main() -> Result<()> {
|
|||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error: {e}");
|
println!("Error: {e}");
|
||||||
|
for e in e.chain() {
|
||||||
|
println!("{e}");
|
||||||
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -181,7 +187,7 @@ fn main() -> Result<()> {
|
|||||||
let mut mini = cli.blue.get_minimizer(Player::Blue);
|
let mut mini = cli.blue.get_minimizer(Player::Blue);
|
||||||
let red_board = play(&mut *maxi, &mut *mini)?;
|
let red_board = play(&mut *maxi, &mut *mini)?;
|
||||||
|
|
||||||
if red_board.is_done() {
|
if red_board.is_full() {
|
||||||
println!(
|
println!(
|
||||||
"\r\n{}{} score:{} {:.2}\n\n",
|
"\r\n{}{} score:{} {:.2}\n\n",
|
||||||
color::Fg(maxi.player().color()),
|
color::Fg(maxi.player().color()),
|
||||||
@@ -202,7 +208,7 @@ fn main() -> Result<()> {
|
|||||||
let mut mini = cli.red.get_minimizer(Player::Red);
|
let mut mini = cli.red.get_minimizer(Player::Red);
|
||||||
let blue_board = play(&mut *maxi, &mut *mini)?;
|
let blue_board = play(&mut *maxi, &mut *mini)?;
|
||||||
|
|
||||||
if blue_board.is_done() {
|
if blue_board.is_full() {
|
||||||
println!(
|
println!(
|
||||||
"\r\n{}{} score:{} {:.2}\n\n",
|
"\r\n{}{} score:{} {:.2}\n\n",
|
||||||
color::Fg(maxi.player().color()),
|
color::Fg(maxi.player().color()),
|
||||||
|
|||||||
28
src/util.rs
28
src/util.rs
@@ -3,6 +3,7 @@ use std::{
|
|||||||
num::NonZeroU8,
|
num::NonZeroU8,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
use termion::color::{self, Color};
|
use termion::color::{self, Color};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
@@ -31,6 +32,7 @@ impl Display for Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
|
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
|
|
||||||
pub enum Symb {
|
pub enum Symb {
|
||||||
Number(NonZeroU8),
|
Number(NonZeroU8),
|
||||||
Zero,
|
Zero,
|
||||||
@@ -69,6 +71,24 @@ impl Symb {
|
|||||||
self == &Self::Minus
|
self == &Self::Minus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_random<R: Rng>(rng: &mut R) -> Self {
|
||||||
|
match rng.gen_range(0..4) {
|
||||||
|
0 => {
|
||||||
|
let n = rng.gen_range(0..=9);
|
||||||
|
if n == 0 {
|
||||||
|
Symb::Zero
|
||||||
|
} else {
|
||||||
|
Symb::Number(NonZeroU8::new(n).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => Symb::Div,
|
||||||
|
2 => Symb::Minus,
|
||||||
|
3 => Symb::Plus,
|
||||||
|
4 => Symb::Times,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn get_char(&self) -> Option<char> {
|
pub const fn get_char(&self) -> Option<char> {
|
||||||
match self {
|
match self {
|
||||||
Self::Plus => Some('+'),
|
Self::Plus => Some('+'),
|
||||||
@@ -91,6 +111,14 @@ impl Symb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_str(s: &str) -> Option<Self> {
|
||||||
|
if s.chars().count() != 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::from_char(s.chars().next()?)
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn from_char(c: char) -> Option<Self> {
|
pub const fn from_char(c: char) -> Option<Self> {
|
||||||
match c {
|
match c {
|
||||||
'1' => Some(Self::Number(unsafe { NonZeroU8::new_unchecked(1) })),
|
'1' => Some(Self::Number(unsafe { NonZeroU8::new_unchecked(1) })),
|
||||||
|
|||||||
Reference in New Issue
Block a user