commit 54762c3c8467769a2681882ee802ce8e5f661872 Author: TQ Hirsch Date: Sun Nov 26 21:29:23 2023 +0100 Hackity hax diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..6de8a8a --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0=" + +use devenv \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e457574 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ + +# Devenv +.devenv* +devenv.local.nix + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml + + + +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4223ef6 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b28ed2f --- /dev/null +++ b/Cargo.toml @@ -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 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f18234 --- /dev/null +++ b/README.md @@ -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) \ No newline at end of file diff --git a/benches/statepack.rs b/benches/statepack.rs new file mode 100644 index 0000000..b4971af --- /dev/null +++ b/benches/statepack.rs @@ -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); \ No newline at end of file diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..31052e4 --- /dev/null +++ b/devenv.lock @@ -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 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..9201ccf --- /dev/null +++ b/devenv.nix @@ -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/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..c7cb5ce --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,3 @@ +inputs: + nixpkgs: + url: github:NixOS/nixpkgs/nixpkgs-unstable diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8441340 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod state; +pub mod search; \ No newline at end of file diff --git a/src/search.rs b/src/search.rs new file mode 100644 index 0000000..981ed9b --- /dev/null +++ b/src/search.rs @@ -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 { + 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: Default { + fn q_next(&mut self) -> Option; + 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 { + pub seen: HashMap, + queue: Q, + visits: usize, + pub last_pos: u32, +} + +impl> Default for SearchEnv { + fn default() -> Self { + let mut res = Self { + seen: HashMap::new(), + queue: Q::default(), + visits: 0, + last_pos: 0, + }; + res.reset(); + res + } +} + +impl> SearchEnv { + 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 { + node_data: HashMap, + queue: BTreeSet<(Weight, Id)>, +} +impl UniquePQueue +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 { + 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 { + 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(VecDeque); +impl SearchQueue for BFS { + fn q_next(&mut self) -> Option { 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 Default for BFS { + fn default() -> Self { BFS(VecDeque::new()) } +} + +struct CostCompare(T); +impl Ord for CostCompare { + fn cmp(&self, other: &Self) -> Ordering { + u32::cmp(&self.0.cost(), &other.0.cost()).reverse() + } +} +impl PartialOrd for CostCompare { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Eq for CostCompare { +} +impl PartialEq for CostCompare { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + + + + +pub struct BFS1{ + q: BinaryHeap>, + seen: HashMap, +} + +impl SearchQueue for BFS1 { + fn q_next(&mut self) -> Option { 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 Default for BFS1 { + fn default() -> Self { + BFS1{ + q: BinaryHeap::new(), + seen: HashMap::new(), + } + } +} + + +pub struct DFS(Vec); +impl SearchQueue for DFS { + fn q_next(&mut self) -> Option { 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 Default for DFS { + fn default() -> Self { DFS(Vec::new()) } +} + diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..5450d98 --- /dev/null +++ b/src/state.rs @@ -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 for State { + fn from(value: u32) -> Self { + State::unpack(value) + } +} + +impl From 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())) + } +} \ No newline at end of file