initial commit
This commit is contained in:
commit
f34c06758f
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,385 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bano"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"rand",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.158"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
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 = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "bano"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.19", features = ["derive"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
rayon = "1.10.0"
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2024 mos
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,32 @@
|
||||||
|
# bano
|
||||||
|
|
||||||
|
Sokoban solver using genetic algorithms.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
bano is a sokoban solver that uses a genetic algorithm to calculate the most
|
||||||
|
optimal solution for a given map. The heuristic approach covered by the tool
|
||||||
|
is twofold and can be described as follows:
|
||||||
|
|
||||||
|
1. Place the boxes in the given goals in as fewer steps as possible. While
|
||||||
|
this is not guaranteed, it can be improved either through subsequent iterations
|
||||||
|
or decreasing the search space (controllable via -d/--ds argument).
|
||||||
|
2. If any box is placed in such a way that the agent can no longer move it, this
|
||||||
|
is considered a losing condition and it is no longer evaluated. The evaluation
|
||||||
|
is scored by calculating the individual box distances and the highest scoring
|
||||||
|
agents end up as a parent to newly bred agents.
|
||||||
|
|
||||||
|
The approach described above is not computationally efficient compared to others and
|
||||||
|
has a tendency to "halt" with more complex problem spaces, therefore it is more suitable
|
||||||
|
for smaller levels. To avoid early convergence one can tune the parameters according to the
|
||||||
|
complexity of the layout.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
`bano --nt 4 -d 4096 -m 256 --ps 8192 < map`
|
||||||
|
|
||||||
|
`bano '8X|4.1@*gX|8X'`
|
||||||
|
|
||||||
|
## Maps
|
||||||
|
|
||||||
|
Some of the maps in the map directory were manually altered and are taken from [here](https://github.com/begoon/sokoban-maps/blob/master/maps/sokoban-maps-60-plain.txt).
|
|
@ -0,0 +1,11 @@
|
||||||
|
....XXXXX.............
|
||||||
|
....X...X.............
|
||||||
|
....X*..X.............
|
||||||
|
..XXX..*XXX...........
|
||||||
|
..X..*..*.X...........
|
||||||
|
XXX.X.XXX.X.....XXXXXX
|
||||||
|
X...X.XXX.XXXXXXX..ggX
|
||||||
|
X.*..*.............ggX
|
||||||
|
XXXXX.XXXX.X@XXXX..ggX
|
||||||
|
....X......XXX..XXXXXX
|
||||||
|
....XXXXXXXX..........
|
|
@ -0,0 +1,12 @@
|
||||||
|
XXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
|
X............XXXXXXXXXXX
|
||||||
|
X..*.......@.XXXXXXXXXXX
|
||||||
|
XXXXXXXXX..XXXXXXXXXXXXX
|
||||||
|
XXXXXXXXX...*.........gX
|
||||||
|
XXXXXXXXX......XXXXXXXXX
|
||||||
|
XXXXXXXXX...XXXXXXXXXXXX
|
||||||
|
XXXXXXXXX............g.X
|
||||||
|
XXXXXXXXX.....*........X
|
||||||
|
XXXXXXXXX........g.....X
|
||||||
|
XXXXXXXXX.......*....g.X
|
||||||
|
XXXXXXXXXXXXXXXXXXXXXXXX
|
|
@ -0,0 +1,11 @@
|
||||||
|
....XXXXX.............
|
||||||
|
....X...X.............
|
||||||
|
....X*..X.............
|
||||||
|
..XXX..*XXX...........
|
||||||
|
..X..*..*.X...........
|
||||||
|
XXX.......X.....XXXXXX
|
||||||
|
X.........XXXXXXX...gX
|
||||||
|
X.*.g*...............X
|
||||||
|
XXXXX.XXXX.X@XXXX....X
|
||||||
|
....X......XXX..XXXXXX
|
||||||
|
....XXXXXXXX..........
|
|
@ -0,0 +1,11 @@
|
||||||
|
......XXXXX
|
||||||
|
XXXXXXX.@XX
|
||||||
|
X.....*..XX
|
||||||
|
X...*XX.*XX
|
||||||
|
XX*XgggX.XX
|
||||||
|
.X.*ggg..XX
|
||||||
|
.X.Xg.gX.XX
|
||||||
|
.X...X.X*.X
|
||||||
|
.X*..*....X
|
||||||
|
.X..XXXXXXX
|
||||||
|
.XXXXXXXXXX
|
|
@ -0,0 +1,11 @@
|
||||||
|
XXXXXXXXXXXX
|
||||||
|
X.g....XX@XX
|
||||||
|
X..*.......X
|
||||||
|
Xgg...*..*.X
|
||||||
|
Xg.......*.X
|
||||||
|
Xg....*....X
|
||||||
|
XXXX...X.*.X
|
||||||
|
...X.*.X...X
|
||||||
|
...X.......X
|
||||||
|
...X.gXX.g.X
|
||||||
|
...XXXXXXXXX
|
|
@ -0,0 +1,7 @@
|
||||||
|
XXXXXXX
|
||||||
|
Xg@.X.X
|
||||||
|
X.*...X
|
||||||
|
X..*..X
|
||||||
|
X.g*..X
|
||||||
|
X.....X
|
||||||
|
XXXXXXX
|
|
@ -0,0 +1,564 @@
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::ops::{Add, Index, IndexMut, Neg};
|
||||||
|
|
||||||
|
pub enum ReadError {
|
||||||
|
BadSymbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
Read(ReadError),
|
||||||
|
BadParam(String),
|
||||||
|
BadMove(Pos),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
enum Direction {
|
||||||
|
U,
|
||||||
|
D,
|
||||||
|
L,
|
||||||
|
R,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
enum Cell {
|
||||||
|
Wall,
|
||||||
|
Box,
|
||||||
|
Goal(bool),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
enum Solution {
|
||||||
|
Ok,
|
||||||
|
Win,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Default, Clone, Copy)]
|
||||||
|
pub struct Pos(isize, isize);
|
||||||
|
|
||||||
|
impl Add for Pos {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self(self.0 + other.0, self.1 + other.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Pos {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn neg(self) -> Self {
|
||||||
|
Self(-self.0, -self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Pos {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{} {}", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ReadError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ReadError::BadSymbol => write!(f, "bad symbol"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Read(e) => write!(f, "{e}"),
|
||||||
|
Error::BadParam(s) => write!(f, "{s}"),
|
||||||
|
Error::BadMove(pos) => write!(f, "invalid move ({pos})"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
struct Level {
|
||||||
|
size: Pos,
|
||||||
|
player: Pos,
|
||||||
|
map: Vec<Cell>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<Pos> for Level {
|
||||||
|
type Output = Cell;
|
||||||
|
|
||||||
|
fn index(&self, pos: Pos) -> &Self::Output {
|
||||||
|
&self.map[((pos.1 * self.size.0) + pos.0) as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<Pos> for Level {
|
||||||
|
fn index_mut(&mut self, pos: Pos) -> &mut Self::Output {
|
||||||
|
&mut self.map[((pos.1 * self.size.0) + pos.0) as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Level {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for i in 0..self.size.1 {
|
||||||
|
for j in 0..self.size.0 {
|
||||||
|
if Pos(j, i) == self.player {
|
||||||
|
write!(f, "@")?;
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self[Pos(j, i)] {
|
||||||
|
Cell::Wall => 'X',
|
||||||
|
Cell::Box => '*',
|
||||||
|
Cell::Goal(true) => '!',
|
||||||
|
Cell::Goal(false) => '.',
|
||||||
|
Cell::None => ' ',
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Level {
|
||||||
|
fn read<R: std::io::BufRead>(file: R) -> Result<Self, ReadError> {
|
||||||
|
let (mut player, mut size) = (Pos::default(), Pos::default());
|
||||||
|
|
||||||
|
let map = file
|
||||||
|
.lines()
|
||||||
|
.flat_map(|l| {
|
||||||
|
let l = l.unwrap();
|
||||||
|
|
||||||
|
if l.len() > size.0 as usize {
|
||||||
|
size.0 = l.len() as isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
size.1 += 1;
|
||||||
|
l.char_indices()
|
||||||
|
.map(|(i, c)| match c {
|
||||||
|
'@' => {
|
||||||
|
player.0 = i as isize;
|
||||||
|
player.1 = size.1 - 1;
|
||||||
|
Ok(Cell::None)
|
||||||
|
}
|
||||||
|
'X' => Ok(Cell::Wall),
|
||||||
|
'*' => Ok(Cell::Box),
|
||||||
|
'g' => Ok(Cell::Goal(false)),
|
||||||
|
'.' => Ok(Cell::None),
|
||||||
|
_ => Err(ReadError::BadSymbol),
|
||||||
|
})
|
||||||
|
.collect::<Vec<Result<_, _>>>()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(Self { size, player, map })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_rle(map: String) -> Result<Self, ReadError> {
|
||||||
|
let mut n: usize = 1;
|
||||||
|
|
||||||
|
Self::read(
|
||||||
|
map.chars()
|
||||||
|
.filter_map(|c| match c {
|
||||||
|
'0'..='9' => {
|
||||||
|
n = c.to_digit(10).unwrap() as usize;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
'|' => Some("\n".into()),
|
||||||
|
_ => Some(c.to_string().repeat(n)),
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("")
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
struct State {
|
||||||
|
lvl: Level,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn new(lvl: Level) -> Self {
|
||||||
|
State { lvl }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_box(&mut self, pos: Pos) {
|
||||||
|
self.lvl[pos] = if self.lvl[pos] == Cell::Goal(true) {
|
||||||
|
Cell::Goal(false)
|
||||||
|
} else {
|
||||||
|
Cell::None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_pos(&mut self, pos: Pos) -> Result<(bool, bool), Error> {
|
||||||
|
let dpos = self.lvl.player + pos;
|
||||||
|
|
||||||
|
if ((dpos.1 * self.lvl.size.0) + dpos.0) as usize >= self.lvl.map.len() {
|
||||||
|
return Err(Error::BadMove(dpos));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.lvl[dpos] {
|
||||||
|
Cell::None | Cell::Goal(false) => self.lvl.player = dpos,
|
||||||
|
Cell::Box | Cell::Goal(true) => {
|
||||||
|
let bpos = dpos + pos;
|
||||||
|
|
||||||
|
match self.lvl[bpos] {
|
||||||
|
Cell::None => {
|
||||||
|
self.toggle_box(dpos);
|
||||||
|
self.lvl[bpos] = Cell::Box;
|
||||||
|
self.lvl.player = dpos;
|
||||||
|
return Ok((false, false));
|
||||||
|
}
|
||||||
|
Cell::Goal(false) => {
|
||||||
|
self.toggle_box(dpos);
|
||||||
|
self.lvl[bpos] = Cell::Goal(true);
|
||||||
|
self.lvl.player = dpos;
|
||||||
|
return Ok((true, false));
|
||||||
|
}
|
||||||
|
_ => return Ok((false, true)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Ok((false, true)),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((false, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_dir(&mut self, dir: Direction) -> Result<(bool, bool), Error> {
|
||||||
|
match dir {
|
||||||
|
Direction::U => self.move_pos(Pos(0, -1)),
|
||||||
|
Direction::D => self.move_pos(Pos(0, 1)),
|
||||||
|
Direction::L => self.move_pos(Pos(-1, 0)),
|
||||||
|
Direction::R => self.move_pos(Pos(1, 0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_box(&self, pos: Pos, bt: bool) -> bool {
|
||||||
|
let dir: &[Pos] = &[Pos(0, -1), Pos(0, 1), Pos(-1, 0), Pos(1, 0)];
|
||||||
|
let mut mbc = 0;
|
||||||
|
|
||||||
|
for i in dir {
|
||||||
|
match self.lvl[pos + *i] {
|
||||||
|
Cell::Goal(false) | Cell::None => {
|
||||||
|
let dpos = pos + -*i;
|
||||||
|
|
||||||
|
match self.lvl[dpos] {
|
||||||
|
Cell::Goal(false) | Cell::None => mbc += 1,
|
||||||
|
Cell::Box => {
|
||||||
|
if bt {
|
||||||
|
mbc += self.test_box(dpos, false) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mbc != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dist_box(&self, pos: Pos) -> usize {
|
||||||
|
let mut d = 0;
|
||||||
|
|
||||||
|
for i in 0..self.lvl.size.1 {
|
||||||
|
for j in 0..self.lvl.size.0 {
|
||||||
|
if self.lvl[Pos(j, i)] == Cell::Goal(false) {
|
||||||
|
d += ((j as i32 - pos.0 as i32).abs() + (i as i32 - pos.1 as i32).abs())
|
||||||
|
as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dist_boxes(&self) -> usize {
|
||||||
|
let (mut max, mut rb) = (0, 0);
|
||||||
|
|
||||||
|
for i in 0..self.lvl.size.1 {
|
||||||
|
for j in 0..self.lvl.size.0 {
|
||||||
|
if self.lvl[Pos(j, i)] == Cell::Box {
|
||||||
|
max += self.dist_box(Pos(j, i));
|
||||||
|
rb += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
max * rb
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(&self) -> Solution {
|
||||||
|
let mut cond = Solution::Win;
|
||||||
|
|
||||||
|
for i in 0..self.lvl.size.1 {
|
||||||
|
for j in 0..self.lvl.size.0 {
|
||||||
|
match self.lvl[Pos(j, i)] {
|
||||||
|
Cell::Goal(false) => cond = Solution::Ok,
|
||||||
|
Cell::Box => {
|
||||||
|
if !self.test_box(Pos(j, i), true) {
|
||||||
|
return Solution::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Agent {
|
||||||
|
state: State,
|
||||||
|
dna: Vec<Direction>,
|
||||||
|
score: f32,
|
||||||
|
dir: usize,
|
||||||
|
goal: Solution,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Agent {
|
||||||
|
const DIR_TABLE: &'static [Direction] =
|
||||||
|
&[Direction::U, Direction::D, Direction::L, Direction::R];
|
||||||
|
|
||||||
|
fn chromo() -> Direction {
|
||||||
|
Self::DIR_TABLE[rand::random::<usize>() % Self::DIR_TABLE.len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_dna(state: State, dna: Vec<Direction>) -> Self {
|
||||||
|
Self {
|
||||||
|
state,
|
||||||
|
dna,
|
||||||
|
score: 0.0,
|
||||||
|
dir: 0,
|
||||||
|
goal: Solution::Ok,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(state: State, size: usize) -> Self {
|
||||||
|
let dna: Vec<Direction> = (0..size).map(|_| Self::chromo()).collect();
|
||||||
|
|
||||||
|
Self::with_dna(state, dna)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cross(&mut self, seed: &Self, cr: usize) {
|
||||||
|
let ratio = (rand::random::<usize>() % cr) + 1;
|
||||||
|
let len = self.dna.len();
|
||||||
|
|
||||||
|
self.dna[..len / ratio].copy_from_slice(&seed.dna[..len / ratio]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mutate(&mut self, max: usize) {
|
||||||
|
let len = self.dna.len();
|
||||||
|
let ni = rand::random::<usize>() % max;
|
||||||
|
let nd = rand::random::<usize>() % len;
|
||||||
|
|
||||||
|
for x in 0..ni {
|
||||||
|
self.dna[(nd + x) % len] = Agent::chromo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_dna(&mut self) -> Result<Solution, Error> {
|
||||||
|
if self.dir == self.dna.len() || self.goal == Solution::None {
|
||||||
|
Ok(Solution::None)
|
||||||
|
} else {
|
||||||
|
let (mb, hit) = self.state.move_dir(self.dna[self.dir])?;
|
||||||
|
|
||||||
|
if mb {
|
||||||
|
self.score += 0.00021;
|
||||||
|
}
|
||||||
|
|
||||||
|
if hit {
|
||||||
|
self.score -= 0.00080;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dir += 1;
|
||||||
|
Ok(self.state.evaluate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(&mut self) -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let player = self.state.lvl.player;
|
||||||
|
|
||||||
|
match self.move_dna()? {
|
||||||
|
Solution::Ok => (),
|
||||||
|
Solution::Win => {
|
||||||
|
self.score += 100000.0;
|
||||||
|
self.goal = Solution::Win;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Solution::None => {
|
||||||
|
self.score -= 0.0032;
|
||||||
|
self.goal = Solution::None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if player == self.state.lvl.player {
|
||||||
|
self.score -= 0.0010;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.score -= 1024.0 + self.state.dist_boxes() as f32;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solution(&self) -> String {
|
||||||
|
(0..self.dir)
|
||||||
|
.map(|i| match self.dna[i] {
|
||||||
|
Direction::U => "U",
|
||||||
|
Direction::D => "D",
|
||||||
|
Direction::L => "L",
|
||||||
|
Direction::R => "R",
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Population {
|
||||||
|
gen: usize,
|
||||||
|
epop_max: usize,
|
||||||
|
mut_max: usize,
|
||||||
|
cross_ratio: usize,
|
||||||
|
map: Level,
|
||||||
|
life: Vec<Agent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub num_threads: usize,
|
||||||
|
pub dna_size: usize,
|
||||||
|
pub pop_size: usize,
|
||||||
|
pub epop_max: usize,
|
||||||
|
pub mut_max: usize,
|
||||||
|
pub gen_max: usize,
|
||||||
|
pub gen_depth: usize,
|
||||||
|
pub cross_ratio: usize,
|
||||||
|
pub map_output: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Population {
|
||||||
|
fn new(config: &Config, map: Level) -> Self {
|
||||||
|
let life: Vec<Agent> = (0..config.pop_size)
|
||||||
|
.map(|_| Agent::new(State::new(map.clone()), config.dna_size))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
gen: 0,
|
||||||
|
epop_max: config.epop_max,
|
||||||
|
mut_max: config.mut_max,
|
||||||
|
cross_ratio: config.cross_ratio,
|
||||||
|
map,
|
||||||
|
life,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self) -> Result<(), Error> {
|
||||||
|
self.life
|
||||||
|
.par_iter_mut()
|
||||||
|
.map(|a| a.evaluate())
|
||||||
|
.collect::<Result<Vec<()>, _>>()?;
|
||||||
|
self.life.par_sort_by(|a, b| b.score.total_cmp(&a.score));
|
||||||
|
|
||||||
|
if self.life[0].goal != Solution::Win {
|
||||||
|
let epop: Vec<Agent> = (0..self.epop_max)
|
||||||
|
.map(|i| {
|
||||||
|
let mut a = self.life[i].clone();
|
||||||
|
|
||||||
|
a.cross(
|
||||||
|
if i % 2 == 0 {
|
||||||
|
&self.life[(i + 1) % self.epop_max]
|
||||||
|
} else {
|
||||||
|
&self.life[(i - 1) % self.epop_max]
|
||||||
|
},
|
||||||
|
self.cross_ratio,
|
||||||
|
);
|
||||||
|
a
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for i in epop.len()..self.life.len() {
|
||||||
|
let mut next = Agent::with_dna(
|
||||||
|
State::new(self.map.clone()),
|
||||||
|
if i % 2 == 0 {
|
||||||
|
epop[i % self.epop_max].dna.clone()
|
||||||
|
} else {
|
||||||
|
epop[(i + 1) % self.epop_max].dna.clone()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
next.mutate(self.mut_max);
|
||||||
|
self.life[i] = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.gen += 1;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(config: Config, rle: Option<String>) -> Result<(), Error> {
|
||||||
|
rayon::ThreadPoolBuilder::new()
|
||||||
|
.num_threads(config.num_threads)
|
||||||
|
.build_global()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if config.pop_size == 0 {
|
||||||
|
return Err(Error::BadParam("population size cannot be 0".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.mut_max > config.dna_size {
|
||||||
|
return Err(Error::BadParam("mutation length too large".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.cross_ratio == 0 {
|
||||||
|
return Err(Error::BadParam("crossover ratio cannot be 0".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let map = if let Some(rle) = rle {
|
||||||
|
Level::read_rle(rle)
|
||||||
|
} else {
|
||||||
|
Level::read(std::io::stdin().lock())
|
||||||
|
}
|
||||||
|
.map_err(Error::Read)?;
|
||||||
|
|
||||||
|
let mut agents: Vec<Agent> = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..config.gen_depth {
|
||||||
|
let mut pop = Population::new(&config, map.clone());
|
||||||
|
|
||||||
|
if let Some(agent) = || -> Result<Option<Agent>, Error> {
|
||||||
|
while pop.gen < config.gen_max {
|
||||||
|
pop.step()?;
|
||||||
|
|
||||||
|
if config.map_output {
|
||||||
|
print!("{}", pop.life[0].state.lvl);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("gen {} score {}", pop.gen, pop.life[0].score);
|
||||||
|
if pop.life[0].goal == Solution::Win {
|
||||||
|
return Ok(Some(pop.life[0].clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}()? {
|
||||||
|
agents.push(agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
agents.par_sort_by_key(|a| a.dir);
|
||||||
|
|
||||||
|
if !agents.is_empty() {
|
||||||
|
println!("{}", agents[0].solution());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
struct Args {
|
||||||
|
/// Number of threads
|
||||||
|
#[arg(short, long, default_value_t = 2)]
|
||||||
|
nt: usize,
|
||||||
|
|
||||||
|
/// DNA strand size (increases search space)
|
||||||
|
#[arg(short, long, default_value_t = 1024)]
|
||||||
|
ds: usize,
|
||||||
|
|
||||||
|
/// Population size
|
||||||
|
#[arg(long, default_value_t = 1024)]
|
||||||
|
ps: usize,
|
||||||
|
|
||||||
|
/// Elite population max size
|
||||||
|
#[arg(short, long, default_value_t = 128)]
|
||||||
|
em: usize,
|
||||||
|
|
||||||
|
/// Maximum mutation length
|
||||||
|
#[arg(short, long, default_value_t = 32)]
|
||||||
|
mt: usize,
|
||||||
|
|
||||||
|
/// Maximum ratio for crossover reproduction
|
||||||
|
#[arg(long, default_value_t = 341)]
|
||||||
|
cr: usize,
|
||||||
|
|
||||||
|
/// Maximum generations until next iteration
|
||||||
|
#[arg(long, default_value_t = 1048576)]
|
||||||
|
gm: usize,
|
||||||
|
|
||||||
|
/// Generation iteration depth
|
||||||
|
#[arg(long, default_value_t = 1)]
|
||||||
|
gd: usize,
|
||||||
|
|
||||||
|
/// Print map for each generation
|
||||||
|
#[arg(short, default_value_t = false)]
|
||||||
|
verbose: bool,
|
||||||
|
|
||||||
|
/// Run-length encoded map
|
||||||
|
map: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
if let Err(e) = bano::run(
|
||||||
|
bano::Config {
|
||||||
|
num_threads: args.nt,
|
||||||
|
dna_size: args.ds,
|
||||||
|
pop_size: args.ps,
|
||||||
|
epop_max: args.em,
|
||||||
|
mut_max: args.mt,
|
||||||
|
gen_max: args.gm,
|
||||||
|
gen_depth: args.gd,
|
||||||
|
cross_ratio: args.cr,
|
||||||
|
map_output: args.verbose,
|
||||||
|
},
|
||||||
|
args.map,
|
||||||
|
) {
|
||||||
|
eprintln!("error: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue