Hackity hax

This commit is contained in:
2023-11-26 21:29:23 +01:00
commit 54762c3c84
12 changed files with 1453 additions and 0 deletions

3
.envrc Normal file
View File

@@ -0,0 +1,3 @@
source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0="
use devenv

16
.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
# Devenv
.devenv*
devenv.local.nix
# direnv
.direnv
# pre-commit
.pre-commit-config.yaml
# Added by cargo
/target

657
Cargo.lock generated Normal file
View File

@@ -0,0 +1,657 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
dependencies = [
"anstyle",
"clap_lex",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "errno"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hermit-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "js-sys"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rubik"
version = "0.1.0"
dependencies = [
"criterion",
]
[[package]]
name = "rustix"
version = "0.38.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
[[package]]
name = "web-sys"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

15
Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "rubik"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dev-dependencies]
criterion = "0.5.1"
[[bench]]
name = "statepack"
harness = false

37
README.md Normal file
View File

@@ -0,0 +1,37 @@
Pyramix analysis
================
This is a small rust program that evaluates every possible shape of the
[Pyraminx](https://www.speedcube.nl/speedcube/pyraminx/qiyi-x-man-bell-v2-pyraminx-m/) in an attempt to solve it from
scratch.
It requires around 250MB of memory to run successfully, and by default prints every possible position along with its
solution.
Position naming
===============
Every piece has a position and an orientation.
The four corners are in a fixed position. I've numbered them in the order top, back, right, left. Their orientations are
named according to how many counterclockwise turns are necessary to bring them into the correct orientation.
The 6 edge pieces are named according to their correct positions. First, the bottom layer: left, right, front.
Next, the upper layer: back, right, front.
The orientations are named somewhat arbitrarily in a way that makes computation easy. Specifically,
each corner rotation has an associated edge position that flips as eges rotate through it: the top corner flips edge 4
(upper right), the back edge flips edge (lower left), and the other two flip edge (lower front). This appears to produce
a consistent naming, but this has not yet been proven.
Given this, cube positions are printed as follows: `[edges]/[corners]`
The edges are given names from a-f according to where they belong, and they are printed in order based on what piece is
actually there. The letter is capitalized if it is "flipped". The corners are printed in order, as `+` if it is rotated
clockwise from its correct position, `-` if rotated counterclockwise, and `,` if it is rotated correctly.
Interesting facts
=================
There are a total of 933,120 legal positions for the cube. There are a total of 3,732,480 possible configurations total,
meaning that there are two different types of single-bit parity violations: these can be summarized as an ordering
violation within the face. (e.g., `acbdef` is invalid), or an odd number of edges are flipped (e.g., `abCdef` is invalid)

10
benches/statepack.rs Normal file
View File

@@ -0,0 +1,10 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rubik::state::State;
pub fn bench_pack(c: &mut Criterion) {
c.bench_function("unpack", |b| b.iter(|| State::unpack(black_box(12345))));
c.bench_function("pack", |b| b.iter(|| State::pack(black_box(State::default()))));
}
criterion_group!(benches, bench_pack);
criterion_main!(benches);

156
devenv.lock Normal file
View File

@@ -0,0 +1,156 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1700140236,
"narHash": "sha256-OpukFO0rRG2hJzD+pCQq+nSWuT9dBL6DSvADQaUlmFg=",
"owner": "cachix",
"repo": "devenv",
"rev": "525d60c44de848a6b2dd468f6efddff078eb2af2",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1685518550,
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1700108881,
"narHash": "sha256-+Lqybl8kj0+nD/IlAWPPG/RDTa47gff9nbei0u7BntE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7414e9ee0b3e9903c24d3379f577a417f0aae5f1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1685801374,
"narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.05",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1700064067,
"narHash": "sha256-1ZWNDzhu8UlVCK7+DUN9dVQfiHX1bv6OQP9VxstY/gs=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "e558068cba67b23b4fbc5537173dbb43748a17e8",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"nixpkgs": "nixpkgs",
"pre-commit-hooks": "pre-commit-hooks"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

29
devenv.nix Normal file
View File

@@ -0,0 +1,29 @@
{ pkgs, ... }:
{
# https://devenv.sh/basics/
env.GREET = "devenv";
# https://devenv.sh/packages/
packages = [ pkgs.git ];
# https://devenv.sh/scripts/
scripts.hello.exec = "echo hello from $GREET";
enterShell = ''
hello
git --version
'';
# https://devenv.sh/languages/
# languages.nix.enable = true;
# https://devenv.sh/pre-commit-hooks/
# pre-commit.hooks.shellcheck.enable = true;
languages.rust.enable = true;
# https://devenv.sh/processes/
# processes.ping.exec = "ping example.com";
# See full reference at https://devenv.sh/reference/options/
}

3
devenv.yaml Normal file
View File

@@ -0,0 +1,3 @@
inputs:
nixpkgs:
url: github:NixOS/nixpkgs/nixpkgs-unstable

2
src/lib.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod state;
pub mod search;

300
src/search.rs Normal file
View File

@@ -0,0 +1,300 @@
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap, HashMap}, hash::Hash};
use std::collections::{BTreeMap, BTreeSet, HashSet};
use crate::state::{M0N, M0P, M1N, M1P, M2N, M2P, M3N, M3P, State};
#[derive(Copy, Clone)]
pub struct SearchNode {
id: u32,
last: u32,
depth: u32,
mov: &'static str,
}
pub trait SearchState {
type Id: Ord+Hash+Copy;
fn id(&self) -> Self::Id;
fn cost(&self) -> u32 {
1
}
fn heuristic(&self) -> u32 {
0
}
}
// ordering tuned for BinaryHeap
impl Ord for SearchNode {
fn cmp(&self, other: &Self) -> Ordering {
self.depth.cmp(&other.depth)
}
}
impl PartialOrd for SearchNode {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for SearchNode {
fn eq(&self, other: &Self) -> bool {
self.depth == other.depth
}
}
impl Eq for SearchNode {}
impl SearchState for SearchNode {
type Id = u32;
fn id(&self) -> Self::Id {
self.id
}
fn cost(&self) -> u32 {
self.depth
}
}
pub trait SearchQueue<T>: Default {
fn q_next(&mut self) -> Option<T>;
fn q_empty(&self) -> bool;
fn q_push(&mut self, item: T);
fn q_clear(&mut self);
fn q_len(&self) -> usize;
}
pub struct SearchEnv<Q> {
pub seen: HashMap<u32, (u32, &'static str)>,
queue: Q,
visits: usize,
pub last_pos: u32,
}
impl<Q: SearchQueue<SearchNode>> Default for SearchEnv<Q> {
fn default() -> Self {
let mut res = Self {
seen: HashMap::new(),
queue: Q::default(),
visits: 0,
last_pos: 0,
};
res.reset();
res
}
}
impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
pub fn reset(&mut self) {
self.seen.clear();
self.queue.q_clear();
let init_node = SearchNode{
id: State::default().pack(),
depth: 0,
last: 0,
mov: "["
};
self.seen.insert(init_node.id, (init_node.id, "S"));
self.queue.q_push(init_node);
}
pub fn is_done(&self) -> bool {
self.queue.q_empty()
}
pub fn search(&mut self) {
self.reset();
eprintln!();
let mut last_pos = 0;
while !self.is_done() {
let node = self.queue.q_next().unwrap();
// check whether node should be re-expanded
self.visits += 1;
if self.visits % 1000 == 0 {
eprintln!("\x1b[1A\x1b[K{}k ({} in queue)", self.visits/100, self.queue.q_len());
}
// if self.seen.insert(node.id, (node.last, node.mov)).is_some() {
// continue;
// }
last_pos = node.id;
self.visit(node.id, node.depth);
}
self.last_pos = last_pos;
eprintln!("Max depth: {last_pos:x}")
}
pub fn visit(&mut self, id: u32, depth: u32) {
let cur = State::from(id);
for (name, mov) in [
("T-", M0P), ("T+", M0N), ("L-", M3P), ("L+", M3N),
("R-", M2P), ("R+", M2N), ("B-", M1P), ("B+", M1N),
] {
let next = cur + mov;
let node = SearchNode {
id: next.into(),
depth: depth+1,
last: id,
mov: name,
};
if self.seen.contains_key(&node.id) {
continue
} else {
self.seen.insert(node.id, (node.last, node.mov))
};
self.queue.q_push(node);
}
}
}
/// Unique priority queue
/// This contains a list of items, each with an ID and a weight. Only the minimum weight for a given item is kept.
pub struct UniquePQueue<Id, Weight, Data> {
node_data: HashMap<Id, (Weight, Data)>,
queue: BTreeSet<(Weight, Id)>,
}
impl<I,W,D> UniquePQueue<I,W,D>
where I: Hash+Ord+Copy,
W: Ord+Copy,
D: Clone {
pub fn insert(&mut self, id: I, weight: W, data: D) {
if let Some((o_weight, o_data)) = self.node_data.get_mut(&id) {
if *o_weight < weight {
return;
}
self.queue.remove(&(*o_weight, id));
*o_weight = weight;
*o_data = data;
} else {
self.node_data.insert(id, (weight, data));
}
self.queue.insert((weight, id));
}
pub fn clear(&mut self) {
self.node_data.clear();
self.queue.clear();
}
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn pop_next(&mut self) -> Option<D> {
self.queue.pop_first().map(|(_,d)| self.node_data[&d].1.clone())
}
pub fn new() -> Self {
Self {
node_data: HashMap::new(),
queue: BTreeSet::new(),
}
}
}
#[derive(Copy, Clone)]
struct TOf32(f32);
impl Ord for TOf32 {
fn cmp(&self, other: &Self) -> Ordering {
self.0.total_cmp(&other.0)
}
}
impl PartialOrd for TOf32 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for TOf32 {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for TOf32 {}
// BFS
pub struct BFS<T>(VecDeque<T>);
impl<T> SearchQueue<T> for BFS<T> {
fn q_next(&mut self) -> Option<T> { self.0.pop_front() }
fn q_empty(&self) -> bool { self.0.is_empty() }
fn q_push(&mut self, item: T) { self.0.push_back(item); }
fn q_clear(&mut self) { self.0.clear(); }
fn q_len(&self) -> usize { self.0.len() }
}
impl<T> Default for BFS<T> {
fn default() -> Self { BFS(VecDeque::new()) }
}
struct CostCompare<T: SearchState>(T);
impl<T: SearchState> Ord for CostCompare<T> {
fn cmp(&self, other: &Self) -> Ordering {
u32::cmp(&self.0.cost(), &other.0.cost()).reverse()
}
}
impl<T: SearchState> PartialOrd for CostCompare<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: SearchState> Eq for CostCompare<T> {
}
impl<T: SearchState> PartialEq for CostCompare<T> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
pub struct BFS1<T: SearchState>{
q: BinaryHeap<CostCompare<T>>,
seen: HashMap<T::Id, u32>,
}
impl<T: SearchState> SearchQueue<T> for BFS1<T> {
fn q_next(&mut self) -> Option<T> { self.q.pop().map(|x| x.0) }
fn q_empty(&self) -> bool { self.q.is_empty() }
fn q_push(&mut self, item: T) {
let id = item.id();
if let Some(odist) = self.seen.get(&id) {
if *odist < item.cost() {
return;
}
}
self.seen.insert(id, item.cost());
self.q.push(CostCompare(item));
}
fn q_clear(&mut self) {
self.q.clear();
self.seen.clear();
}
fn q_len(&self) -> usize { self.q.len() }
}
impl<T: SearchState> Default for BFS1<T> {
fn default() -> Self {
BFS1{
q: BinaryHeap::new(),
seen: HashMap::new(),
}
}
}
pub struct DFS<T>(Vec<T>);
impl<T> SearchQueue<T> for DFS<T> {
fn q_next(&mut self) -> Option<T> { self.0.pop() }
fn q_empty(&self) -> bool { self.0.is_empty() }
fn q_push(&mut self, item: T) { self.0.push(item); }
fn q_clear(&mut self) { self.0.clear(); }
fn q_len(&self) -> usize { self.0.len() }
}
impl<T> Default for DFS<T> {
fn default() -> Self { DFS(Vec::new()) }
}

225
src/state.rs Normal file
View File

@@ -0,0 +1,225 @@
use std::fmt::{Display, Formatter};
use std::ops::Add;
#[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct State {
pub edges: [(u8, bool); 6],
pub corners: [u8; 4],
}
impl Default for State {
fn default() -> Self {
Self {
edges: [
(0, false),
(1, false),
(2, false),
(3, false),
(4, false),
(5, false)
],
corners: [0; 4],
}
}
}
impl State {
pub fn pack(self) -> u32 {
// edges first; each edge is numberd 0..=5 and a bool
let edges = self.edges.iter().copied().map(|(n,f)| (((f as u32) << 3) | n as u32))
.fold(0, |acc, new| acc << 4 | new);
let corners = self.corners.iter().map(|c| *c as u32 % 3).fold(edges, |a,n| a << 2 | n);
corners
}
pub fn unpack(mut v: u32) -> Self {
let mut res = Self::default();
for i in 0..6 {
let slot = v >> 28;
let flip = slot & 8 != 0;
let n = (slot & 7) as u8;
res.edges[i] = (n, flip);
v <<= 4;
}
for i in 0..4 {
res.corners[i] = ((v >> 30) & 3) as u8;
v <<= 2;
}
res
}
const fn mk_move(corner: usize, dir: bool) -> Self {
let mut state = Self {
edges: [
(0, false),
(1, false),
(2, false),
(3, false),
(4, false),
(5, false)
],
corners: [0; 4],
}
;
let mut edges = match corner {
0 => [4,5,3],
1 => [0,1,3],
2 => [2,4,1],
3 => [2,0,5],
_ => unimplemented!()
};
if !dir {
// counter-clockwise
let e0 = edges[1];
edges[1] = edges[2];
edges[2] = e0;
}
const fn mflip((n,f): (u8, bool), df: bool) -> (u8, bool) {
(n, f ^ df)
}
state.edges[edges[1]] = (edges[0] as u8, true);
state.edges[edges[2]] = (edges[1] as u8, false);
state.edges[edges[0]] = (edges[2] as u8, true);
state.corners[corner] = if dir {
1
} else {
2
};
state
}
}
impl Display for State {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut res = String::with_capacity(11);
for (n,flip) in self.edges {
res.push(char::from(if flip { b'A'} else { b'a'} + n) );
}
res.push('/');
for d in self.corners {
res.push(match d {
0 => ',',
1 => '+',
2 => '-',
_ => '?',
})
}
f.write_str(res.as_str())
}
}
impl From<u32> for State {
fn from(value: u32) -> Self {
State::unpack(value)
}
}
impl From<State> for u32 {
fn from(value: State) -> Self {
value.pack()
}
}
impl Add for State {
type Output = State;
fn add(self, rhs: Self) -> Self::Output {
let mut result = self;
for i in 0..6 {
let (sn, sf) = rhs.edges[i];
let (rn, rf) = self.edges[sn as usize];
result.edges[i] = (rn, rf ^ sf);
}
for i in 0..4 {
result.corners[i] = (self.corners[i] + rhs.corners[i]) % 3;
}
result
}
}
pub const M0P: State = State::mk_move(0, true);
pub const M1P: State = State::mk_move(1, true);
pub const M2P: State = State::mk_move(2, true);
pub const M3P: State = State::mk_move(3, true);
pub const M0N: State = State::mk_move(0, false);
pub const M1N: State = State::mk_move(1, false);
pub const M2N: State = State::mk_move(2, false);
pub const M3N: State = State::mk_move(3, false);
#[cfg(test)]
mod test {
use super::*;
fn test_flipped(s: State, edge: usize) -> bool {
s.edges[edge] == (edge as u8, true)
}
macro_rules! test_flipped {
($mov:expr, $edge:expr) => {{
let mov = $mov;
assert_eq!(mov.edges[$edge], ($edge, true));
}}
}
#[test]
fn test_203_2() {
test_flipped!(M2P + M0P + M3P, 2)
}
#[test]
fn test_312_2() {
test_flipped!(M3P + M1P + M2P, 2)
}
#[test]
fn test_102_1() {
test_flipped!(M1P + M0P + M2P, 1)
}
#[test]
fn test_231_1() {
test_flipped!(M2P + M3P + M1P, 1)
}
#[test]
fn test_301_0() {
test_flipped!(M3P + M0P + M1P, 0)
}
#[test]
fn test_123_0() {
test_flipped!(M1P + M2P + M3P, 0)
}
#[test]
fn test_210_4() {
test_flipped!(M2P + M1P + M0P, 4)
}
#[test]
fn test_032_4() {
test_flipped!(M0P + M3P + M2P, 4)
}
#[test]
fn test_130_3() {
test_flipped!(M1P+M3P+M0P, 3)
}
#[test]
fn test_021_3() {
test_flipped!(M0P+M2P+M1P, 3)
}
#[test]
fn test_320_5() {
test_flipped!(M2P + M0P + M3P, 2)
}
#[test]
fn test_013_5() {
test_flipped!(M0P+M1P+M3P, 5)
}
#[test]
fn test_pack() {
let st = State::default();
assert_eq!(st, State::unpack(st.into()))
}
}