nissy-classic

Stable branch of nissy
git clone https://git.tronto.net/nissy-classic
Download | Log | Files | Refs | README | LICENSE

commit 3568412f8f230774d0d11d7ed1c897424f95d3ef
parent 67e1b5e6e6a2c917a2fe58a37a1382c982b1e5c5
Author: Sebastiano Tronto <sebastiano.tronto@gmail.com>
Date:   Thu, 11 Nov 2021 21:37:34 +0100

Rewritten from scratch. Welocme nissy 2.0!

Diffstat:
AINSTALL | 27+++++++++++++++++++++++++++
MLICENSE | 2+-
AMakefile | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
MREADME.md | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
ATODO.md | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcompile.sh | 1-
Adoc/nissy.1 | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ddocs/add.txt | 18------------------
Ddocs/change.txt | 21---------------------
Ddocs/clear.txt | 9---------
Ddocs/co.txt | 39---------------------------------------
Ddocs/dr.txt | 45---------------------------------------------
Ddocs/drcorners.txt | 34----------------------------------
Ddocs/drfinish.txt | 29-----------------------------
Ddocs/eo.txt | 38--------------------------------------
Ddocs/exit.txt | 9---------
Ddocs/help.txt | 15---------------
Ddocs/htr.txt | 30------------------------------
Ddocs/htrfinish.txt | 23-----------------------
Ddocs/invert.txt | 14--------------
Ddocs/nissy.txt | 88-------------------------------------------------------------------------------
Ddocs/pic.txt | 20--------------------
Ddocs/print.txt | 19-------------------
Ddocs/quit.txt | 9---------
Ddocs/replace.txt | 22----------------------
Ddocs/save.txt | 22----------------------
Ddocs/scramble.txt | 22----------------------
Ddocs/solve.txt | 33---------------------------------
Ddocs/unniss.txt | 14--------------
Dmakedoc.sh | 29-----------------------------
Dnissy | 0
Anissy-2.0beta1.tar.gz | 0
Dnissy-win.exe | 0
Aold/2020-unknown-date/cube_backup.c | 527+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2020-unknown-date/cubeutils.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2020-unknown-date/cubeutils.h | 18++++++++++++++++++
Aold/2020-unknown-date/moves.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2020-unknown-date/moves.h | 9+++++++++
Aold/2020-unknown-date/pieces.c | 208+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2020-unknown-date/pieces.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/cube.c | 664+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/cube.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/main.c | 46++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/solve.c | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/solve.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/utils.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-06/utils.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/compile.sh | 1+
Aold/2021-02-18-piecefilter/nissy | 0
Aold/2021-02-18-piecefilter/src/cube.c | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/cube.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/main.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/moves.c | 482+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/moves.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/solve.c | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/solve.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/transformations.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/transformations.h | 37+++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/utils.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-18-piecefilter/src/utils.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/cube.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/cube.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/main.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/moves.c | 489+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/moves.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/solve.c | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/solve.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/transformations.c | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/transformations.h | 34++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/utils.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-02-28-transformcube-works/src/utils.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/cube.c | 293+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/cube.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/main.c | 24++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/moves.c | 412+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/moves.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/solve.c | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/solve.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/transformations.c | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/transformations.h | 34++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/utils.c | 197+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-05-26-before-restyle/utils.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-02-cleanedup/cube.c | 2010+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-02-cleanedup/cube.h | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-02-cleanedup/main.c | 35+++++++++++++++++++++++++++++++++++
Aold/2021-06-02-cleanedup/steps.h | 22++++++++++++++++++++++
Aold/2021-06-14-oldalg/cube.c | 2849+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-14-oldalg/cube.h | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-14-oldalg/cubetypes.h | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-14-oldalg/main.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-15-before-separating-steps/cube.c | 2836+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-15-before-separating-steps/cube.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-15-before-separating-steps/cubetypes.h | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-15-before-separating-steps/main.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-15-before-separating-steps/steps.h | 20++++++++++++++++++++
Aold/2021-06-17-cachedata/cube.c | 2738+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-17-cachedata/cube.h | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-17-cachedata/cubetypes.h | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-17-cachedata/main.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-17-cachedata/steps.c | 327+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-17-cachedata/steps.h | 34++++++++++++++++++++++++++++++++++
Aold/2021-06-23-realizing-bad-tables/HERE | 1+
Aold/2021-06-23-realizing-bad-tables/cube.c | 2844+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-realizing-bad-tables/cube.h | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-realizing-bad-tables/cubetypes.h | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-realizing-bad-tables/main.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-realizing-bad-tables/steps.c | 486+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-realizing-bad-tables/steps.h | 42++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-uint16t/cube.c | 2762+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-uint16t/cube.h | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-23-uint16t/cubetypes.h | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-noreached/cube.c | 3394+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-noreached/cube.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-noreached/cubetypes.h | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-noreached/macros.h | 23+++++++++++++++++++++++
Aold/2021-06-30-noreached/main.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-noreached/steps.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-noreached/steps.h | 24++++++++++++++++++++++++
Aold/2021-06-30-reached/cube.c | 3419+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-reached/cube.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-reached/cubetypes.h | 251+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-reached/macros.h | 23+++++++++++++++++++++++
Aold/2021-06-30-reached/main.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-reached/steps.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-06-30-reached/steps.h | 24++++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/cube.c | 3394+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/cube.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/cubetypes.h | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/macros.h | 23+++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/main.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/steps.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-02-genptable-dfs/steps.h | 24++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/README | 3+++
Aold/2021-07-15-almostbeforerefactor/alg.c | 3388+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/alg.h | 27+++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/cube.c | 3391+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/cube.h | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/cubetypes.h | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/macros.h | 23+++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/main.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/nissy | 0
Aold/2021-07-15-almostbeforerefactor/steps.c | 284+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-07-15-almostbeforerefactor/steps.h | 24++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/alg.c | 366+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/alg.h | 35+++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/commands.c | 329+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/commands.h | 13+++++++++++++
Aold/2021-11-10-beforeremovingchecker/coord.c | 629+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/coord.h | 40++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/cube.c | 716+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/cube.h | 40++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/cubetypes.h | 298+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/env.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/env.h | 15+++++++++++++++
Aold/2021-11-10-beforeremovingchecker/moves.c | 474+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/moves.h | 16++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/pf.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/pf.h | 18++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/pruning.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/pruning.h | 23+++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/shell.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/shell.h | 13+++++++++++++
Aold/2021-11-10-beforeremovingchecker/solve.c | 209+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/solve.h | 9+++++++++
Aold/2021-11-10-beforeremovingchecker/steps.c | 916+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/steps.h | 10++++++++++
Aold/2021-11-10-beforeremovingchecker/symcoord.c | 359+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/symcoord.h | 15+++++++++++++++
Aold/2021-11-10-beforeremovingchecker/trans.c | 372+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/trans.h | 13+++++++++++++
Aold/2021-11-10-beforeremovingchecker/utils.c | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/2021-11-10-beforeremovingchecker/utils.h | 41+++++++++++++++++++++++++++++++++++++++++
Aold/backup-manysteps-pretrans/steps.c | 369+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/backup-manysteps-pretrans/steps.h | 36++++++++++++++++++++++++++++++++++++
Aold/coord.c | 1014+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/coord.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Aold/utils.c | 294+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aold/utils.h | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/alg.c | 364+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/alg.h | 35+++++++++++++++++++++++++++++++++++
Asrc/commands.c | 346+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/commands.h | 13+++++++++++++
Asrc/coord.c | 522+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/coord.h | 23+++++++++++++++++++++++
Dsrc/coordinates.c | 286-------------------------------------------------------------------------------
Dsrc/coordinates.h | 92-------------------------------------------------------------------------------
Asrc/cube.c | 701+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cube.h | 40++++++++++++++++++++++++++++++++++++++++
Asrc/cubetypes.h | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/env.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/env.h | 15+++++++++++++++
Dsrc/helppages.h | 689-------------------------------------------------------------------------------
Dsrc/io.c | 232-------------------------------------------------------------------------------
Dsrc/io.h | 21---------------------
Dsrc/main.c | 937-------------------------------------------------------------------------------
Msrc/moves.c | 956++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/moves.h | 91+++++++++++--------------------------------------------------------------------
Asrc/pf.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pf.h | 18++++++++++++++++++
Asrc/pruning.c | 271+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pruning.h | 23+++++++++++++++++++++++
Dsrc/pruning_tables.c | 483-------------------------------------------------------------------------------
Dsrc/pruning_tables.h | 66------------------------------------------------------------------
Asrc/shell.c | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/shell.h | 13+++++++++++++
Asrc/solve.c | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/solve.h | 9+++++++++
Dsrc/solver.c | 1058-------------------------------------------------------------------------------
Dsrc/solver.h | 16----------------
Asrc/steps.c | 941+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/steps.h | 10++++++++++
Asrc/symcoord.c | 355+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/symcoord.h | 15+++++++++++++++
Asrc/trans.c | 375+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/trans.h | 13+++++++++++++
Msrc/utils.c | 398+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/utils.h | 99+++++++++++++++++++++++++++++++++----------------------------------------------
217 files changed, 59483 insertions(+), 5284 deletions(-)

diff --git a/INSTALL b/INSTALL @@ -0,0 +1,27 @@ +# Requirements + +A full installation of nissy requires about 1.8Gb of space, of which 1.6Gb are +occupied by the huge pruning table for optimal solving, and running it requires +the same amount of RAM. +One can choose to never use the optimal solver and not to install the relative +pruning table. If so, about 200Mb should be enough. + +# Installation + +## On a UNIX system: + +Edit the Makefile to match your local configuration (usually not necessary, but you +may want to change the PREFIX variable) and run make, followed by make install. +After that, you need to download some files and manually save them to your NISSYDATA +folder (se manual page, ENVIRONMENT section). If you don't do it nissy can compute +these files when needed and save them in the correct folders, but some of them +take hours to generate even for a powerful computer (for reference, the huge pruning +table takes about 20 hours on my laptop). +Choose one of the following: + https://math.uni.lu/tronto/nissy/nissy-tables-full.zip + https://math.uni.lu/tronto/nissy/nissy-tables-full.tar.gz + https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.zip + https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.tar.gz +extract the archive and copy the tables folder into NISSIDATA (paste there +the whole folder, not file by file). The "nohuge" files are much smaller and do not +contain the huge pruning table for the optimal solver. diff --git a/LICENSE b/LICENSE @@ -1,4 +1,4 @@ - GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> diff --git a/Makefile b/Makefile @@ -0,0 +1,54 @@ +# See LICENSE file for copyright and license details. + +VERSION = 2.0beta1 + +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +CPPFLAGS = -DVERSION=\"${VERSION}\" +CFLAGS = -pedantic -Wall -Wextra -Wno-unused-parameter -O3 ${CPPFLAGS} +DBGFLAGS = -pedantic -Wall -Wextra -Wno-unused-parameter -g ${CPPFLAGS} + +CC = cc + + +all: options nissy + +options: + @echo nissy build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "CC = ${CC}" + +nissy: + ${CC} ${CFLAGS} -o nissy.o src/*.c + +debug: + ${CC} ${DBGFLAGS} -o nissy.o src/*.c + +clean: + rm -rf nissy.o nissy-${VERSION}.tar.gz + +dist: clean + mkdir -p nissy-${VERSION} + cp -R LICENSE Makefile INSTALL doc src nissy-${VERSION} + groff -Tpdf -mandoc doc/nissy.1 > nissy-${VERSION}/doc/nissy.pdf + groff -Thtml -mandoc doc/nissy.1 > nissy-${VERSION}/doc/nissy.html + tar -cf nissy-${VERSION}.tar nissy-${VERSION} + gzip nissy-${VERSION}.tar + rm -rf nissy-${VERSION} + +install: nissy + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f nissy.o ${DESTDIR}${PREFIX}/bin/nissy + chmod 755 ${DESTDIR}${PREFIX}/bin/nissy + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < doc/nissy.1 \ + > ${DESTDIR}${MANPREFIX}/man1/nissy.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/nissy.1 + +uninstall: + rm -rf ${DESTDIR}${PREFIX}/bin/nissy ${DESTDIR}${MANPREFIX}/man1/nissy.1 + for s in ${SCRIPTS}; do rm -rf ${DESTDIR}${PREFIX}/bin/$$s; done + +.PHONY: all options nissy debug clean dist install uninstall + diff --git a/README.md b/README.md @@ -1,41 +1,128 @@ -# nissy -A Rubik's cube solver and FMC assistant. +# Nissy -## Just another cube solver? -Yes, pretty much. I wanted to write one and I started coding without any -specific goal in mind. It is not more efficient than [CubeExplorer](http://kociemba.org/cube.htm), nor it is -particularly user-friendly. +A Rubik's cube solver and FMC assistant. For optimal HTM solving nissy is about as +fast as Herbert Kociemba's [Cube Explorer](http://kociemba.org/cube.htm), and it +uses the same method. Nissy can also solve many different substeps of +Thistlethwaite's algorithm (DR/HTR), and can use NISS (Normal-Inverse Scramble Switch). -## But does it do something unique? -Yes, actually it does, but only for a very small niche of people. It allows to produce step-by-step solutions using DR -([Thistlethwaite](/https://www.speedsolving.com/wiki/index.php/Thistlethwaite%27s_algorithm)/[Kociemba](https://www.speedsolving.com/wiki/index.php/Kociemba%27s_Algorithm) algorithm) -combined with [NISS](https://www.speedsolving.com/wiki/index.php/Fewest_Moves_techniques). This makes it somewhat useful for [FMC](https://www.speedsolving.com/wiki/index.php/Fewest_Moves_Challenge) solvers who want to analyze a scramble and see if they missed something, -or what was the optimal way to solve a certain substep at a given point, and so on. +It can be useful to analyze your DR solves (and more, once I implement more features). -## How to use it -**Update:** the file nissy-win.exe should work as a Windows executable, but -I have not tested it (I don't have a Windows machine). +## Why should I use nissy? -Check out the help pages in the docs folder. They are also available from -within nissy with the command "help". +You should use nissy if you: +* Want to analyze your DR solutions or check for multiple optimal (or sub-optimal) +solutions for EO/DR/HTR or similar substeps. +* You just want a Rubik's cube solver and you like command line interfaces. +* You want an alternative to Cube Explorer. -I will add more examples and maybe screenshots when I feel like. +## Requirements + +A full installation of nissy requires about 1.8Gb of space, of which 1.6Gb are +occupied by the huge pruning table for optimal solving, and running it requires +the same amount of RAM. +One can choose to never use the optimal solver and not to install the relative +pruning table. If so, about 200Mb should be enough. ## Installation -For now you have to download all the files and compile the source -code yourself. Remember to tell your -compiler to use the [C99 standard](https://en.wikipedia.org/wiki/C99). For -example, on a Linux system with GCC installed: - -``` -cd path/to/nissy -gcc -O2 -std=c99 -o nissy ./src/*.c -./nissy -``` - -You can also use the script compile.sh, which executes that -gcc line (with a few extra options). - -## Tips -You can use a tool such as [rlwrap](https://github.com/hanslub42/rlwrap) to allow -for infinte command history within nissy! + +### On a UNIX system: + +You can download the source code for the latest version from git or simply clone +the repo with `git clone https://github.com/sebastianotronto/nissy`. + +Edit the Makefile to match your local configuration (usually not necessary, but you +may want to change the `PREFIX` variable) and run `make`, followed by `make install`. +After that, you need to download some files and manually save them to your `NISSYDATA` +folder (see manual page, ENVIRONMENT section). If you don't do it nissy can compute +these files when needed and save them in the correct folders, but some of them +take hours to generate even for a powerful computer (for reference, the huge pruning +table takes about 20 hours on my laptop). + +Choose one of the following: + +| |.zip|.tar.gz| +|-|-|-| +|Full (~720Mb)|[full.zip](https://math.uni.lu/tronto/nissy/nissy-tables-full.zip)|[full.tar.gz](https://math.uni.lu/tronto/nissy/nissy-tables-full.tar.gz)| +|No huge table (~90Mb)|[nohuge.zip](https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.zip)|[nohuge.tar.gz](https://math.uni.lu/tronto/nissy/nissy-tables-nohuge.tar.gz)| + +extract the archive and copy the `tables` folder into `NISSIDATA` (paste there +the whole folder, not the single files). The `nohuge` files are much smaller and +do not contain the huge pruning table for the optimal solver. + +### On Windows + +Coming soon. + +## Structure of the code + +You can find all the source code in the `src` folder. +I strived to keep it legible but I did not write many comments (barely any at all). +I'll try to explain here the main parts of the program. + +### Cube, moves and transformations + +There are many ways to represent a cube. In nissy I use two: +* An array representation `CubeArray`: 3 arrays representing the permutation +of corners, edges and centers and 2 arrays for the orientation of corners and edges. +* An 11-integers representation `Cube`: 3 integers for edge orientation (with respect +to the three axes), 3 for corner orientation, and so on. Edge permutation is a bit +complicated because encoding 12 factorial as a single number is too large for some +practical reasons, so I use 3 integers for that. + +Moves are easy to apply on the array form, but they are slow. So `moves.c` +contains the instructions to create all the transition tables necessary +to get the next position for the cube with just 11 lookup operations +(one for each of the 11 integers in the second representation). +These transition tables are saved in the `mtables` file in the +`tables` folder in binary format. + +The 11 integers are obviously redundant, but keeping all of them makes it easy +to apply transformations. A transformation is a rotation of the whole cube, possibly +combined with a mirror operation. Applying a transformation to a cube (say obtained +by applying a scramble to the solved cube) means applying the transformation to a +solved cube, then the scramble and then the inverse of the transformation +(i.e. conjugating by it). + +### Coordinates and pruning tables + +A *coordinate* consists of a function that takes a cube (in the 11-integer +representation) and return an (unsigned, 64-bit) integer. They are used +to "linearize" a cube and build pruning tables, which speed up significantly the +solving process. To be able to access the pruning table quickly, the function +needs to be very fast (e.g. it should not convert between the two representations +of the cube if not necessary). + +Some coordinates make use of symmetries to reduce the size of the resulting +pruning table. Unfortunately this complicates the code a lot, but it is a huge +advantage: it reduces by a factor of about 16 the huge pruning table, which +results in around 1.6Gb instead of 24 or so. + +Pruning tables are related to a specific step, a moveset and a coordinate. They +contain one value from 0 to 15 (4 bits) for each possible value for the coordinate, +which is less or equal than the minimum number of moves required to solve the +given step with the given moveset for a cube which has the given coordinate. For example, +say the coordinate `neo` gives the number of non-oriented edges (say with respect to +F/B). Then the possible values for the coordinate are 0,2,4,...,12. An associate pruning +table to solving EO with HTM moveset and this coordinate would have values 0 (for +`neo=0`), 3 (for `neo=2`), 1 (for `neo=4`)... + +There is one caveat: each coordinates also needs an inverse function that takes a +coordinate value and returns a cube which has that coordinate. This is in general +more complicated, but luckily the cube does not need to be fully built or consistent. +This inverse-coordinate is used only in one specific step when building pruning tables +to avoid using up hundreds of Gb of memory. + +Note: this part is different from what Cube Explorer does. Overall I think it is +conceptually easier, although in practice it was still hard to implement. +If anything it is more generalizable and one can use it to build any coordinate +they might like. + +### Solving + +Solving is implemented as a generic function that takes both a step and +a (scrambled) cube as input, as well as some extra parameters that say e.g. +how many solution one wants. A step consists, among other things, of +an estimator function that, given a cube, gives a lower bound for the number +of moves needed to complete the step. Many of these estimators simply +look up the corresponding values in the appropriate pruning table. + diff --git a/TODO.md b/TODO.md @@ -0,0 +1,53 @@ +# TODO list + +This is a list of things that I would like to add or change at some point. +It's more of a personal reminder than anything else. + +## Commands + +### Commands that are available in nissy 1.0, but not in this version (yet): +* drcorners (solve corners after dr) +* search and improve non-optimal subsequences +* unniss (rewrite A (B) -> B' A) +* scramble [dr, corners only, edges only, htr, ...] +* save and edit algs as "variables" +* invert an alg + +### More steps for `solve` +* QTM optimal solving +* Block-building steps (cross, roux blocks, ...) +* Other common steps (LSE, ...) + +### Improvements to currently implemented commands +* solve should re-orient first if needed and not just give up if centers are off +* solve should try up to a small bound without loading the large pruning table +* drfin for HTR scrambles should try all 3 axis and pick the best solutions; + in general every step that automatically detects orientation should do this + +### New features +* cleanup: translate an alg to the standard HTM moveset + reorient at the end +* batch mode: read list of commands from stdin or a file and exec them + one after the other non-interactively +* configurability: add an `alias` command, run config file at startup + +## Distribution + +* make env.c compatible with Windows (and check that it works with + BSD/MacOS) +* better internal help page for each command (take it from man page) +* better man page +* find a better way to distribute the large tables, especially khuge + +## Technical stuff + +### Memory management +* fail gracefully when there is not enough memory to load a large table +* free tables from memory when not used +* optionally run in low-memory friendly version (no tables above a few Mb); + this can be useful e.g. for embedded devices + +### Structural changes +* client/server architecture: run a server process in the background so that + multiple client processess can send it queries and get results; this would + open up the door for a web-based version or graphical clients +* use multiple threads to search for solutions in parallel diff --git a/compile.sh b/compile.sh @@ -1 +0,0 @@ -gcc -Wall -Wextra -O2 -std=c99 -o nissy -g ./src/*.c diff --git a/doc/nissy.1 b/doc/nissy.1 @@ -0,0 +1,153 @@ +.Dd November 2021 +.Dt NISSY 1 +.Os +.Sh NAME +.Nm nissy +.Nd a Rubik's cube solver and FMC assistant +. +.Sh SYNOPSIS +.Nm +.Nm +.Ar command +.Op options... +. +When run without any argument an interactive shell is launched, otherwise +the provided +.Ar command +is executed and nissy terminates. +The commands that can be run in the interactive shell are the same that can +be run non-interactively and are provided below. +. +.Sh DESCRIPTION +.Nm +is a Rubik's Cube solver. Its optimal solver function uses Kociemba's one-step +algorithm (huge optimal solver), and its performance is comparable to that +of Kociemba's implementation in Cube Explorer. +nissy can also solve different substeps of the Thistlethwaite's algorithm +and more. +. +.Sh COMMANDS +The available +.Ar commands +are the following: +. +.Bl -tag -width Ds +. +.It Nm commands +List all available commands. +. +.It Nm help Op Ar command +Display help. If no +.Ar command +is given, a generic help message is printed, otherwise a specific help +relative to +.Ar command +is returned. +. +.It Nm print Ar scramble +Display a text-only description of the cube obtained after applying +.Ar scramble . +. +.It Nm quit +Quit nissy. +. +.It Nm solve Ar step Oo Ar options Oc Ar scramble +Solve the given +.Ar step +on the given +.Ar scramble. +By default it finds only one (shortest) solution, without using niss, and it +displays the number of moves at the end of the line. +. +The options for the +.Ar solve +command are the following: +. +.Bl -tag -width Ds +. +.It Fl m Ar min +Only look for solution that are at least +.Ar min +moves long. +. +.It Fl M Ar MAX +Only look for solution that are at most +.Ar MAX +moves long. +. +.It Fl s Ar n +Try to find +.Ar n +solutions. +. +.It Fl a +Print all solutions: some solutions are filtered out by default for some +steps, for examples EOs that finish with F\(aq, with this options they are not. +. +.It Fl n +Allow use of NISS. +. +.It Fl o +Only find solutions that require the minimum number of moves. +. +.It Fl p +Plain style: do not print the number of moves. +. +.It Fl v +Verbose mode: print some information during the search and print each solution +as it is found instead of only printing them all together at the end. +. +. +.El +. +.It Nm steps +List all available +.Ar steps +for the +.Ar solve +command. +. +.It Nm version +Display version information. +. +.El +. +.Sh ENVIRONMENT +Data is stored in the folder pointed to by +.Nm $NISSYDATA. +If that variable is unset the folder +.Nm $XDG_DATA_HOME/nissy +or +.Nm $HOME/.nissy +is used instead. +. +.Sh EXAMPLES +. +The command: +.Dl nissy solve -v \(dqR\(aqU\(aqFD2L2FR2U2R2BD2LB2D\(aqB2L\(aqR\(aqBD2BU2LU2R\(aqU\(aqF\(dq +. +Returns: +.Dl Found 0 solutions, searching depth 0... +.Dl Found 0 solutions, searching depth 1... +.Dl (some more lines) +.Dl Found 0 solutions, searching depth 16... +.Dl D2 F\(aq U2 D2 F\(aq L2 D R2 D F B2 R\(aq L2 F\(aq U\(aq D +.Dl D2 F\(aq U2 D2 F\(aq L2 D R2 D F B2 R\(aq L2 F\(aq U\(aq D (16) +. +The command: +.Dl nissy solve eofb -m 4 -M 5 -n -s 6 \(dqR\(aqU\(aqFD2L2 FR2 U2R2BD2 L B2 D\(aq B2 L\(aq R\(aq\(dq +. +Returns: +.Dl U B U\(aq B (4) +.Dl U2 D2 B (B) (4) +.Dl U2 F R2 F (4) +.Dl U2 B U2 B (4) +.Dl U2 B (U B) (4) +.Dl U\(aq B U B (4) +. +.Sh AUTHORS +.An Sebastiano Tronto Aq Mt sebastiano.tronto@gmail.com +. +.Sh SOURCE CODE +Source code is available at +.Lk https://github.com/sebastianotronto/nissy diff --git a/docs/add.txt b/docs/add.txt @@ -1,18 +0,0 @@ - -HELP PAGE FOR COMMAND add - -SYNTAX -add [MOVES|$ID1|@ID1] $ID2 - -DESCRIPTION -Appends either MOVES, the scramble memorized under $ID1 or the output sequence -memorized under @ID1 at the end of the scramble memorized under $ID2. If none -of MOVES, $ID1 or @ID1 is specified, the user will be asked to type the moves. -Menmonic: "add x to y" or just "add to y". - -EXAMPLES -add $1 - The user is required to type the moves that will be appended to $1. -add F R B $1 - Appends the moves F R B to scramble $1. Now scramble $1 ends with F R B. - diff --git a/docs/change.txt b/docs/change.txt @@ -1,21 +0,0 @@ - -HELP PAGE FOR COMMAND change - -SYNTAX -change $ID1 [MOVES|$ID2|@ID2] - -DESCRIPTION -Changes the scramble $ID1 to either MOVES, the scramble $ID2, the output @ID2 -or, if none is specified, the moves entered by the user. The scramble that was -memorized under $ID1 is then lost. -Mnemonic: "change x to y", or just "change x". - -EXAMPLES -change $1 - The user is required to type the moves that will replace $1. -change $2 $3 - Saves the scramble that was saved under $3 in $2. Now $2 and $3 are the same - scrambles, and the old $2 is lost. -change $1 U R - Saves U R as scramble $1. - diff --git a/docs/clear.txt b/docs/clear.txt @@ -1,9 +0,0 @@ - -HELP PAGE FOR COMMAND clear - -SYNTAX -clear - -DESCRIPTION -Resets all saved scrambles and output sequences. - diff --git a/docs/co.txt b/docs/co.txt @@ -1,39 +0,0 @@ - -HELP PAGE FOR COMMAND co - -SYNTAX -co [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Solves CO for a given scramble. A scramble can be given as last argument of the -command, or an ID of a saved scramble can be provided. If none of the two is -given, a prompt will ask the user to input a new scramble. - -OPTIONS -axis={fb,rl,ud} Specify the axis for the CO. One to three axes can be given, - comma separated, no spaces. - Default: CO on any of the three axis (omitting the option is - the same as specifying axis=fb,rl,ud). -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -h Show hidden COs. - Default, if an CO ending in e.g. F is shown, the equivalent - one ending in F' is hidden. -i Ignore centers. By default the CO is aligned with centers. -niss Use NISS. - Default: does not use NISS. -n=N Specify a maximum number of COs to be output. N must be a - number. - Default value: 1. - -EXAMPLES -co axis=fb $1 - Finds one optimal CO on fb for the first saved scramble. - -co n=5 b=4 U R F - Finds up to 5 COs of length at most 4 for scramble U R F. - -co n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F - Finds up to 100 COs of lenth at most 4, possibly using NISS, including - "hidden" COs, excluding the rl axis. - diff --git a/docs/dr.txt b/docs/dr.txt @@ -1,45 +0,0 @@ - -HELP PAGE FOR COMMAND dr - -SYNTAX -dr [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Solves DR for a given scramble. A scramble can be given as last argument of the -command, or an ID of a saved scramble can be provided. If none of the two is -given, a prompt will ask the user to input a new scramble. -If the option "from" is given (see below), it solves DR from an EO (if edges -are oriented) without breaking that EO. -The first time this command is called without the option from (and, to some -extent, also the first time it is called with the option from), nissy loads -some pruning tables that were not loaded on startup, causing a small but -noticeable delay. - -OPTIONS -axis={fb,rl,ud} Specify the axis for the DR. One to three axes can be given, - comma separated, no spaces. - Default: DR on any of the three axis (omitting the option is - the same as specifying axis=fb,rl,ud). -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -h Show hidden DRs. - Default, if an DR ending in e.g. R is shown, the equivalent - one ending in R' is hidden. -from {fb|rl|ud} Solve DR from the specified EO, which must be solved, - without breaking the EO. -niss Use NISS. It works only if solving DR from EO. - Default: does not use NISS. -n=N Specify a maximum number of EOs to be output. N must be a - number. - Default value: 1. - -EXAMPLES -dr from rl axis=ud $1 - Finds optimal DR on ud, starting from EO on rl, for the first saved scramble. - -dr niss from fb n=10 m=6 F2 R L B' F D U' R2 L' F D B - Finds up to 10 DRs of length at most 6 from EO on fb, possibly using NISS. - -dr n=100 axis=ud h - Finds 100 DRs on ud, including "hidden" DRs. - diff --git a/docs/drcorners.txt b/docs/drcorners.txt @@ -1,34 +0,0 @@ - -HELP PAGE FOR COMMAND drcorners - -SYNTAX -drcorners [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Similar to drfinish, but only solves corners. CO must be solved. The scramble -can be given as the last argument of the command, or it may be given as an $ID -or @ID, or it can be typed out on the following line. - -OPTIONS -from {ud|fb|rl} Allows to specify on which axis the CO is solved. It is - usually not necessary, since nissy will find a CO on any - axis. -i Ignores E-layer centers. By default cornersare solved - relatively to centers; this options allows for solutions - which solve corners relatively to each other and to the - U and D sides, but not to the E layer (or any equivalent - if the CO is not on U/D). -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -n=N Specify a maximum number of solutions to be output. N must - be a number. - Default value: 1. - -EXAMPLES -drcorners n=3 R' D R2 D' R' U2 R D R' U2 R' D' R - Produces the following output: -Found 3 results. -@1: U' F2 U R2 U2 F2 U F2 U R2 (10) -@2: U' F2 U R2 U2 B2 U R2 D R2 (10) -@3: U' F2 U R2 U2 B2 U L2 U L2 (10) - diff --git a/docs/drfinish.txt b/docs/drfinish.txt @@ -1,29 +0,0 @@ - -HELP PAGE FOR COMMAND drfinish - -SYNTAX -drfinish [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Solves the given scramble using the DR moveset. DR must be solved. The scramble -can be given as the last argument of the command, or it may be given as an $ID -or @ID, or it can be typed out on the following line. - -OPTIONS -from {ud|fb|rl} Allows to specify on which axis the DR is solved. It is - usually not necessary, since nissy will find a DR on any - axis, but it can be useful if one want to e.g. solve an HTR - state allowing quarter-turns from a specific DR. -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -n=N Specify a maximum number of solutions to be output. N must - be a number. - Default value: 1. - -EXAMPLES -dr from ud R L' U2 R' L F2 - Solves the given scramble using the moveset <U,D,R2,L2,F2,B2>. In this case: -@1: U2 R2 F2 R2 U2 R2 F2 R2 (8) -drfinish b=7 n=10 $1 - Finds (at most) 10 solutions of length at most 7 for the scramble $1. - diff --git a/docs/eo.txt b/docs/eo.txt @@ -1,38 +0,0 @@ - -HELP PAGE FOR COMMAND eo - -SYNTAX -eo [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Solves EO for a given scramble. A scramble can be given as last argument of the -command, or an ID of a saved scramble can be provided. If none of the two is -given, a prompt will ask the user to input a new scramble. - -OPTIONS -axis={fb,rl,ud} Specify the axis for the EO. One to three axes can be given, - comma separated, no spaces. - Default: EO on any of the three axis (omitting the option is - the same as specifying axis=fb,rl,ud). -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -h Show hidden EOs. - Default, if an EO ending in e.g. F is shown, the equivalent - one ending in F' is hidden. -niss Use NISS. - Default: does not use NISS. -n=N Specify a maximum number of EOs to be output. N must be a - number. - Default value: 1. - -EXAMPLES -eo axis=fb $1 - Finds one optimal EO on fb for the first saved scramble. - -eo n=5 b=4 U R F - Finds up to 5 EOs of length at most 4 for scramble U R F. - -eo n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F - Finds up to 100 EOs of lenth at most 4, possibly using NISS, including - "hidden" EOs, excluding the rl axis. - diff --git a/docs/exit.txt b/docs/exit.txt @@ -1,9 +0,0 @@ - -HELP PAGE FOR COMMAND exit - -SYNTAX -exit - -DESCRIPTION -Exits nissy. - diff --git a/docs/help.txt b/docs/help.txt @@ -1,15 +0,0 @@ - -HELP PAGE FOR COMMAND help - -SYNTAX -help [nissy|COMMAND] - -DESCRIPTION -'help nissy' prints a general user manual. 'help COMMAND' prints a detailed -help page for the command COMMAND, if it exists. 'help' prints a list of all -available commands a short description for each. - -EXAMPLES -help help - Prints this help page. - diff --git a/docs/htr.txt b/docs/htr.txt @@ -1,30 +0,0 @@ - -HELP PAGE FOR COMMAND htr - -SYNTAX -htr [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Finds HTR for a given scramble. DR must be solved. A scramble can be given as -last argument of the command, or an ID of a saved scramble can be provided. If -none of the two is given, a prompt will ask the user to input a new scramble. - -OPTIONS -from {ud|fb|rl} Allows to specify on which axis the DR is. This is usually - not needed, since nissy will automatically find it out. -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -h Show hidden HTRs. - Default, if an HTR ending in e.g. R is shown, the equivalent - one ending in R' is hidden. -niss Use NISS. - Default: does not use NISS. -n=N Specify a maximum number of HTRs to be output. N must be a - number. - Default value: 1. - -EXAMPLES -eo n=10 b=7 niss $1 - Finds up to 100 HTRs of lenth at most 7, possibly using NISS, including - "hidden" HTRs, for scramble $1. DR must be solved. - diff --git a/docs/htrfinish.txt b/docs/htrfinish.txt @@ -1,23 +0,0 @@ - -HELP PAGE FOR COMMAND htrfinish - -SYNTAX -htrfinish [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Similar to drfinish, but uses the moveset <U2,D2,R2,L2,F2,B2>. HTR must be -solved. The scramble can be given as the last argument of the command, or it -may be given as an $ID or @ID, or it can be typed out on the following line. - -OPTIONS -b=N Specify a bound for the number of moves. N must be a number. - Default value: 20. -n=N Specify a maximum number ofsolutions to be output. N must be - a number. - Default value: 1. - -EXAMPLES -htrfinish R L' U2 R' L F2 - Produces the following solution: -@1: U2 R2 F2 R2 U2 R2 F2 R2 (8) - diff --git a/docs/invert.txt b/docs/invert.txt @@ -1,14 +0,0 @@ - -HELP PAGE FOR COMMAND invert - -SYNTAX -invert [MOVES|$ID|@ID] - -DESCRIPTION -Inverts a sequence of moves, which can be given also as $ID or @ID. The given -sequence must not use NISS (if it does, use the command unniss first). - -EXAMPLES -invert F R D' - Prints D R' F' - diff --git a/docs/nissy.txt b/docs/nissy.txt @@ -1,88 +0,0 @@ - -******************************************************************************* -********************* NISSY: a cube solver and FMC helper ********************* -******************************************************************************* - -If you just want to solve the cube, type 'solve' followed by the scramble. This -will not always give you an optimal solution, unless it is 10 moves or less or -you use the "o" option. Finding the optimal solution might take very long if it -is 16 moves or more, especially for the first time. - -Now the fun stuff. With nissy you can save and manipulate move sequences, for -example: - -nissy-# save R' U' F -$1: R' U' F -nissy-# add L2D' $1 -$1: R' U' F L2 D' - -You can then ask nissy to solve certain substepson a saved scramble: - -nissy-# eo axis=rl $1 -@1: U D F' R (4) - -And of course it uses also NISS, if you ask: - -nissy-# eo niss axis=rl $1 -@1: (R) (1) - -Notice that the sequences you save are marked with a $, while the "output" -sequences are marked with @. The difference between these two type of sequences -is that those marked with @ are temporary and get lost once you get new output. -Most commands accept as input either a move sequence typed out, a $-sequence or -a @-sequence. For example, you can however save a @-sequence and make it -persistent: - -nissy-# save @1 -$2: (R) - -Nissy also understands NISS. Let's see a more complicated example where you -save a scramble, ask for some EOs (using NISS) and then a DR on inverse: - -nissy-# save R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F -nissy-# eo n=10 niss axis=fb,rl $3 -Found 10 results. -@1: (U' L B D F) (5) -@2: (U' L B' D F) (5) -@3: (L B U D F) (5) -@4: (L B' U D F) (5) -@5: R U B U L (5) -@6: R U' L (B L) (5) -@7: R U' B U L (5) -@8: R L (L B L) (5) -@9: R (U2 D' F R) (5) -@10: R (U2 F D' R) (5) -nissy-# add @6 $3 -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) -nissy-# unniss $3 -@1: L' B' R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L -nissy-# invert @1 -@1: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L -nissy-# save @1 -$5: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L -nissy-# dr from rl $5 -@1: F2 U D2 F' B D B (7) -nissy-# - -If you ask nissy to solve a substep (or the whole cube) using a sequence with -NISS as scramble, it will first un-NISS it (but without saving the unNISSed -scramble anywhere): - -print $3 -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) -nissy-# solve $3 -@1: F U' R2 F2 U2 F U2 R2 L2 D R2 U B2 D' L2 D B2 D2 (18) - -Nissy knows how to solve certain common sub-steps for DR (or Thistlethwaite / -Kociemba algorithms). For now it does know more common speedsolving methods. - -For a full list of commands type "help". For a more detailed help on a specific -command, type "help (command)". The help pages can also be found in the docs -folder. - -If you want to report a bug (I'm sure there are many!) or give a suggestion, -you can send an email to sebastiano.tronto@gmail.com. - -Have fun! - diff --git a/docs/pic.txt b/docs/pic.txt @@ -1,20 +0,0 @@ - -HELP PAGE FOR COMMAND pic - -SYNTAX -pic [MOVES|$ID|@ID] - -DESCRIPTION -Prints the cube state after applying the given scramble. - -EXAMPLES -pic R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F - Gives the following output: - UF UL UB UR DF DL DB DR FR FL BL BR -EP: FR UL FL UR UF DB DR BL UB BR DL DF -EO(F/B): x x x x x x x x - - UFR UFL UBL UBR DFR DFL DBL DBR -CP: UBR UFR DFL DBL UFL UBL DBR DFR -CO(U/D): ccw cw ccw cw - diff --git a/docs/print.txt b/docs/print.txt @@ -1,19 +0,0 @@ - -HELP PAGE FOR COMMAND print - -SYNTAX -print [$ID|@ID] - -DESCRIPTION -Prints memorized sequences. If no argument is given, it prints all memorized -scrambles ($ only). If $ID or @ID is specified, it only prints the relative -memorized sequence. - -EXAMPLES -print - Prints a list of all memorized scrambles (only $). -print $2 - Prints the second memorized scramble. -print @13 - Prints the 13th sequence that was part of the output of the last command. - diff --git a/docs/quit.txt b/docs/quit.txt @@ -1,9 +0,0 @@ - -HELP PAGE FOR COMMAND quit - -SYNTAX -quit - -DESCRIPTION -Exits nissy. - diff --git a/docs/replace.txt b/docs/replace.txt @@ -1,22 +0,0 @@ - -HELP PAGE FOR COMMAND replace - -SYNTAX -eo [OPTIONS] [MOVES|$ID|@ID] - -DESCRIPTION -Looks for non-optimal subsequences and replaces them to shorten the given -sequence. By default it tries to shorten every subsequence of up to 10 moves, -but this can be change with the "b" option. -It outputs at most 10 equivalent optimal sequences for each replaceable part. - -OPTIONS -b=N Finds non-optimal subsequences of up to N moves. - -EXAMPLES -replace D2 F' D2 U2 F' L2 R2 U' D B2 D B2 U B2 F L2 R' F' D U' - Produces the following output: -Replace [ R2 U' D B2 D B2 ] (moves 7-12) with: [ D R2 D U' ] (-6+4) -Replace [ R2 U' D B2 D B2 U ] (moves 7-13) with: [ D R2 D ] (-7+3) -Replace [ U' D B2 D B2 U ] (moves 8-13) with: [ R2 D R2 D ] (-6+4) - diff --git a/docs/save.txt b/docs/save.txt @@ -1,22 +0,0 @@ - -HELP PAGE FOR COMMAND save - -SYNTAX -save [MOVES|@ID|$ID] - -DESCRIPTION -Memorizes the scramble specified by MOVES, given as input or temporarily saved -as @ID, where ID is a number ('help nissy' for for more on IDs). If an $ID is -given, it makes a copy of the scramble. An identifier of the form $ID, where ID -is a number, is assigned to the memorized scramble. - -EXAMPLES -save R U R' U' - Saves the scramble R U R' U'. -save F (B) - Saves the scramble F (B) (NISS notation). -save @3 - Saves the third output sequence of the last command. -save $2 - Makes a copy of the second saved scramble. - diff --git a/docs/scramble.txt b/docs/scramble.txt @@ -1,22 +0,0 @@ - -HELP PAGE FOR COMMAND scramble - -SYNTAX -scramble [OPTIONS] - -DESCRIPTION -Produces a random-state scramble. There are options to get a corners-only, -edges-only or dr-state scramble. - -OPTIONS -c Scrambles corners only (edges are solved). -e Scrambles edges only (corners are solved). -dr DR-state scramble. The DR is always on the U/D axis. - - -EXAMPLES -scramble - Gives a random-state scramble -scramble dr - Gives a random-DR-state scramble - diff --git a/docs/solve.txt b/docs/solve.txt @@ -1,33 +0,0 @@ - -HELP PAGE FOR COMMAND solve - -SYNTAX -solve [MOVES|$ID|@ID] - -DESCRIPTION -Solves the given scramble, which can be given as a sequence of moves or as $ID -or @ID. If none is given, the user can type it on the next line. -The algorithm first tries to find a short (<=10 moves) solution, and then -switches to a 2-step algorithm (unless the option "o" is specified, in which -case it keeps looking for an optimal solution). -The first time it uses the 2-step algorithm it needs to load some tables, which -can take a few seconds. It runs much faster after that. If the option "o" is -specified, the first time it loads some large tables, which can take a minute -or two. - -OPTIONS -b=N Only looks for solutions up to N moves. -n=N Tries to find multiple solutions, at most N. Multiple - Solutions will only be found if they are <=10 moves. -o Looks for optimal solution. - - -EXAMPLES -solve R' U' F - Solves the scramble R' U' F. -solve o b=14 $1 - Tries to solve the scramble $1 optimally, but stops if no solution of 14 - moves or shorter is found. -solve n=16 R L' U2 R' L F2 - Finds the 16 shortest solutions for the scramble above. - diff --git a/docs/unniss.txt b/docs/unniss.txt @@ -1,14 +0,0 @@ - -HELP PAGE FOR COMMAND unniss - -SYNTAX -unniss [MOVES|$ID|@ID] - -DESCRIPTION -Removes NISS from a sequence of moves, which can be given also as $ID or @ID. -A sequence of the form A (B) is translated to B' A. - -EXAMPLES -invert F R (D' L2) - Prints L2 D F R - diff --git a/makedoc.sh b/makedoc.sh @@ -1,29 +0,0 @@ -#!/bin/sh - -OUT=./src/helppages.h -DOCDIR=./docs -N=$(ls -1 $DOCDIR | wc -l) - -# This deletes the content of the file -echo "/* To generate this help page, use the script makedoc.sh */" > $OUT - -echo "" >> $OUT -echo "int Npages = $N;" >> $OUT -echo "" >> $OUT -echo "char *helppages[][10] = {" >> $OUT - -for f in $(ls $DOCDIR) -do - name=$(echo "$f" | sed 's/\..*//') - echo "" >> $OUT - echo "{ \"$name\"," >> $OUT - - echo "\"\\" >> $OUT - sed 's/$/\\n\\/;s/\"/\\\"/g' $DOCDIR/$f >> $OUT - echo "\"" >> $OUT - - echo "}," >> $OUT -done - -echo "};" >> $OUT - diff --git a/nissy b/nissy Binary files differ. diff --git a/nissy-2.0beta1.tar.gz b/nissy-2.0beta1.tar.gz Binary files differ. diff --git a/nissy-win.exe b/nissy-win.exe Binary files differ. diff --git a/old/2020-unknown-date/cube_backup.c b/old/2020-unknown-date/cube_backup.c @@ -0,0 +1,527 @@ +#include <stdio.h> +#include <string.h> +#include "cube.h" + +/* The next few functions are used to convert from the Cube structure + * representation to actual arrays of pieces. */ +void cube_to_ep_array(Cube cube, int ep[12]); +void cube_to_eofb_array(Cube cube, int eo[12]); +void cube_to_eorl_array(Cube cube, int eo[12]); +void cube_to_eoud_array(Cube cube, int eo[12]); +void cube_to_cp_array(Cube cube, int cp[8]); +void cube_to_coud_array(Cube cube, int co[8]); +void cube_to_cofb_array(Cube cube, int co[8]); +void cube_to_corl_array(Cube cube, int co[8]); +void cube_to_centerpos_array(Cube cube, int centerpos[6]); +Cube ep_array_to_cube(int ep[12]); +Cube eofb_array_to_cube(int eo[12]); +Cube eorl_array_to_cube(int eo[12]); +Cube eoud_array_to_cube(int eo[12]); +Cube cp_array_to_cube(int cp[8]); +Cube coud_array_to_cube(int co[8]); +Cube cofb_array_to_cube(int co[8]); +Cube corl_array_to_cube(int co[8]); +Cube centerpos_array_to_cube(int centerpos[6]); +Cube move_array(Cube cube, void (cube_to_arr)(Cube, int *), + Cube (*arr_to_cube)(int *), + int *perm, int *orient, int n, int m); + + +/* Transition tables */ +int epose_ttable[NMOVES][factorial12/factorial8]; +int eposs_ttable[NMOVES][factorial12/factorial8]; +int eposm_ttable[NMOVES][factorial12/factorial8]; +int eofb_ttable[NMOVES][pow2to11]; +int eorl_ttable[NMOVES][pow2to11]; +int eoud_ttable[NMOVES][pow2to11]; +int cp_ttable[NMOVES][factorial8]; +int coud_ttable[NMOVES][pow3to7]; +int cofb_ttable[NMOVES][pow3to7]; +int corl_ttable[NMOVES][pow3to7]; +int centerpos_ttable[NMOVES][factorial6]; + +char edge_string[12][5] = { + "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" +}; +char corner_string[8][5] = { "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR" }; +char center_string[6][5] = { "U", "D", "R", "L", "F", "B" }; +char move_string[NMOVES][5] = { + "-", + "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", + "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'", + "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'", + "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'", +}; + +int epe_solved[] = {FR, FL, BL, BR}; +int eps_solved[] = {UL, UR, DL, DR}; +int epm_solved[] = {UF, UB, DF, DB}; + +/**************************/ +/* Internal use functions */ +/**************************/ + +int cube_to_ep_array(Cube cube, int ep[12]) { + int epe[4], eps[4], epm[4]; + index_to_perm(cube.epose % factorial(4), 4, epe); + index_to_perm(cube.eposs % factorial(4), 4, eps); + index_to_perm(cube.eposm % factorial(4), 4, epm); + + int epose[12], eposs[12], eposm[12]; + index_to_subset(cube.epose / factorial(4), 12, 4, epose); + index_to_subset(cube.eposs / factorial(4), 12, 4, eposs); + index_to_subset(cube.eposm / factorial(4), 12, 4, eposm); + for (int i = 0; i < 4; i++) { + swap(&eposs[eps_solved[i]], &eposs[i+8]); + swap(&eposm[epm_solved[i]], &eposm[i+8]); + } + + for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) { + if (epose[i]) ep[i] = epe_solved[epe[ie++]]; + if (eposs[i]) ep[i] = eps_solved[eps[is++]]; + if (eposm[i]) ep[i] = epm_solved[epm[im++]]; + } +} + +void cube_to_eofb_array(Cube cube, int eo[12]) { + int_to_sum_zero_array(cube.eofb, 2, 12, eo); +} + +void cube_to_eorl_array(Cube cube, int eo[12]) { + int_to_sum_zero_array(cube.eorl, 2, 12, eo); +} + +void cube_to_eoud_array(Cube cube, int eo[12]) { + int_to_sum_zero_array(cube.eoud, 2, 12, eo); +} + +void cube_to_cp_array(Cube cube, int cp[8]) { + index_to_perm(cube.cp, 8, cp); +} + +void cube_to_coud_array(Cube cube, int co[8]) { + int_to_sum_zero_array(cube.coud, 3, 8, co); +} + +void cube_to_cofb_array(Cube cube, int co[8]) { + int_to_sum_zero_array(cube.cofb, 3, 8, co); +} + +void cube_to_corl_array(Cube cube, int co[8]) { + int_to_sum_zero_array(cube.corl, 3, 8, co); +} + +void cube_to_centerpos_array(Cube cube, int centerpos[6]) { + index_to_perm(cube.centerpos, 6, centerpos); +} + +Cube ep_array_to_cube(int ep[12]) { + int epe[4], eps[4], epm[4]; + int epose[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eposs[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eposm[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) { + for (int j = 0; j < 4; j++) { + if (ep[i] == epe_solved[j]) { epe[ie++] = j; epose[i] = 1; } + if (ep[i] == eps_solved[j]) { eps[is++] = j; eposs[i] = 1; } + if (ep[i] == epm_solved[j]) { epm[im++] = j; eposm[i] = 1; } + } + } + for (int i = 0; i < 4; i++) { + swap(&eposs[eps_solved[i]], &eposs[i+8]); + swap(&eposm[epm_solved[i]], &eposm[i+8]); + } + return { .epose = factorial(4) * subset_to_index(epose, 12, 4) + + perm_to_index(epe, 4), + .eposs = factorial(4) * subset_to_index(eposs, 12, 4) + + perm_to_index(eps, 4), + .eposm = factorial(4) * subset_to_index(eposm, 12, 4) + + perm_to_index(epm, 4) }; +} + +Cube eofb_array_to_cube(int eo[12]) { + return { .eofb = digit_array_to_int(eo, 11, 2) }; +} + +Cube eorl_array_to_cube(int eo[12]) { + return { .eorl = digit_array_to_int(eo, 11, 2) }; +} + +Cube eoud_array_to_cube(int eo[12]) { + return { .eoud = digit_array_to_int(eo, 11, 2) }; +} + +Cube cp_array_to_cube(int cp[8]) { + return { .cp = perm_to_index(cp, 8) }; +} + +Cube coud_array_to_cube(int co[8]) { + return { .coud = digit_array_to_int(co, 7, 3) }; +} + +Cube cofb_array_to_cube(int co[8]) { + return { .cofb = digit_array_to_int(co, 7, 3) }; +} + +Cube corl_array_to_cube(int co[8]) { + return { .corl = digit_array_to_int(co, 7, 3) }; +} + +Cube centerpos_array_to_cube(int centerpos[6]) { + return { .centerpos = perm_to_index(centerpos, 6) }; +} + +Cube move_array(Cube cube, void (*cube_to_arr)(Cube, int *), + void (*arr_to_cube)(int *), + int *perm, int *orient, int n, int m) { + int arr[n]; + cube_to_arr(cube, arr); + apply_permutation(perm, arr, n); + sum_arrays_mod(arr, orient, n, m); + return arr_to_cube(arr); +} + +/***********************/ +/* Interface functions */ +/***********************/ + +Move inverse(Move m) { + if (m == NULLMOVE) + return m; + int mod = (m-1)%3; + Move base = m - mod; + return base + 2 - mod; +} + +void copy_alg(NissMove *src, NissMove *dest) { + for (int i = 0; src[i].m != NULLMOVE; i++) + dest[i] = src[i]; +} + +bool commute(Move m1, Move m2) { + return equal(apply_move(m2, apply_move(m1, {0})), + apply_move(m1, apply_move(m2, {0}))); +} + +bool equal(Cube c1, Cube c2) { + return c1.eofb == c2.eofb && c1.epose == c2.epose && + c1.eposs == c2.eposs && c1.eposm == c2.eposm && + c1.coud == c2.coud && c1.cp == c2.cp && + c1.centerpos == c2.centerpos; +} + +bool solvable(Cube cube) { + /* Since we memorize orientation truncating the last digit, we only need to + * check that the permutations have the correct sign. */ + /* TODO: add check that every integer is in range */ + int ep[12], cp[12], c[6]; + /* TODO: change to use cube-specific functions */ + cube_to_ep_array(cube, ep); + cube_to_cp_array(cube, 8, cp); + cube_to_centerpos_array(cube, 6, c); + return (perm_sign(ep, 12) ^ perm_sign(c, 6)) == perm_sign(cp, 8); +} + +bool is_solved(Cube cube) { + return (!cube.eofb && !cube.coud && !cube.cp && + !cube.epose && !cube.eposs && !cube.eposm && !cube.centerpos); +} + +char *to_string(Cube cube) { + int eo[12], co[8], ep[12], cp[8], cenpos[6]; + cube_to_eofb_array(cube, eo); + cube_to_coud_array(cube, co); + cube_to_ep_array(cube, ep); + cube_to_cp_array(cube, cp); + cube_to_centerpos_array(cube, cenpos); + + static char ret[1000]; + + for (int i = 0; i < 12; i++) { + strcat(ret, " "); + strcat(ret, edge_string[ep[i]]); + strcat(ret, " "); + } + strcat(ret, "\n"); + for (int i = 0; i < 12; i++) { + strcat(ret, " "); + char num[2] = { [0] = eo[i] + '0', [1] = 0 }; + strcat(ret, num); + strcat(ret, " "); + } + strcat(ret, "\n"); + for (int i = 0; i < 8; i++) { + strcat(ret, corner_string[cp[i]]); + strcat(ret, " "); + } + strcat(ret, "\n"); + for (int i = 0; i < 8; i++) { + strcat(ret, " "); + char num[2] = { [0] = co[i] + '0', [1] = 0 }; + strcat(ret, num); + strcat(ret, " "); + } + strcat(ret, "\n"); + for (int i = 0; i < 6; i++) { + strcat(ret, " "); + strcat(ret, center_string[cenpos[i]]); + strcat(ret, " "); + } + strcat(ret, "\n"); + + return ret; +} + +void init_ttables() { + FILE *ttf; + + if ((ttf = fopen("ttables", "rb")) != NULL) { + for (int m = 0; m < NMOVES; m++) { + fread(epose_ttable[m], sizeof(int), factorial12/factorial8, ttf); + fread(eposs_ttable[m], sizeof(int), factorial12/factorial8, ttf); + fread(eposm_ttable[m], sizeof(int), factorial12/factorial8, ttf); + fread(eofb_ttable[m], sizeof(int), pow2to11, ttf); + fread(eorl_ttable[m], sizeof(int), pow2to11, ttf); + fread(eoud_ttable[m], sizeof(int), pow2to11, ttf); + fread(cp_ttable[m], sizeof(int), factorial8, ttf); + fread(coud_ttable[m], sizeof(int), pow3to7, ttf); + fread(corl_ttable[m], sizeof(int), pow3to7, ttf); + fread(cofb_ttable[m], sizeof(int), pow3to7, ttf); + fread(centerpos_ttable[m], sizeof(int), factorial6, ttf); + } + fclose(ttf); + } else { + ttf = fopen("ttables", "wb"); + int empty[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + /* For each type of pieces only the effects of U, x and y are described */ + int edge_cycle[NMOVES][12] = { + [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, + [X] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR}, + [Y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL}, + }; + int eofb_flipped[NMOVES][12] = { + [X] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [Y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }, + }; + int eorl_flipped[NMOVES][12] = { + [X] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [Y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }, + }; + int eoud_flipped[NMOVES][12] = { + [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [X] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [Y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + }; + int corner_cycle[NMOVES][8] = { + [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [X] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [Y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL}, + }; + int coud_flipped[NMOVES][8] = { + [X] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2}, + }; + int corl_flipped[NMOVES][8] = { + [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [Y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1}, + }; + int cofb_flipped[NMOVES][8] = { + [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [X] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=2,[UFL]=1,[DBL]=1,[DFL]=2}, + [Y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2}, + }; + int center_cycle[NMOVES][6] = { + [X] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [Y] = {U_center, D_center, B_center, F_center, R_center, L_center}, + }; + + /* Each move is reduced to a combination of U, x and y using this table */ + Move equiv_moves[NMOVES][13] = { + [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D] = { X, X, U, X, X, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D2] = { X, X, U, U, X, X, 0, 0, 0, 0, 0, 0, 0 }, + [D3] = { X, X, U, U, U, X, X, 0, 0, 0, 0, 0, 0 }, + [R] = { Y, X, U, X, X, X, Y, Y, Y, 0, 0, 0, 0 }, + [R2] = { Y, X, U, U, X, X, X, Y, Y, Y, 0, 0, 0 }, + [R3] = { Y, X, U, U, U, X, X, X, Y, Y, Y, 0, 0 }, + [L] = { Y, Y, Y, X, U, X, X, X, Y, 0, 0, 0, 0 }, + [L2] = { Y, Y, Y, X, U, U, X, X, X, Y, 0, 0, 0 }, + [L3] = { Y, Y, Y, X, U, U, U, X, X, X, Y, 0, 0 }, + [F] = { X, U, X, X, X, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F2] = { X, U, U, X, X, X, 0, 0, 0, 0, 0, 0, 0 }, + [F3] = { X, U, U, U, X, X, X, 0, 0, 0, 0, 0, 0 }, + [B] = { X, X, X, U, X, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B2] = { X, X, X, U, U, X, 0, 0, 0, 0, 0, 0, 0 }, + [B3] = { X, X, X, U, U, U, X, 0, 0, 0, 0, 0, 0 }, + + [Uw] = { X, X, U, X, X, Y, 0, 0, 0, 0, 0, 0, 0 }, + [Uw2] = { X, X, U, U, X, X, Y, Y, 0, 0, 0, 0, 0 }, + [Uw3] = { X, X, U, U, U, X, X, Y, Y, Y, 0, 0, 0 }, + [Dw] = { U, Y, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw2] = { U, U, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw3] = { U, U, U, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Rw] = { Y, Y, Y, X, U, X, X, X, Y, X, 0, 0, 0 }, + [Rw2] = { Y, Y, Y, X, U, U, X, X, X, Y, X, X, 0 }, + [Rw3] = { Y, Y, Y, X, U, U, U, Y, X, X, X, Y, 0 }, + [Lw] = { Y, X, U, X, X, X, Y, Y, Y, X, X, X, 0 }, + [Lw2] = { Y, X, U, U, X, X, X, Y, Y, Y, X, X, 0 }, + [Lw3] = { Y, X, U, U, U, X, X, X, Y, Y, Y, X, 0 }, + [Fw] = { X, X, X, U, Y, Y, Y, X, 0, 0, 0, 0, 0 }, + [Fw2] = { X, X, X, U, U, Y, Y, X, 0, 0, 0, 0, 0 }, + [Fw3] = { X, X, X, U, U, U, Y, X, 0, 0, 0, 0, 0 }, + [Bw] = { X, U, Y, Y, Y, X, X, X, 0, 0, 0, 0, 0 }, + [Bw2] = { X, U, U, Y, Y, X, X, X, 0, 0, 0, 0, 0 }, + [Bw3] = { X, U, U, U, Y, X, X, X, 0, 0, 0, 0, 0 }, + + [M] = { Y, X, U, X, X, U, U, U, Y, X, Y, Y, Y }, + [M2] = { Y, X, U, U, X, X, U, U, X, X, X, Y, 0 }, + [M3] = { Y, X, U, U, U, X, X, U, Y, X, X, X, Y }, + [S] = { X, U, U, U, X, X, U, Y, Y, Y, X, 0, 0 }, + [S2] = { X, U, U, X, X, U, U, Y, Y, X, 0, 0, 0 }, + [S3] = { X, U, X, X, U, U, U, Y, X, 0, 0, 0, 0 }, + [E] = { U, X, X, U, U, U, X, X, Y, Y, Y, 0, 0 }, + [E2] = { U, U, X, X, U, U, X, X, Y, Y, 0, 0, 0 }, + [E3] = { U, U, U, X, X, U, X, X, Y, 0, 0, 0, 0 }, + + [X] = { X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [X2] = { X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [X3] = { X, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Y] = { Y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Y2] = { Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Y3] = { Y, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Z] = { Y, Y, Y, X, Y, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Z2] = { Y, Y, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Z3] = { Y, X, Y, Y, Y, 0, 0, 0, 0, 0, 0, 0, 0 }, + }; + + /* Generate all move cycles and flips */ + for (int i = 0; i < NMOVES; i++) { + if (i == U || i == X || i == Y) + continue; + + Cube cube = {0}; + + cube_to_ep_array(&cube, edge_cycle[i]); + cube_to_eofb_array(&cube, eofb_flipped[i]); + cube_to_eorl_array(&cube, eorl_flipped[i]); + cube_to_eoud_array(&cube, eoud_flipped[i]); + cube_to_cp_array(&cube, corner_cycle[i]); + cube_to_coud_array(&cube, coud_flipped[i]); + cube_to_cofb_array(&cube, cofb_flipped[i]); + cube_to_corl_array(&cube, corl_flipped[i]); + cube_to_centerpos_array(&cube, center_cycle[i]); + + for (int j = 0; equiv_moves[i][j]; j++) { + int m = equiv_moves[i][j]; + + apply_permutation(edge_cycle[m], edge_cycle[i], 12); + apply_permutation(edge_cycle[m], eofb_flipped[i], 12); + apply_permutation(edge_cycle[m], eorl_flipped[i], 12); + apply_permutation(edge_cycle[m], eoud_flipped[i], 12); + + sum_arrays_mod(eofb_flipped[i], eofb_flipped[m], 12, 2); + sum_arrays_mod(eorl_flipped[i], eorl_flipped[m], 12, 2); + sum_arrays_mod(eoud_flipped[i], eoud_flipped[m], 12, 2); + + apply_permutation(corner_cycle[m], corner_cycle[i], 8); + apply_permutation(corner_cycle[m], coud_flipped[i], 8); + apply_permutation(corner_cycle[m], cofb_flipped[i], 8); + apply_permutation(corner_cycle[m], corl_flipped[i], 8); + + sum_arrays_mod(coud_flipped[i], coud_flipped[m], 8, 3); + sum_arrays_mod(corl_flipped[i], corl_flipped[m], 8, 3); + sum_arrays_mod(cofb_flipped[i], cofb_flipped[m], 8, 3); + + apply_permutation(center_cycle[m], center_cycle[i], 6); + } + } + + /* Initialize transition tables */ + for (int m = 0; m < NMOVES; m++) { + for (int i = 0; i < factorial12/factorial8; i++) { + Cube cube = { .epose = i }; + move_array(&cube, cube_to_ep_array, ep_array_to_cube, + edge_cycle[m], empty, 12, 1); + epose_ttable[m][i] = cube.epose; + cube.eposs = i; + move_array(&cube, cube_to_ep_array, ep_array_to_cube, + edge_cycle[m], empty, 12, 1); + eposs_ttable[m][i] = cube.eposs; + cube.eposm = i; + move_array(&cube, cube_to_ep_array, ep_array_to_cube, + edge_cycle[m], empty, 12, 1); + eposm_ttable[m][i] = cube.eposm; + } + for (int i = 0; i < pow2to11; i++ ) { + Cube cube = { .eofb = i, .eorl = i, .eoud = i }; + move_array(&cube, cube_to_eofb_array, eofb_array_to_cube, + edge_cycle[m], eofb_flipped[m], 12, 2); + move_array(&cube, cube_to_eorl_array, eorl_array_to_cube, + edge_cycle[m], eorl_flipped[m], 12, 2); + move_array(&cube, cube_to_eoud_array, eoud_array_to_cube, + edge_cycle[m], eoud_flipped[m], 12, 2); + eofb_ttable[m][i] = cube.eofb; + eorl_ttable[m][i] = cube.eorl; + eoud_ttable[m][i] = cube.eoud; + } + for (int i = 0; i < factorial8; i++) { + /* TODO: Maybe do this by hand to optimize? */ + Cube cube = { .cp = i }; + move_array(&cube, cube_to_cp_array, ep_array_to_cube, + corner_cycle[m], empty, 8, 1); + cp_ttable[m][i] = cube.cp; + } + for (int i = 0; i < pow3to7; i++) { + Cube cube = { .coud = i, .corl = i, .cofb = i }; + move_array(&cube, cube_to_coud_array, coud_array_to_cube, + corner_cycle[m], coud_flipped[m], 8, 3); + move_array(&cube, cube_to_corl_array, corl_array_to_cube, + corner_cycle[m], corl_flipped[m], 8, 3); + move_array(&cube, cube_to_cofb_array, cofb_array_to_cube, + corner_cycle[m], cofb_flipped[m], 8, 3); + coud_ttable[m][i] = cube.coud; + corl_ttable[m][i] = cube.corl; + cofb_ttable[m][i] = cube.cofb; + } + for (int i = 0; i < factorial6; i++) { + Cube cube = { .centerpos = i }; + move_array(&cube, cube_to_centerpos_array, centerpos_array_to_cube, + center_cycle[m], empty, 6, 1); + centerpos_ttable[m][i] = cube.centerpos; + } + fwrite(epose_ttable[m], sizeof(int), factorial12/factorial8, ttf); + fwrite(eposs_ttable[m], sizeof(int), factorial12/factorial8, ttf); + fwrite(eposm_ttable[m], sizeof(int), factorial12/factorial8, ttf); + fwrite(eofb_ttable[m], sizeof(int), pow2to11, ttf); + fwrite(eorl_ttable[m], sizeof(int), pow2to11, ttf); + fwrite(eoud_ttable[m], sizeof(int), pow2to11, ttf); + fwrite(cp_ttable[m], sizeof(int), factorial8, ttf); + fwrite(coud_ttable[m], sizeof(int), pow3to7, ttf); + fwrite(corl_ttable[m], sizeof(int), pow3to7, ttf); + fwrite(cofb_ttable[m], sizeof(int), pow3to7, ttf); + fwrite(centerpos_ttable[m], sizeof(int), factorial6, ttf); + } + fclose(ttf); + } +} + +Cube apply_move(Move m, Cube cube) { + Cube moved = cube; + + moved.eofb = eofb_ttable[m][cube.eofb]; + moved.eorl = eorl_ttable[m][cube.eorl]; + moved.eoud = eoud_ttable[m][cube.eoud]; + moved.coud = coud_ttable[m][cube.coud]; + moved.cofb = cofb_ttable[m][cube.cofb]; + moved.corl = corl_ttable[m][cube.corl]; + moved.epose = epose_ttable[m][cube.epose]; + moved.eposs = eposs_ttable[m][cube.eposs]; + moved.eposm = eposm_ttable[m][cube.eposm]; + moved.cp = cp_ttable[m][cube.cp]; + moved.centerpos = centerpos_ttable[m][cube.centerpos]; + + return moved; +} diff --git a/old/2020-unknown-date/cubeutils.c b/old/2020-unknown-date/cubeutils.c @@ -0,0 +1,100 @@ +#include <stdio.h> +#include "utils.h" +#include "pieces.h" +#include "cubeutils.h" + +int epe_solved[] = {FR, FL, BL, BR}; +int eps_solved[] = {UL, UR, DL, DR}; +int epm_solved[] = {UF, UB, DF, DB}; + +void cube_to_ep_array(Cube *cube, int ep[12]) { + int epe[4], eps[4], epm[4]; + index_to_perm(cube->epose % factorial(4), 4, epe); + index_to_perm(cube->eposs % factorial(4), 4, eps); + index_to_perm(cube->eposm % factorial(4), 4, epm); + + int epose[12], eposs[12], eposm[12]; + index_to_subset(cube->epose / factorial(4), 12, 4, epose); + index_to_subset(cube->eposs / factorial(4), 12, 4, eposs); + index_to_subset(cube->eposm / factorial(4), 12, 4, eposm); + for (int i = 0; i < 4; i++) { + swap(&eposs[eps_solved[i]], &eposs[i+8]); + swap(&eposm[epm_solved[i]], &eposm[i+8]); + } + + for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) { + if (epose[i]) ep[i] = epe_solved[epe[ie++]]; + if (eposs[i]) ep[i] = eps_solved[eps[is++]]; + if (eposm[i]) ep[i] = epm_solved[epm[im++]]; + } +} + +void ep_array_to_epos(int ep[12], Cube *cube) { + int epe[4], eps[4], epm[4]; + int epose[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eposs[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eposm[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + for (int i = 0, ie = 0, is = 0, im = 0; i < 12; i++) { + for (int j = 0; j < 4; j++) { + if (ep[i] == epe_solved[j]) { epe[ie++] = j; epose[i] = 1; } + if (ep[i] == eps_solved[j]) { eps[is++] = j; eposs[i] = 1; } + if (ep[i] == epm_solved[j]) { epm[im++] = j; eposm[i] = 1; } + } + } + for (int i = 0; i < 4; i++) { + swap(&eposs[eps_solved[i]], &eposs[i+8]); + swap(&eposm[epm_solved[i]], &eposm[i+8]); + } + cube->epose = factorial(4) * subset_to_index(epose, 12, 4) + + perm_to_index(epe, 4); + cube->eposs = factorial(4) * subset_to_index(eposs, 12, 4) + + perm_to_index(eps, 4); + cube->eposm = factorial(4) * subset_to_index(eposm, 12, 4) + + perm_to_index(epm, 4); +} + +bool solvable(Cube *cube) { + /* Since we memorize orientation truncating the last digit, we only need to + * check that the permutations have the correct sign. */ + /* TODO: add check that every integer is in range */ + int ep[12], cp[12], c[6]; + cube_to_ep_array(cube, ep); + index_to_perm(cube->cp, 8, cp); + index_to_perm(cube->centerpos, 6, c); + return (perm_sign(ep, 12) ^ perm_sign(c, 6)) == perm_sign(cp, 8); +} + +Cube *solved_cube() { + static Cube cube = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + return &cube; +} + +bool solved(Cube *cube) { + return (cube->eofb == 0 && cube->coud == 0 && cube->cp == 0 && + cube->epose == 0 && cube->eposs == 0 && cube->eposm == 0 && + cube->centerpos == 0); +} + +char *to_string(Cube *cube) { + int eo[12], co[8], ep[12], cp[8], cenpos[6]; + cube_to_eofb_array(cube->eofb, eo); + cube_to_coud_array(cube->coud, co); + cube_to_ep_array(cube, ep); + cube_to_cp_array(cube->cp, cp); + cube_to_centerpos_array(cube->centerpos, cenpos); + + char *ret[1000]; + + for (int i = 0; i < 12; i++) sprintf(ret, " %s ", edge_string[ep[i]]); + sprintf(ret, "\n"); + for (int i = 0; i < 12; i++) sprintf(ret, " %d ", eo[i]); + sprintf(ret, "\n"); + for (int i = 0; i < 8; i++) sprintf(ret, "%s ", corner_string[cp[i]]); + sprintf(ret, "\n"); + for (int i = 0; i < 8; i++) sprintf(ret, " %d ", co[i]); + sprintf(ret, "\n"); + for (int i = 0; i < 6; i++) sprintf(ret, " %s ", center_string[cenpos[i]]); + sprintf(ret, "\n"); + + return ret; +} diff --git a/old/2020-unknown-date/cubeutils.h b/old/2020-unknown-date/cubeutils.h @@ -0,0 +1,18 @@ +/* Cube-specific utility functions */ + +#ifndef CUBEUTILS_H +#define CUBEUTILS_H + +#include <stdbool.h> +#include "pieces.h" + +void cube_to_ep_array(Cube *cube, int ep[12]); +void ep_array_to_epos(int ep[12], Cube *cube); + +Cube *solved_cube(); +bool solvable(Cube *cube); +bool solved(Cube *cube); + +void print(Cube *cube); + +#endif diff --git a/old/2020-unknown-date/moves.c b/old/2020-unknown-date/moves.c @@ -0,0 +1,57 @@ +#include <stdbool.h> +#include "pieces.h" +#include "moves.h" +#include "utils.h" + +/* Transition tables */ +int eofb_ttable[pow2to11][NULLMOVE]; +int eorl_ttable[pow2to11][NULLMOVE]; +int eoud_ttable[pow2to11][NULLMOVE]; +int coud_ttable[pow3to7][NULLMOVE]; +int cofb_ttable[pow3to7][NULLMOVE]; +int corl_ttable[pow3to7][NULLMOVE]; +int epose_ttable[factorial12/factorial8][NULLMOVE]; +int eposs_ttable[factorial12/factorial8][NULLMOVE]; +int eposm_ttable[factorial12/factorial8][NULLMOVE]; +int cp_ttable[factorial8][NULLMOVE]; +int centerpos_ttable[factorial6][NULLMOVE]; + +void apply_move_ep(Move m, int a[12]) { + int aux[12]; + intarrcopy(a, aux, 12); + for (int i = 0; i < 12; i++) + a[i] = aux[edge_cycle[m][i]]; +} + +void apply_move_cp(Move m, int a[8]) { + int aux[8]; + intarrcopy(a, aux, 8); + for (int i = 0; i < 8; i++) + a[i] = aux[corner_cycle[m][i]]; +} + +void apply_move_centerpos(Move m, int a[6]) { + int aux[6]; + intarrcopy(a, aux, 6); + for (int i = 0; i < 6; i++) + a[i] = aux[center_cycle[m][i]]; +} + +void init_ttables() { + /* TODO */ +} + +void apply_move(Move m, Cube *cube) { + cube->eofb = eofb_ttable[cube->eofb][m]; + cube->eorl = eorl_ttable[cube->eorl][m]; + cube->eoud = eoud_ttable[cube->eoud][m]; + cube->coud = coud_ttable[cube->coud][m]; + cube->cofb = cofb_ttable[cube->cofb][m]; + cube->corl = corl_ttable[cube->corl][m]; + cube->epose = epose_ttable[cube->epose][m]; + cube->eposs = eposs_ttable[cube->eposs][m]; + cube->eposm = eposm_ttable[cube->eposm][m]; + cube->cp = cp_ttable[cube->cp][m]; + cube->centerpos = centerpos_ttable[cube->centerpos][m]; +} + diff --git a/old/2020-unknown-date/moves.h b/old/2020-unknown-date/moves.h @@ -0,0 +1,9 @@ +/* Moves and transition tables */ + +#ifndef MOVES_H +#define MOVES_H + +void apply_move(Move m, Cube *cube); +void init_ttables(); + +#endif diff --git a/old/2020-unknown-date/pieces.c b/old/2020-unknown-date/pieces.c @@ -0,0 +1,208 @@ +#include "pieces.h" + +char edge_string[12][5] = { + "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" +}; + +char corner_string[8][5] = { + "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR" +}; + +char center_string[6][5] = { "U", "D", "R", "L", "F", "B" }; + +char move_string[NULLMOVE+1][5] = { + "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", + "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'", + "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'", + "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'", + "-" +}; + +int edge_cycle[NULLMOVE+1][12] = { + [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, + [U2] = {UB, UR, UF, UL, DF, DL, DB, DR, FR, FL, BL, BR}, + [U3] = {UL, UB, UR, UF, DF, DL, DB, DR, FR, FL, BL, BR}, + [D] = {UF, UL, UB, UR, DL, DB, DR, DF, FR, FL, BL, BR}, + [D2] = {UF, UL, UB, UR, DB, DR, DF, DL, FR, FL, BL, BR}, + [D3] = {UF, UL, UB, UR, DR, DF, DL, DB, FR, FL, BL, BR}, + [R] = {UF, UL, UB, FR, DF, DL, DB, BR, DR, FL, BL, UR}, + [R2] = {UF, UL, UB, DR, DF, DL, DB, UR, BR, FL, BL, FR}, + [R3] = {UF, UL, UB, BR, DF, DL, DB, FR, UR, FL, BL, DR}, + [L] = {UF, BL, UB, UR, DF, FL, DB, DR, FR, UL, DL, BR}, + [L2] = {UF, DL, UB, UR, DF, UL, DB, DR, FR, BL, FL, BR}, + [L3] = {UF, FL, UB, UR, DF, BL, DB, DR, FR, DL, UL, BR}, + [F] = {FL, UL, UB, UR, FR, DL, DB, DR, UF, DF, BL, BR}, + [F2] = {DF, UL, UB, UR, UF, DL, DB, DR, FL, FR, BL, BR}, + [F3] = {FR, UL, UB, UR, FL, DL, DB, DR, DF, UF, BL, BR}, + [B] = {UF, UL, BR, UR, DF, DL, BL, DR, FR, FL, UB, DB}, + [B2] = {UF, UL, DB, UR, DF, DL, UB, DR, FR, FL, BR, BL}, + [B3] = {UF, UL, BL, UR, DF, DL, BR, DR, FR, FL, DB, UB}, + + [Uw] = {UR, UF, UL, UB, DF, DL, DB, DR, BR, FR, FL, BL}, + [Uw2] = {UB, UR, UF, UL, DF, DL, DB, DR, BL, BR, FR, FL}, + [Uw3] = {UL, UB, UR, UF, DF, DL, DB, DR, FL, BL, BR, FR}, + [Dw] = {UF, UL, UB, UR, DL, DB, DR, DF, FL, BL, BR, FR}, + [Dw2] = {UF, UL, UB, UR, DB, DR, DF, DL, BL, BR, FR, FL}, + [Dw3] = {UF, UL, UB, UR, DR, DF, DL, DB, BR, FR, FL, BL}, + [Rw] = {DF, UL, UF, FR, DB, DL, UB, BR, DR, FL, BL, UR}, + [Rw2] = {DB, UL, DF, DR, UB, DL, UF, UR, BR, FL, BL, FR}, + [Rw3] = {UB, UL, DB, BR, UF, DL, DF, FR, UR, FL, BL, DR}, + [Lw] = {UB, BL, DB, UR, UF, FL, DF, DR, FR, UL, DL, BR}, + [Lw2] = {DB, DL, DF, UR, UB, UL, UF, DR, FR, BL, FL, BR}, + [Lw3] = {DF, FL, UF, UR, DB, BL, UB, DR, FR, DL, UL, BR}, + [Fw] = {FL, DL, UB, UL, FR, DR, DB, UR, UF, DF, BL, BR}, + [Fw2] = {DF, DR, UB, DL, UF, UR, DB, UL, FL, FR, BL, BR}, + [Fw3] = {FR, UR, UB, DR, FL, UL, DB, DL, DF, UF, BL, BR}, + [Bw] = {UF, UR, BR, DR, DF, UL, BL, DL, FR, FL, UB, DB}, + [Bw2] = {UF, DR, DB, DL, DF, UR, UB, UL, FR, FL, BR, BL}, + [Bw3] = {UF, DL, BL, UL, DF, DR, BR, UR, FR, FL, DB, UB}, + + [M] = {UB, UL, DB, UR, UF, DL, DF, DR, FR, FL, BL, BR}, + [M2] = {DB, UL, DF, UR, UB, DL, UF, DR, FR, FL, BL, BR}, + [M3] = {DF, UL, UF, UR, DB, DL, UB, DR, FR, FL, BL, BR}, + [S] = {UF, DL, UB, UL, DF, DR, DB, UR, FR, FL, BL, BR}, + [S2] = {UF, DR, UB, DL, DF, UR, DB, UL, FR, FL, BL, BR}, + [S3] = {UF, UR, UB, DR, DF, UL, DB, DL, FR, FL, BL, BR}, + [E] = {UF, UL, UB, UR, DF, DL, DB, DR, FL, BL, BR, FR}, + [E2] = {UF, UL, UB, UR, DF, DL, DB, DR, BL, BR, FR, FL}, + [E3] = {UF, UL, UB, UR, DF, DL, DB, DR, BR, FR, FL, BL}, + + [X] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR}, + [X2] = {DB, DL, DF, DR, UB, UL, UF, UR, BR, BL, FL, FR}, + [X3] = {UB, BL, DB, BR, UF, FL, DF, FR, UR, UL, DL, DR}, + [Y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL}, + [Y2] = {UB, UR, UF, UL, DB, DR, DF, DL, BL, BR, FR, FL}, + [Y3] = {UL, UB, UR, UF, DL, DB, DR, DF, FL, BL, BR, FR}, + [Z] = {FL, DL, BL, UL, FR, DR, BR, UR, UF, DF, DB, UB}, + [Z2] = {DF, DR, DB, DL, UF, UR, UB, UL, FL, FR, BR, BL}, + [Z3] = {FR, UR, BR, DR, FL, UL, BL, DL, DF, UF, UB, DB}, + + [NULLMOVE] = {UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR}, +}; + +int corner_cycle[NULLMOVE+1][8] = { + [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [U2] = {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR}, + [U3] = {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR}, + [D] = {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR}, + [D2] = {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL}, + [D3] = {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL}, + [R] = {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR}, + [R2] = {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR}, + [R3] = {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR}, + [L] = {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR}, + [L2] = {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR}, + [L3] = {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR}, + [F] = {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR}, + [F2] = {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR}, + [F3] = {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR}, + [B] = {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL}, + [B2] = {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL}, + [B3] = {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR}, + + [Uw] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [Uw2] = {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR}, + [Uw3] = {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR}, + [Dw] = {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR}, + [Dw2] = {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL}, + [Dw3] = {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL}, + [Rw] = {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR}, + [Rw2] = {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR}, + [Rw3] = {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR}, + [Lw] = {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR}, + [Lw2] = {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR}, + [Lw3] = {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR}, + [Fw] = {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR}, + [Fw2] = {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR}, + [Fw3] = {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR}, + [Bw] = {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL}, + [Bw2] = {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL}, + [Bw3] = {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR}, + + [M] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [M2] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [M3] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [S] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [S2] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [S3] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [E] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [E2] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + [E3] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, + + [X] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [X2] = {DBR, DBL, DFL, DFR, UBR, UBL, UFL, UFR}, + [X3] = {UBR, UBL, DBL, DBR, UFR, UFL, DFL, DFR}, + [Y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL}, + [Y2] = {UBL, UBR, UFR, UFL, DBL, DBR, DFR, DFL}, + [Y3] = {UFL, UBL, UBR, UFR, DFL, DBL, DBR, DFR}, + [Z] = {UFL, DFL, DBL, UBL, UFR, DFR, DBR, UBR}, + [Z2] = {DFL, DFR, DBR, DBL, UFL, UFR, UBR, UBL}, + [Z3] = {DFR, UFR, UBR, DBR, DFL, UFL, UBL, DBL}, + + [NULLMOVE] = {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, +}; + +int center_cycle[NULLMOVE+1][6] = { + [U] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [U2] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [U3] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [D] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [D2] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [D3] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [R] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [R2] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [R3] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [L] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [L2] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [L3] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [F] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [F2] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [F3] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [B] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [B2] = {U_center, D_center, R_center, L_center, F_center, B_center}, + [B3] = {U_center, D_center, R_center, L_center, F_center, B_center}, + + [Uw] = {U_center, D_center, B_center, F_center, R_center, L_center}, + [Uw2] = {U_center, D_center, L_center, R_center, B_center, F_center}, + [Uw3] = {U_center, D_center, F_center, B_center, L_center, R_center}, + [Dw] = {U_center, D_center, F_center, B_center, L_center, R_center}, + [Dw2] = {U_center, D_center, L_center, R_center, B_center, F_center}, + [Dw3] = {U_center, D_center, B_center, F_center, R_center, L_center}, + [Rw] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [Rw2] = {D_center, U_center, R_center, L_center, B_center, F_center}, + [Rw3] = {B_center, F_center, R_center, L_center, U_center, D_center}, + [Lw] = {B_center, F_center, R_center, L_center, U_center, D_center}, + [Lw2] = {D_center, U_center, R_center, L_center, B_center, F_center}, + [Lw3] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [Fw] = {L_center, R_center, U_center, D_center, F_center, B_center}, + [Fw2] = {D_center, U_center, L_center, R_center, F_center, B_center}, + [Fw3] = {R_center, L_center, D_center, U_center, F_center, B_center}, + [Bw] = {R_center, L_center, D_center, U_center, F_center, B_center}, + [Bw2] = {D_center, U_center, L_center, R_center, F_center, B_center}, + [Bw3] = {L_center, R_center, U_center, D_center, F_center, B_center}, + + [X] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [X2] = {D_center, U_center, R_center, L_center, B_center, F_center}, + [X3] = {B_center, F_center, R_center, L_center, U_center, D_center}, + [Y] = {U_center, D_center, B_center, F_center, R_center, L_center}, + [Y2] = {U_center, D_center, L_center, R_center, B_center, F_center}, + [Y3] = {U_center, D_center, F_center, B_center, L_center, R_center}, + [Z] = {L_center, R_center, U_center, D_center, F_center, B_center}, + [Z2] = {D_center, U_center, L_center, R_center, F_center, B_center}, + [Z3] = {R_center, L_center, D_center, U_center, F_center, B_center}, + + [M] = {B_center, F_center, R_center, L_center, U_center, D_center}, + [M2] = {D_center, U_center, R_center, L_center, B_center, F_center}, + [M3] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [E] = {U_center, D_center, F_center, B_center, L_center, R_center}, + [E2] = {U_center, D_center, L_center, R_center, B_center, F_center}, + [E3] = {U_center, D_center, B_center, F_center, R_center, L_center}, + [S] = {L_center, R_center, U_center, D_center, F_center, B_center}, + [S2] = {D_center, U_center, L_center, R_center, F_center, B_center}, + [S3] = {R_center, L_center, D_center, U_center, F_center, B_center}, + + [NULLMOVE] = {U_center, D_center, R_center, L_center, F_center, B_center}, +}; + diff --git a/old/2020-unknown-date/pieces.h b/old/2020-unknown-date/pieces.h @@ -0,0 +1,59 @@ +/* Moves and other things */ + +#ifndef PIECES_H +#define PIECES_H + +typedef enum { + U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3, + Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, S, S2, S3, E, E2, E3, + X, X2, X3, Y, Y2, Y3, Z, Z2, Z3, + NULLMOVE +} Move; +typedef enum { + U_center, D_center, R_center, L_center, F_center, B_center +} Center; +typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge; +typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner; + +/* Arrays for strings of pieces and moves */ +extern char edge_string[12][5]; +extern char corner_string[8][5]; +extern char center_string[6][5]; +extern char move_string[NULLMOVE+1][5]; + +/* Three big arrays that determine the movement of edges, corners and + * centers for some of the basic moves. Yes, one can be much more elegant and + * write much fewer lines. But then things get less elegant somewhere else. */ +extern int edge_cycle[NULLMOVE+1][12]; +extern int corner_cycle[NULLMOVE+1][8]; +extern int center_cycle[NULLMOVE+1][6]; + +/* Representation of the cube */ +typedef struct { + /* Edge orientation */ + int eofb; + int eorl; + int eoud; + + /* Corner orientation */ + int coud; + int cofb; + int corl; + + /* Position of M/E/S slice edges */ + /* TODO: keep in mind that the 4! positions with edges in correct slice + * must be the first 4! indeces */ + int epose; + int eposs; + int eposm; + + /* Permutation of corners */ + int cp; + + /* Position of centers */ + int centerpos; +} Cube; + +#endif diff --git a/old/2021-02-06/cube.c b/old/2021-02-06/cube.c @@ -0,0 +1,664 @@ +#include "cube.h" + +typedef struct { + int ep[12], eofb[12], eorl[12], eoud[12], + cp[8], coud[8], corl[8], cofb[8], cpos[6]; +} CubeArray; + +typedef struct { + bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos; +} PieceFilter; + +PieceFilter fAll = {true,true,true,true,true,true,true,true,true,true,true}; +PieceFilter cpos_only = { .cpos = true }; + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +Cube arrays_to_cube(CubeArray arr, PieceFilter f); +void move_cubearray(Move m, CubeArray *arr, PieceFilter f); +Cube move_via_array(Move m, Cube cube, PieceFilter f); +void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front); +bool read_ttables_file(); +bool write_ttables_file(); + +/* Transition tables */ +uint16_t epose_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposs_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposm_ttable[NMOVES][factorial12/factorial8]; +uint16_t eofb_ttable[NMOVES][pow2to11]; +uint16_t eorl_ttable[NMOVES][pow2to11]; +uint16_t eoud_ttable[NMOVES][pow2to11]; +uint16_t cp_ttable[NMOVES][factorial8]; +uint16_t coud_ttable[NMOVES][pow3to7]; +uint16_t cofb_ttable[NMOVES][pow3to7]; +uint16_t corl_ttable[NMOVES][pow3to7]; +uint16_t cpos_ttable[NMOVES][factorial6]; + +bool commute[NMOVES][NMOVES]; +bool possible_next[NMOVES][NMOVES][NMOVES]; +Move inverse[NMOVES]; + +char edge_string[12][5] = + { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" }; +char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" }; +char center_string[6][5] = { "U", "D", "R", "L", "F", "B" }; +char move_string[NMOVES][5] = + { "-", + "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", + "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'", + "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'", + "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" }; + +/* For each type of pieces only the effects of U, x and y are described */ +int edge_cycle[NMOVES][12] = + { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, + [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR}, + [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} }; +int eofb_flipped[NMOVES][12] = + { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eorl_flipped[NMOVES][12] = + { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eoud_flipped[NMOVES][12] = + { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; +int corner_cycle[NMOVES][8] = + { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} }; +int coud_flipped[NMOVES][8] = + { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} }; +int corl_flipped[NMOVES][8] = + { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} }; +int cofb_flipped[NMOVES][8] = + { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=2,[UFL]=1,[DBL]=1,[DFL]=2}, + [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} }; +int center_cycle[NMOVES][6] = + { [x] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [y] = {U_center, D_center, B_center, F_center, R_center, L_center} }; + +/* Each move is reduced to a combination of U, x and y using this table */ +Move equiv_moves[NMOVES][14] = { + [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 }, + [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 }, + [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 }, + [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 }, + [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 }, + [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 }, + [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 }, + + [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 }, + [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 }, + [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 }, + [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 }, + [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 }, + [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 }, + [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 }, + [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 }, + [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 }, + [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + + [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 }, + [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 }, + [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 }, + [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 }, + [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 }, + [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 }, + [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 }, + [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 }, + [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 }, + + [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/* Movesets */ +bool standard_moveset[NMOVES] = { + [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true, + [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true, + [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true, +}; + +int epe_solved[] = {FR, FL, BL, BR}; +int eps_solved[] = {UL, UR, DL, DR}; +int epm_solved[] = {UF, UB, DF, DB}; + +Cube blank_cube() { + Cube c = {0}; + return c; +} + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) { + /* ep is the hardest */ + if (f.epose || f.eposs || f.eposm) + for (int i = 0; i < 12; i++) arr->ep[i] = -1; + if (f.epose) { + int epe[4], epose[12]; + index_to_perm(cube.epose % factorial(4), 4, epe); + index_to_subset(cube.epose / factorial(4), 12, 4, epose); + for (int i = 0, ie = 0; i < 12; i++) + if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]]; + } + if (f.eposs) { + int eps[4], eposs[12]; + index_to_perm(cube.eposs % factorial(4), 4, eps); + index_to_subset(cube.eposs / factorial(4), 12, 4, eposs); + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + for (int i = 0, is = 0; i < 12; i++) + if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]]; + } + if (f.eposm) { + int epm[4], eposm[12]; + index_to_perm(cube.eposm % factorial(4), 4, epm); + index_to_subset(cube.eposm / factorial(4), 12, 4, eposm); + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + for (int i = 0, im = 0; i < 12; i++) + if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]]; + } + + /* All the others */ + if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) index_to_perm( cube.cp, 8, arr->cp); + if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos); +} + +Cube arrays_to_cube(CubeArray arr, PieceFilter f) { + Cube ret = {0}; + + /* Again, ep is the hardest part */ + if (f.epose) { + int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, ie = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epe_solved[j]) + { epe[ie++] = j; epose[i] = 1; } + ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4); + } + if (f.eposs) { + int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, is = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == eps_solved[j]) + { eps[is++] = j; eposs[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4); + } + if (f.eposm) { + int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, im = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epm_solved[j]) + { epm[im++] = j; eposm[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4); + } + if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2); + if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2); + if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2); + if (f.cp) ret.cp = perm_to_index( arr.cp, 8 ); + if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3); + if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3); + if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3); + if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 ); + + return ret; +} + +void move_cubearray(Move m, CubeArray *arr, PieceFilter f) { + if (f.epose || f.eposs || f.eposm) + apply_permutation(edge_cycle[m], arr->ep, 12); + if (f.eofb) { + apply_permutation(edge_cycle[m], arr->eofb, 12); + sum_arrays_mod(eofb_flipped[m], arr->eofb, 12, 2); + } + if (f.eofb) { + apply_permutation(edge_cycle[m], arr->eorl, 12); + sum_arrays_mod(eorl_flipped[m], arr->eorl, 12, 2); + } + if (f.eofb) { + apply_permutation(edge_cycle[m], arr->eoud, 12); + sum_arrays_mod(eoud_flipped[m], arr->eoud, 12, 2); + } + if (f.cp) + apply_permutation(corner_cycle[m], arr->cp, 8); + if (f.coud) { + apply_permutation(corner_cycle[m], arr->coud, 8); + sum_arrays_mod(coud_flipped[m], arr->coud, 8, 3); + } + if (f.corl) { + apply_permutation(corner_cycle[m], arr->corl, 8); + sum_arrays_mod(corl_flipped[m], arr->corl, 8, 3); + } + if (f.cofb) { + apply_permutation(corner_cycle[m], arr->cofb, 8); + sum_arrays_mod(cofb_flipped[m], arr->cofb, 8, 3); + } + if (f.cpos) + apply_permutation(center_cycle[m], arr->cpos, 6); +} + +Cube move_via_array(Move m, Cube cube, PieceFilter f) { + CubeArray arr = {0}; + cube_to_arrays(cube, &arr, f); + move_cubearray(m, &arr, f); + return arrays_to_cube(arr, f); +} + +int copy_alg(NissMove *src, NissMove *dest) { + int i; + for (i = 0; src[i].m != NULLMOVE; i++) + dest[i] = src[i]; + dest[i].m = NULLMOVE; + return i; +} + +bool equal(Cube c1, Cube c2) { + return c1.eofb == c2.eofb && c1.epose == c2.epose && + c1.eposs == c2.eposs && c1.eposm == c2.eposm && + c1.coud == c2.coud && c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +bool solvable(Cube cube) { + /* Since we memorize orientation truncating the last digit, we only need to + * check that the permutations have the correct sign. */ + CubeArray arr = {0}; + cube_to_arrays(cube, &arr, fAll); + return (perm_sign(arr.ep,12) ^ perm_sign(arr.cpos,6)) == perm_sign(arr.cp,8); +} + +bool is_solved(Cube cube, bool reorient) { + if (!reorient || !cube.cpos) + return !cube.eofb && !cube.coud && !cube.cp && + !cube.epose && !cube.eposs && !cube.eposm; + + bool ret = false; + for (int i = x; i <= z3; i++) { + ret = ret || is_solved(move_cube(i, cube), false); + for (int j = x; j <= z3; j++) + ret = ret || is_solved(move_cube(i, move_cube(j, cube)), false); + } + return ret; +} + +void print_cube(Cube cube) { + CubeArray arr = {0}; + cube_to_arrays(cube, &arr, fAll); + + for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arr.ep[i]]); + printf("\n"); + for (int i = 0; i < 12; i++) printf(" %c ", arr.eofb[i] + '0'); + printf("\n"); + for (int i = 0; i < 8; i++) printf("%s ", corner_string[arr.cp[i]]); + printf("\n"); + for (int i = 0; i < 8; i++) printf(" %c ", arr.coud[i] + '0'); + printf("\n"); + for (int i = 0; i < 6; i++) printf(" %s ", center_string[arr.cpos[i]]); + printf("\n"); +} + +/* TODO: all strings start with space?? */ +void print_moves(NissMove *alg) { + bool niss = false; + for (int i = 0; alg[i].m != NULLMOVE; i++) { + char *fill = !niss && alg[i].inverse ? " (" : + (niss && !alg[i].inverse ? ") " : " "); + printf("%s%s", fill, move_string[alg[i].m]); + niss = alg[i].inverse; + } + printf("%s\n", niss ? ")" : ""); +} + +int read_moves(char *str, NissMove *alg, int n) { + bool niss = false; + int c = 0; + + for (int i = 0; str[i] && c < n; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' || str[i] == ')') { + if ((niss && str[i] == '(') || (!niss && str[i] == ')')) + return -1; + niss = !niss; + continue; + } + + alg[c].inverse = niss; alg[c].m = NULLMOVE; + for (Move j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + alg[c].m = j; + if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; } + if (str[i+1]=='2') { alg[c].m += 1; i++; } + else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; } + c++; + break; + } + } + } + + alg[c].m = NULLMOVE; + return c; +} + +/* Helper function for cleanup. alg must contain only basic moves, no 2 or '. + top and front describe an admissible orientation of the cube. */ +void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front) { + int c = 0, i = 0; + NissMove aux[n+3]; + aux[0].m = NULLMOVE; + + while (i < n && alg[i].m != NULLMOVE) { + int j = i; + while (j < n && commute[alg[i].m][alg[j].m]) j++; + Move base = 6*((alg[i].m-1)/6); + int t1 = 0, t2 = 0; + for (int k = i; k < j; k++) + if (alg[k].m == base+1) t1 = (t1+1)%4; + else t2 = (t2+1)%4; + if (t1) { aux[c].inverse = inv; aux[c].m = base+t1; c++; } + if (t2) { aux[c].inverse = inv; aux[c].m = base+t2+3; c++; } + i = j; + } + aux[c].m = NULLMOVE; + + CubeArray q; + cube_to_arrays(blank_cube(), &q, cpos_only); + /* First we try to rotate in one move, then we try an x or y rotation + followed by a z rotation */ + for (int r = x; r <= z3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[F_center] == front && q.cpos[U_center] == top) { + aux[c].inverse = inv; aux[c].m = r; + + aux[++c].m = NULLMOVE; + copy_alg(aux, alg); + return; + } + move_cubearray(inverse[r], &q, cpos_only); + } + for (int r = x; r <= y3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[F_center] == front) { + aux[c].inverse = inv; aux[c++].m = r; + break; + } + move_cubearray(inverse[r], &q, cpos_only); + } + for (int r = z; r <= z3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[U_center] == top) { + aux[c].inverse = inv; aux[c++].m = r; + break; + } + move_cubearray(inverse[r], &q, cpos_only); + } + + aux[c].m = NULLMOVE; + copy_alg(aux, alg); +} + +void cleanup(NissMove *alg, int n) { + int count_n = 0, count_i = 0, *count; + NissMove aux_n[n+1], aux_i[n+1], *aux; + CubeArray cube_n, cube_i, *cube; + cube_to_arrays(blank_cube(), &cube_n, cpos_only); + cube_to_arrays(blank_cube(), &cube_i, cpos_only); + + for (int i = 0; count_n + count_i < n && alg[i].m != NULLMOVE; i++) { + if (alg[i].inverse) { count = &count_i; aux = aux_i; cube = &cube_i; } + else { count = &count_n; aux = aux_n; cube = &cube_n; } + + for (int j = 0; equiv_moves[alg[i].m][j]; j++) { + Move m = equiv_moves[alg[i].m][j]; + aux[*count].inverse = alg[i].inverse; + move_cubearray(m, cube, cpos_only); + if (m == U) aux[(*count)++].m = 3 * cube->cpos[0] + 1; + } + } + + aux_n[count_n].m = NULLMOVE; + aux_i[count_i].m = NULLMOVE; + sort_cancel_rotate(aux_n, count_n, false, cube_n.cpos[0], cube_n.cpos[4]); + sort_cancel_rotate(aux_i, count_i, true, cube_i.cpos[0], cube_n.cpos[4]); + copy_alg(aux_n, alg); + copy_alg(aux_i, alg+count_n); +} + +Cube inverse_cube(Cube cube) { + CubeArray arr = {0}, inv = {0}; + cube_to_arrays(cube, &arr, fAll); + + for (int i = 0; i < 12; i++) { + inv.ep[arr.ep[i]] = i; + inv.eofb[arr.ep[i]] = arr.eofb[i]; + inv.eorl[arr.ep[i]] = arr.eorl[i]; + inv.eoud[arr.ep[i]] = arr.eoud[i]; + } + for (int i = 0; i < 8; i++) { + inv.cp[arr.cp[i]] = i; + inv.coud[arr.cp[i]] = arr.coud[i]; + inv.corl[arr.cp[i]] = arr.corl[i]; + inv.cofb[arr.cp[i]] = arr.cofb[i]; + } + for (int i = 0; i < 6; i++) + inv.cpos[arr.cpos[i]] = i; + + return arrays_to_cube(inv, fAll); +} + +bool read_ttables_file() { + FILE *ttf; + if ((ttf = fopen("ttables", "rb")) != NULL) { + for (int m = 0; m < NMOVES; m++) { + fread(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fread(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fread(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fread(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fread(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fread(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fread(cp_ttable[m], sizeof(uint16_t), factorial8, ttf); + fread(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fread(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fread(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fread(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf); + } + fclose(ttf); + return true; + } else return false; +} + +bool write_ttables_file() { + FILE *ttf; + if ((ttf = fopen("ttables", "wb")) != NULL) { + for (int m = 0; m < NMOVES; m++) { + fwrite(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fwrite(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fwrite(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fwrite(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fwrite(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fwrite(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fwrite(cp_ttable[m], sizeof(uint16_t), factorial8, ttf); + fwrite(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fwrite(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fwrite(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fwrite(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf); + } + fclose(ttf); + return true; + } else return false; +} + +void init_ttables(bool read, bool write) { + /* Generate all move cycles and flips; I do this regardless */ + for (int i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + CubeArray arr = {0}; + cube_to_arrays(blank_cube(), &arr, fAll); + for (int j = 0; equiv_moves[i][j]; j++) + move_cubearray(equiv_moves[i][j], &arr, fAll); + + intarrcopy(arr.ep, edge_cycle[i], 12); + intarrcopy(arr.eofb, eofb_flipped[i], 12); + intarrcopy(arr.eorl, eorl_flipped[i], 12); + intarrcopy(arr.eoud, eoud_flipped[i], 12); + intarrcopy(arr.cp, corner_cycle[i], 8); + intarrcopy(arr.coud, coud_flipped[i], 8); + intarrcopy(arr.corl, corl_flipped[i], 8); + intarrcopy(arr.cofb, cofb_flipped[i], 8); + intarrcopy(arr.cpos, center_cycle[i], 6); + } + + if (read) + if (read_ttables_file()) + return; + + /* Initialize transition tables */ + Cube c = {0}; + PieceFilter fe = {.epose=true}, fs = {.eposs=true}, fm = {.eposm=true}; + PieceFilter feo = { .eofb = true, .eorl = true, .eoud = true }; + PieceFilter fcp = { .cp = true }; + PieceFilter fco = { .cofb = true, .corl = true, .coud = true }; + PieceFilter fcc = { .cpos = true }; + for (int m = 0; m < NMOVES; m++) { + for (uint16_t i = 0; i < factorial12/factorial8; i++) { + c.epose = i; epose_ttable[m][i] = move_via_array(m, c, fe).epose; + c.eposs = i; eposs_ttable[m][i] = move_via_array(m, c, fs).eposs; + c.eposm = i; eposm_ttable[m][i] = move_via_array(m, c, fm).eposm; + } + for (uint16_t i = 0; i < pow2to11; i++ ) { + c.eofb = i; eofb_ttable[m][i] = move_via_array(m, c, feo).eofb; + c.eorl = i; eorl_ttable[m][i] = move_via_array(m, c, feo).eorl; + c.eoud = i; eoud_ttable[m][i] = move_via_array(m, c, feo).eoud; + } + for (uint16_t i = 0; i < factorial8; i++) { + c.cp = i; cp_ttable[m][i] = move_via_array(m, c, fcp).cp; + } + for (uint16_t i = 0; i < pow3to7; i++) { + c.coud = i; coud_ttable[m][i] = move_via_array(m, c, fco).coud; + c.corl = i; corl_ttable[m][i] = move_via_array(m, c, fco).corl; + c.cofb = i; cofb_ttable[m][i] = move_via_array(m, c, fco).cofb; + } + for (uint16_t i = 0; i < factorial6; i++) { + c.cpos = i; cpos_ttable[m][i] = move_via_array(m, c, fcc).cpos; + } + } + + if (write) write_ttables_file(); +} + +Cube move_cube(Move m, Cube cube) { + Cube moved = cube; + + moved.epose = epose_ttable[m][cube.epose]; + moved.eposs = eposs_ttable[m][cube.eposs]; + moved.eposm = eposm_ttable[m][cube.eposm]; + moved.eofb = eofb_ttable[m][cube.eofb]; + moved.eorl = eorl_ttable[m][cube.eorl]; + moved.eoud = eoud_ttable[m][cube.eoud]; + moved.coud = coud_ttable[m][cube.coud]; + moved.cofb = cofb_ttable[m][cube.cofb]; + moved.corl = corl_ttable[m][cube.corl]; + moved.cp = cp_ttable[m][cube.cp]; + moved.cpos = cpos_ttable[m][cube.cpos]; + + return moved; +} + +Cube compose(Cube c2, Cube c1) { + /* This is basically the same as the move_cubearray function above */ + CubeArray arr2 = {0}, arr1 = {0}; + cube_to_arrays(c2, &arr2, fAll); + cube_to_arrays(c1, &arr1, fAll); + + apply_permutation(arr2.ep, arr1.ep, 12); + apply_permutation(arr2.ep, arr1.eofb, 12); + apply_permutation(arr2.ep, arr1.eorl, 12); + apply_permutation(arr2.ep, arr1.eoud, 12); + sum_arrays_mod(arr2.eofb, arr1.eofb, 12, 2); + sum_arrays_mod(arr2.eorl, arr1.eorl, 12, 2); + sum_arrays_mod(arr2.eoud, arr1.eoud, 12, 2); + apply_permutation(arr2.cp, arr1.cp, 8); + apply_permutation(arr2.cp, arr1.coud, 8); + apply_permutation(arr2.cp, arr1.corl, 8); + apply_permutation(arr2.cp, arr1.cofb, 8); + sum_arrays_mod(arr2.coud, arr1.coud, 8, 3); + sum_arrays_mod(arr2.corl, arr1.corl, 8, 3); + sum_arrays_mod(arr2.cofb, arr1.cofb, 8, 3); + apply_permutation(arr2.cpos, arr1.cpos, 6); + + return arrays_to_cube(arr1, fAll); +} + +Cube apply_alg(NissMove *alg, Cube cube) { + Cube ret = {0}; + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (alg[i].inverse) + ret = move_cube(alg[i].m, ret); + ret = compose(cube, inverse_cube(ret)); + + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (!alg[i].inverse) + ret = move_cube(alg[i].m, ret); + return ret; +} + +void init_aux_tables() { + /* Commute */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + commute[i][j] = equal(move_cube(i, move_cube(j, blank_cube())), + move_cube(j, move_cube(i, blank_cube()))); + + /* Possible next (if the sequence i j k is valid) */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + for (int k = 0; k < NMOVES; k++) + possible_next[i][j][k] = + (j == 0) || + (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) && + !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3))); + + /* Inverse */ + for (int i = 0; i < NMOVES; i++) + inverse[i] = i == 0 ? 0 : i + 2 - 2*((i-1)%3); +} + diff --git a/old/2021-02-06/cube.h b/old/2021-02-06/cube.h @@ -0,0 +1,60 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "utils.h" + +#define NMOVES (z3+1) + +/* Constants for moves and pieces */ +typedef enum { + NULLMOVE, + U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3, + Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, S, S2, S3, E, E2, E3, + x, x2, x3, y, y2, y3, z, z2, z3, +} Move; +typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center; +typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge; +typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner; + +/* An alg is an array of "NissMoves", which can be on normal or on inverse. */ +typedef struct { bool inverse; Move m; } NissMove; + +/* Representation of the cube */ +typedef struct { + uint16_t eofb, eorl, eoud, coud, cofb, corl, + epose, eposs, eposm, cp, cpos; +} Cube; + +extern bool commute[NMOVES][NMOVES]; +extern bool possible_next[NMOVES][NMOVES][NMOVES]; +extern Move inverse[NMOVES]; +/* Movesets */ +extern bool standard_moveset[NMOVES]; + +int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */ + +bool equal(Cube c1, Cube c2); +bool is_solvable(Cube cube); +/* reorient=true allows solved in wrong orientation */ +bool is_solved(Cube cube, bool reorient); +void print_cube(Cube cube); +void print_moves(NissMove *alg); +int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */ +void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */ +Cube blank_cube(); +Cube inverse_cube(Cube cube); +Cube move_cube(Move m, Cube cube); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg */ +Cube apply_alg(NissMove *alg, Cube cube); + +void init_ttables(bool read, bool write); +void init_aux_tables(); + +void init_dbg(); + +#endif diff --git a/old/2021-02-06/main.c b/old/2021-02-06/main.c @@ -0,0 +1,46 @@ +#include <stdio.h> +#include "cube.h" +#include "solve.h" + +int main() { + init_ttables(true, true); + init_aux_tables(); + + + char moves[100] = "MR U' B2 Bw F z xE2 M' x Dw' y Fw2 y2"; + NissMove alg[100]; + read_moves(moves, alg, 100); + Cube cube = apply_alg(alg, blank_cube()); + + /*f_eofb(cube);*/ + + +/* NissMove sol[MAXS][MAXM];*/ + SolveData d = { .optimal_only = true, .available = standard_moveset, + .max_moves = 10, + .cleanup = true, + .max_solutions = 10, + .f = f_eofb }; + read_moves("y", d.pre_rotation, 2); + int n = solve(cube, &d); + printf("%d solutions found:\n", n); + for (int i = 0; i < n; i++) + print_moves(d.solutions[i]); + + NissMove a[5], b[5]; + read_moves("R", a, 5); + read_moves("U", b, 5); + Cube c1 = apply_alg(a,blank_cube()), c2 = apply_alg(b,blank_cube()); + print_cube(compose(c2,c1)); + print_cube(compose(c1,c2)); + /*print_cube(compose(c2,blank_cube()));*/ + + NissMove nm[10]; + read_moves("y(y)RU", nm, 10); + + print_moves(nm); + cleanup(nm, 10); + print_moves(nm); + + return 0; +} diff --git a/old/2021-02-06/solve.c b/old/2021-02-06/solve.c @@ -0,0 +1,177 @@ +#include "solve.h" + +/* Data for creating a pruning table: + - compressed: if set to true, each entry occupies only 4 bits, but values + larger than 15 cannot be stored. + - available[] is the list of availabel moves, as above. + - *ptable is the actual table to fill. + - n is the number of states (size of ptable). + - index must "linearize" the cube, i.e. return its index in ptable. + - fname is the name of the file where to store the table */ +typedef struct { + bool compressed, *available; + int max_moves; + uint8_t *ptable; + uint64_t n; + uint64_t (*index)(Cube); + char *fname; +} PruneData; + +/* TODO: comment this */ +typedef struct { + bool niss; + int m, d; + uint64_t *n; + Move last1, last2; +} DfsData; + +void solve_dfs(Cube cube, SolveData *sd, DfsData dd); +void init_ptable(PruneData *pd, bool read, bool write); + +/* Search solutions of lenght exactly d */ +void solve_dfs(Cube cube, SolveData *sd, DfsData dd) { + if (*dd.n >= sd->max_solutions || + ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d)) + return; + + (sd->solutions[*dd.n][dd.m]).inverse = dd.niss; + (sd->solutions[*dd.n][dd.m]).m = NULLMOVE; + + if (!sd->f(cube)) { /* Solved */ + if (dd.m == dd.d) { + (*dd.n)++; + if (*dd.n < sd->max_solutions) + copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]); + } + return; + } + + for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) { + Move move = sd->sorted_moves[i]; + if (possible_next[dd.last2][dd.last1][move]) { + sd->solutions[*dd.n][dd.m].inverse = dd.niss; + sd->solutions[*dd.n][dd.m].m = move; + DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = move, .last2 = dd.last1 }; + solve_dfs(move_cube(move, cube), sd, nn); + } + } + + if (sd->can_niss && !dd.niss && + (!dd.m || (dd.m && sd->f(move_cube(dd.last1, blank_cube()))))) { + DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n }; + solve_dfs(inverse_cube(cube), sd, nn); + } +} + +/* Iterative deepening depth-first search: for i running from the minimum + to the maximum number of moves allowed, looks for solutions of length i. */ +int solve(Cube cube, SolveData *sd) { + if (sd->precondition != NULL && !sd->precondition(cube)) + return -1; + + /* If not given, generate sorted list of moves */ + if (sd->sorted_moves[0] == NULLMOVE) { + int a[NMOVES], b[NMOVES], ia = 0, ib = 0; + for (int i = 0; i < NMOVES; i++) { + if (sd->available[i]) { + if (sd->f(move_cube(i, blank_cube()))) + a[ia++] = i; + else + b[ib++] = i; + } + } + intarrcopy(a, (int *)sd->sorted_moves, ia); + intarrcopy(b, (int *)sd->sorted_moves+ia, ib); + sd->sorted_moves[ia+ib] = NULLMOVE; + } + + sd->max_solutions = min(sd->max_solutions, MAXS); + Cube rotated = apply_alg(sd->pre_rotation, blank_cube()); + cube = apply_alg(inverse_cube(rotated), compose(cube, rotated)); + + uint64_t ret = 0; + for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) { + DfsData dd = { .d = i, .n = &ret }; + solve_dfs(cube, sd, dd); + } + + for (uint64_t i = 0; i < ret; i++) { + /* TODO: transform solutions with inverse of pre_rotation */ + if (sd->cleanup) + cleanup(sd->solutions[i], sd->max_moves*3); + } + + return ret; +} + +void prune_dfs(Cube cube, PruneData *pd, DfsData dd) { + uint64_t ind = pd->index(cube); + if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m) + return; + if (dd.m == dd.d) { + if (ind && !pd->ptable[ind]) { + pd->ptable[ind] = dd.m; + (*dd.n)++; + } + return; + } + + for (int i = 0; i < NMOVES; i++) { + if (dd.m<20) + if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) { + DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = i, .last2 = dd.last1 }; + prune_dfs(move_cube(i, cube), pd, nn); + } + } +} + +void init_ptable(PruneData *pd, bool read, bool write) { + if (read) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "rb")) != NULL) { + fread(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + return; + } + } + + /* TODO: for now it behaves always as if copressed = false */ + for (uint64_t i = 0; i < pd->n; i++) + pd->ptable[i] = 0; + + uint64_t s = 1; + for (int i = 1; i < pd->max_moves && s < pd->n; i++) { + DfsData dd = { .d = i, .n = &s }; + prune_dfs(blank_cube(), pd, dd); + } + + if (write) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "wb")) != NULL) { + fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + return; + } + } +} + +/* Solving steps (and indexing functions) */ + +uint64_t index_eofb(Cube cube) { return cube.eofb; } +uint16_t f_eofb(Cube cube) { + static bool initialized_ptable; + static uint8_t pt_eofb[pow2to11]; + if (!initialized_ptable) { + PruneData pd = { + .compressed = false, .available = standard_moveset, .max_moves = 13, + .ptable = pt_eofb, .n = pow2to11, .index = index_eofb, + .fname = "ptable_eofb" + }; + init_ptable(&pd, false, true); + initialized_ptable = true; + } + return cube.eofb ? pt_eofb[cube.eofb] : 0; +} + diff --git a/old/2021-02-06/solve.h b/old/2021-02-06/solve.h @@ -0,0 +1,55 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include <stdlib.h> +#include "cube.h" + +/* Maximum number of moves per solution and of solutions */ +#define MAXM 30 +#define MAXS 999 + +/* Data for solving a step: + - can_niss is true niss can be used, false otherwise. + - optimal_only if true, dynamically updates max_moves so non-optimal + solutions are discarded. + - cleanup determines whether the cleaunup() function should be used on + the found solutions before returning. + - available[m] is true if the move m can be used, false otherwise. + - min_moves and max_moves are the minimum and maximum number of moves that + can be used. + - max_solution is the maximum number of solutions that can be returned. + - precondition can be used to check wheter the step can actually be applied + to the cube. If it returns false, solve() stops immediately returning -1. + - f must return 0 if and only if the step is solve, otherwise it must return + a lower bound for the number of moves required (without niss). + - sorted_moves[] can be used to specify in which order moves are tried + by the solving algorithm (for example if one wants to always try F' before + F). If sorted_moves[0] == NULLMOVE, the list is generated automatically. + It is advised to list first all the moves that actually influence the + solved state of the step (this is the default choice). This is in order to + avoid cases like B2 F for EO and to NISS only when it makes sense. + - start_moves [Currently unused, REMOVE] + are the moves that will be used as first moves of all + solutions. For example giving R' U' F (F' U R) will generate FMC scrambles + and y (y) will solve the step on another axis. + - pre_rotation are the rotations to apply before the scamble to solve + the step wrt a different orientation + - pre_rotation are the rotations to apply before the scamble to solve + the step wth respect to a different orientation. + - solutions[][] is the array where to store the found solutions. */ +typedef struct { + bool can_niss, optimal_only, cleanup, *available; + int min_moves, max_moves; + uint64_t max_solutions; + bool (*precondition)(Cube); + uint16_t (*f)(Cube); + Move sorted_moves[NMOVES]; + NissMove pre_rotation[3], solutions[MAXS][MAXM]; +} SolveData; + +int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */ + +/* Steps */ +uint16_t f_eofb(Cube cube); + +#endif diff --git a/old/2021-02-06/utils.c b/old/2021-02-06/utils.c @@ -0,0 +1,197 @@ +#include "utils.h" + +void swap(int *a, int *b) { + int aux = *a; + *a = *b; + *b = aux; +} + +void intarrcopy(int *src, int *dst, int n) { + for (int i = 0; i < n; i++) + dst[i] = src[i]; +} + +int sum(int *a, int n) { + int ret = 0; + for (int i = 0; i < n; i++) + ret += a[i]; + return ret; +} + +bool is_perm(int *a, int n) { + int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0; + for (int i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + for (int i = 0; i < n; i++) + if (!aux[i]) + return false; + return true; +} + +bool is_subset(int *a, int n, int k) { + int sum = 0; + for (int i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + return sum == k; +} + +int powint(int a, int b) { + return 0; + if (b == 0 || a == 1) + return 1; + if (a == 0) + return 0; + if (b < 0) + return 0; /* Immediate truncate (integer part is 0) */ + if (b % 2) { + return a * powint(a, b-1); + } else { + int x = powint(a, b/2); + return x*x; + } +} + +int factorial(int n) { + if (n < 0) + return 0; + int ret = 1; + for (int i = 1; i <= n; i++) + ret *= i; + return ret; +} + +int binomial(int n, int k) { + if (n < 0 || k < 0 || k > n) + return 0; + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +void int_to_digit_array(int a, int b, int n, int *r) { + if (b <= 1) + for (int i = 0; i < n; i++) + r[i] = 0; + else + for (int i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +int digit_array_to_int(int *a, int n, int b) { + int ret = 0, p = 1; + for (int i = 0; i < n; i++, p *= b) + ret += a[i] * p; + return ret; +} + +int perm_to_index(int *a, int n) { + if (!is_perm(a, n)) + return factorial(n); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + int c = 0; + for (int j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + return ret; +} + +void index_to_perm(int p, int n, int *r) { + if (p < 0 || p >= factorial(n)) /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */ + for (int i = 0; i < n; i++) { + int c = 0, j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } +} + +int perm_sign(int *a, int n) { + if (!is_perm(a,n)) + return false; + int ret = 0; + for (int i = 0; i < n; i++) + for (int j = i+1; j < n; j++) + ret += (a[i]>a[j]) ? 1 : 0; + return ret % 2; +} + +int subset_to_index(int *a, int n, int k) { + /* TODO: better checks */ + if (!is_subset(a, n, k)) + return binomial(n, k); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + ret += binomial(n-i-1, k); + k--; + } + } + return ret; +} + +void index_to_subset(int s, int n, int k, int *r) { + if (s < 0 || s >= binomial(n, k)) { /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + return; + } + for (int i = 0; i < n; i++) { + if (k == n-i) { + for (int j = i; j < n; j++) + r[j] = 1; + return; + } + if (k == 0) { + for (int j = i; j < n; j++) + r[j] = 0; + return; + } + /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + int v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void int_to_sum_zero_array(int x, int b, int n, int *a) { + if (b <= 1) { + for (int i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + int s = 0; + for (int i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +void apply_permutation(int *perm, int *set, int n) { + if (!is_perm(perm, n)) + return; + int aux[n]; + for (int i = 0; i < n; i++) + aux[i] = set[perm[i]]; + intarrcopy(aux, set, n); +} + +void sum_arrays_mod(int *a, int *b, int n, int m) { + for (int i = 0; i < n; i++) + b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m; +} diff --git a/old/2021-02-06/utils.h b/old/2021-02-06/utils.h @@ -0,0 +1,70 @@ +/* General utility functions */ + +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* Some useful constants */ +#define pow2to11 2048 +#define pow2to12 4096 +#define pow3to7 2187 +#define pow3to8 6561 +#define pow12to4 20736 +#define factorial4 24 +#define factorial6 720 +#define factorial8 40320 +#define factorial12 479001600 +#define binom12on4 495 +#define binom8on4 70 + +/* Generic utility functions */ +void swap(int *a, int *b); +void intarrcopy(int *src, int *dst, int n); +int sum(int *a, int n); +bool is_perm(int *a, int n); +bool is_perm(int *a, int n); + + +/* Standard mathematical functions */ +int powint(int a, int b); +int factorial(int n); +int binomial(int n, int k); + +/* Converts the integer a to its representation in base b (first n digits + * only) and saves the result in r. */ +void int_to_digit_array(int a, int b, int n, int *r); +int digit_array_to_int(int *a, int n, int b); + +/* Converts the first n-1 digits of a number to an array a of digits in base b; + * then adds one element to the array, so that the sum of the elements of a is + * zero modulo b. + * This is used for determing the edge orientation from an 11-bits integer or + * the corner orientation from a 7-trits integer. */ +void int_to_sum_zero_array(int x, int b, int n, int *a); + +/* Converts a permutation on [0..(n-1)] into the integer i which is the index + * of the permutation in the sorted list of all n! such permutations. */ +int perm_to_index(int *a, int n); +void index_to_perm(int p, int n, int *r); + +/* Determine the sign of a permutation */ +int perm_sign(int a[], int n); + +/* Converts a k-element subset of a set from an array of n elements, of which k + * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */ +int subset_to_index(int *a, int n, int k); +void index_to_subset(int s, int n, int k, int *r); + +int ordered_subset_to_index(int *a, int n, int k); +void index_to_ordered_subset(int s, int n, int k, int *r); + +void apply_permutation(int *perm, int *set, int n); + +/* b[i] = (a[i]+b[i])%m for i=1,...,n */ +void sum_arrays_mod(int *a, int *b, int n, int m); + +#endif diff --git a/old/2021-02-18-piecefilter/compile.sh b/old/2021-02-18-piecefilter/compile.sh @@ -0,0 +1 @@ +gcc -Wall -Wextra -O2 -std=c99 -o nissy ./src/*.c diff --git a/old/2021-02-18-piecefilter/nissy b/old/2021-02-18-piecefilter/nissy Binary files differ. diff --git a/old/2021-02-18-piecefilter/src/cube.c b/old/2021-02-18-piecefilter/src/cube.c @@ -0,0 +1,198 @@ +#include "cube.h" + +char edge_string[12][5] = + { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" }; +char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" }; +char center_string[6][5] = { "U", "D", "R", "L", "F", "B" }; + +int epe_solved[] = {FR, FL, BL, BR}; +int eps_solved[] = {UL, UR, DL, DR}; +int epm_solved[] = {UF, UB, DF, DB}; + +PieceFilter fAll = {true,true,true,true,true,true,true,true,true,true,true}; + +Cube blank_cube() { + Cube c = {0}; + return c; +} + +/* Return axis (ud=0, rl=1, fb=0) of center c */ +int center_axis(int c) { + if (c == U_center || c == D_center) + return 0; + if (c == R_center || c == L_center) + return 1; + return 2; +} + +/* Return slice (e=0, s=1, m=2) to which e belongs */ +int edge_slice(int e) { + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + return 2; +} + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) { + /* ep is the hardest */ + if (f.epose || f.eposs || f.eposm) + for (int i = 0; i < 12; i++) arr->ep[i] = -1; + if (f.epose) { + int epe[4], epose[12]; + index_to_perm(cube.epose % factorial(4), 4, epe); + index_to_subset(cube.epose / factorial(4), 12, 4, epose); + for (int i = 0, ie = 0; i < 12; i++) + if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]]; + } + if (f.eposs) { + int eps[4], eposs[12]; + index_to_perm(cube.eposs % factorial(4), 4, eps); + index_to_subset(cube.eposs / factorial(4), 12, 4, eposs); + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + for (int i = 0, is = 0; i < 12; i++) + if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]]; + } + if (f.eposm) { + int epm[4], eposm[12]; + index_to_perm(cube.eposm % factorial(4), 4, epm); + index_to_subset(cube.eposm / factorial(4), 12, 4, eposm); + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + for (int i = 0, im = 0; i < 12; i++) + if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]]; + } + + /* All the others */ + if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) index_to_perm( cube.cp, 8, arr->cp); + if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos); +} + +Cube arrays_to_cube(CubeArray arr, PieceFilter f) { + Cube ret = {0}; + + /* Again, ep is the hardest part */ + if (f.epose) { + int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, ie = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epe_solved[j]) + { epe[ie++] = j; epose[i] = 1; } + ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4); + } + if (f.eposs) { + int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, is = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == eps_solved[j]) + { eps[is++] = j; eposs[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4); + } + if (f.eposm) { + int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, im = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epm_solved[j]) + { epm[im++] = j; eposm[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4); + } + if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2); + if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2); + if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2); + if (f.cp) ret.cp = perm_to_index( arr.cp, 8 ); + if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3); + if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3); + if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3); + if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 ); + + return ret; +} + +bool equal(Cube c1, Cube c2) { + return c1.eofb == c2.eofb && c1.epose == c2.epose && + c1.eposs == c2.eposs && c1.eposm == c2.eposm && + c1.coud == c2.coud && c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +bool solvable(Cube cube) { + /* Since we memorize orientation truncating the last digit, we only need to + * check that the permutations have the correct sign. */ + CubeArray arr = {0}; + cube_to_arrays(cube, &arr, fAll); + return (perm_sign(arr.ep,12) ^ perm_sign(arr.cpos,6)) == perm_sign(arr.cp,8); +} + +bool is_solved(Cube cube) { + return !cube.eofb && !cube.coud && !cube.cp && + !cube.epose && !cube.eposs && !cube.eposm && cube.cpos; +} + +void print_cube(Cube cube) { + CubeArray arr = {0}; + cube_to_arrays(cube, &arr, fAll); + + for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arr.ep[i]]); + printf("\n"); + for (int i = 0; i < 12; i++) printf(" %c ", arr.eofb[i] + '0'); + printf("\n"); + for (int i = 0; i < 8; i++) printf("%s ", corner_string[arr.cp[i]]); + printf("\n"); + for (int i = 0; i < 8; i++) printf(" %c ", arr.coud[i] + '0'); + printf("\n"); + for (int i = 0; i < 6; i++) printf(" %s ", center_string[arr.cpos[i]]); + printf("\n"); +} + +Cube inverse_cube(Cube cube) { + CubeArray arr = {0}, inv = {0}; + cube_to_arrays(cube, &arr, fAll); + + for (int i = 0; i < 12; i++) { + inv.ep[arr.ep[i]] = i; + inv.eofb[arr.ep[i]] = arr.eofb[i]; + inv.eorl[arr.ep[i]] = arr.eorl[i]; + inv.eoud[arr.ep[i]] = arr.eoud[i]; + } + for (int i = 0; i < 8; i++) { + inv.cp[arr.cp[i]] = i; + inv.coud[arr.cp[i]] = arr.coud[i]; + inv.corl[arr.cp[i]] = arr.corl[i]; + inv.cofb[arr.cp[i]] = arr.cofb[i]; + } + for (int i = 0; i < 6; i++) + inv.cpos[arr.cpos[i]] = i; + + return arrays_to_cube(inv, fAll); +} + +Cube compose_via_arrays(CubeArray arr2, Cube c1, PieceFilter f) { + /* This is basically the same as the move_cubearray function above */ + CubeArray arr1 = {0}; + cube_to_arrays(c1, &arr1, fAll); + + apply_permutation(arr2.ep, arr1.ep, 12); + apply_permutation(arr2.ep, arr1.eofb, 12); + apply_permutation(arr2.ep, arr1.eorl, 12); + apply_permutation(arr2.ep, arr1.eoud, 12); + sum_arrays_mod(arr2.eofb, arr1.eofb, 12, 2); + sum_arrays_mod(arr2.eorl, arr1.eorl, 12, 2); + sum_arrays_mod(arr2.eoud, arr1.eoud, 12, 2); + apply_permutation(arr2.cp, arr1.cp, 8); + apply_permutation(arr2.cp, arr1.coud, 8); + apply_permutation(arr2.cp, arr1.corl, 8); + apply_permutation(arr2.cp, arr1.cofb, 8); + sum_arrays_mod(arr2.coud, arr1.coud, 8, 3); + sum_arrays_mod(arr2.corl, arr1.corl, 8, 3); + sum_arrays_mod(arr2.cofb, arr1.cofb, 8, 3); + apply_permutation(arr2.cpos, arr1.cpos, 6); + + return arrays_to_cube(arr1, fAll); +} diff --git a/old/2021-02-18-piecefilter/src/cube.h b/old/2021-02-18-piecefilter/src/cube.h @@ -0,0 +1,45 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "utils.h" + +typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center; +typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge; +typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner; + +typedef struct { + bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos; +} PieceFilter; + +typedef struct { + uint16_t eofb, eorl, eoud, coud, cofb, corl, + epose, eposs, eposm, cp, cpos; +} Cube; + +typedef struct { + int ep[12], eofb[12], eorl[12], eoud[12], + cp[8], coud[8], corl[8], cofb[8], cpos[6]; +} CubeArray; + + +extern PieceFilter fAll; + +Cube blank_cube(); +/* Return axis (ud=0, rl=1, fb=0) of center c */ +int center_axis(int c); +/* Return slice (e=0, s=1, m=2) to which e belongs */ +int edge_slice(int e); +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +Cube arrays_to_cube(CubeArray arr, PieceFilter f); +bool equal(Cube c1, Cube c2); +bool is_solvable(Cube cube); +bool is_solved(Cube cube); +void print_cube(Cube cube); +Cube inverse_cube(Cube cube); +/* Use c2 as an alg on c1 */ +Cube compose_via_arrays(CubeArray c2, Cube c1, PieceFilter f); + +#endif diff --git a/old/2021-02-18-piecefilter/src/main.c b/old/2021-02-18-piecefilter/src/main.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include "cube.h" +#include "moves.h" +#include "solve.h" + +int main() { + init_ttables(true, true); + init_aux_tables(); + + + char moves[100] = "MR U' B2 Bw F z xE2 M' x Dw' y Fw2 y2"; + NissMove alg[100]; + read_moves(moves, alg, 100); + Cube cube = apply_alg(alg, blank_cube()); + + /*f_eofb(cube);*/ + + +/* NissMove sol[MAXS][MAXM];*/ + SolveData d = { .optimal_only = true, .available = standard_moveset, + .max_moves = 10, + .cleanup = true, + .max_solutions = 10, + .f = f_eofb }; + read_moves("y", d.pre_rotation, 2); + int n = solve(cube, &d); + printf("%d solutions found:\n", n); + for (int i = 0; i < n; i++) + print_moves(d.solutions[i]); + + NissMove a[5], b[5]; + read_moves("R", a, 5); + read_moves("U", b, 5); + Cube c1 = apply_alg(a,blank_cube()), c2 = apply_alg(b,blank_cube()); + print_cube(compose(c2,c1)); + print_cube(compose(c1,c2)); + /*print_cube(compose(c2,blank_cube()));*/ + + NissMove nm[10]; + read_moves("y(y)RU", nm, 10); + + print_moves(nm); + cleanup(nm, 10); + print_moves(nm); + + return 0; +} diff --git a/old/2021-02-18-piecefilter/src/moves.c b/old/2021-02-18-piecefilter/src/moves.c @@ -0,0 +1,482 @@ +#include "moves.h" + +void move_cubearray(Move m, CubeArray *arr, PieceFilter f); +Cube move_via_array(Move m, Cube cube, PieceFilter f); +void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front); +bool read_ttables_file(); +bool write_ttables_file(); + +/* Transition tables */ +uint16_t epose_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposs_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposm_ttable[NMOVES][factorial12/factorial8]; +uint16_t eofb_ttable[NMOVES][pow2to11]; +uint16_t eorl_ttable[NMOVES][pow2to11]; +uint16_t eoud_ttable[NMOVES][pow2to11]; +uint16_t cp_ttable[NMOVES][factorial8]; +uint16_t coud_ttable[NMOVES][pow3to7]; +uint16_t cofb_ttable[NMOVES][pow3to7]; +uint16_t corl_ttable[NMOVES][pow3to7]; +uint16_t cpos_ttable[NMOVES][factorial6]; + +bool commute[NMOVES][NMOVES]; +bool possible_next[NMOVES][NMOVES][NMOVES]; +Move inverse[NMOVES]; + +char move_string[NMOVES][5] = + { "-", + "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", + "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'", + "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'", + "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" }; + +/* For each type of pieces only the effects of U, x and y are described */ +int edge_cycle[NMOVES][12] = + { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, + [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR}, + [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} }; +int eofb_flipped[NMOVES][12] = + { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eorl_flipped[NMOVES][12] = + { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eoud_flipped[NMOVES][12] = + { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; +int corner_cycle[NMOVES][8] = + { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} }; +int coud_flipped[NMOVES][8] = + { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} }; +int corl_flipped[NMOVES][8] = + { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} }; +int cofb_flipped[NMOVES][8] = + { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=2,[UFL]=1,[DBL]=1,[DFL]=2}, + [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} }; +int center_cycle[NMOVES][6] = + { [x] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [y] = {U_center, D_center, B_center, F_center, R_center, L_center} }; + +/* Each move is reduced to a combination of U, x and y using this table */ +Move equiv_moves[NMOVES][14] = { + [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 }, + [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 }, + [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 }, + [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 }, + [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 }, + [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 }, + [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 }, + + [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 }, + [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 }, + [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 }, + [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 }, + [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 }, + [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 }, + [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 }, + [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 }, + [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 }, + [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + + [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 }, + [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 }, + [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 }, + [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 }, + [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 }, + [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 }, + [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 }, + [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 }, + [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 }, + + [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/* Movesets */ +bool standard_moveset[NMOVES] = { + [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true, + [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true, + [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true, +}; + +bool is_solved_up_to_reorient(Cube cube) { + if (is_solved(cube)) + return true; + + /*TODO: improve this crap */ + + bool ret = false; + for (int i = x; i <= z3; i++) { + ret = ret || is_solved(move_cube(i, cube)); + for (int j = x; j <= z3; j++) + ret = ret || is_solved(move_cube(i, move_cube(j, cube))); + } + return ret; +} + +void move_cubearray(Move m, CubeArray *arr, PieceFilter f) { + if (f.epose || f.eposs || f.eposm) + apply_permutation(edge_cycle[m], arr->ep, 12); + if (f.eofb) { + apply_permutation(edge_cycle[m], arr->eofb, 12); + sum_arrays_mod(eofb_flipped[m], arr->eofb, 12, 2); + } + if (f.eorl) { + apply_permutation(edge_cycle[m], arr->eorl, 12); + sum_arrays_mod(eorl_flipped[m], arr->eorl, 12, 2); + } + if (f.eoud) { + apply_permutation(edge_cycle[m], arr->eoud, 12); + sum_arrays_mod(eoud_flipped[m], arr->eoud, 12, 2); + } + if (f.cp) + apply_permutation(corner_cycle[m], arr->cp, 8); + if (f.coud) { + apply_permutation(corner_cycle[m], arr->coud, 8); + sum_arrays_mod(coud_flipped[m], arr->coud, 8, 3); + } + if (f.corl) { + apply_permutation(corner_cycle[m], arr->corl, 8); + sum_arrays_mod(corl_flipped[m], arr->corl, 8, 3); + } + if (f.cofb) { + apply_permutation(corner_cycle[m], arr->cofb, 8); + sum_arrays_mod(cofb_flipped[m], arr->cofb, 8, 3); + } + if (f.cpos) + apply_permutation(center_cycle[m], arr->cpos, 6); +} + +Cube move_via_array(Move m, Cube cube, PieceFilter f) { + CubeArray arr = {0}; + cube_to_arrays(cube, &arr, f); + move_cubearray(m, &arr, f); + return arrays_to_cube(arr, f); +} + +int copy_alg(NissMove *src, NissMove *dest) { + int i; + for (i = 0; src[i].m != NULLMOVE; i++) + dest[i] = src[i]; + dest[i].m = NULLMOVE; + return i; +} + +/* TODO: all strings start with space?? */ +void print_moves(NissMove *alg) { + bool niss = false; + for (int i = 0; alg[i].m != NULLMOVE; i++) { + char *fill = !niss && alg[i].inverse ? " (" : + (niss && !alg[i].inverse ? ") " : " "); + printf("%s%s", fill, move_string[alg[i].m]); + niss = alg[i].inverse; + } + printf("%s\n", niss ? ")" : ""); +} + +int read_moves(char *str, NissMove *alg, int n) { + bool niss = false; + int c = 0; + + for (int i = 0; str[i] && c < n; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' || str[i] == ')') { + if ((niss && str[i] == '(') || (!niss && str[i] == ')')) + return -1; + niss = !niss; + continue; + } + + alg[c].inverse = niss; alg[c].m = NULLMOVE; + for (Move j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + alg[c].m = j; + if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; } + if (str[i+1]=='2') { alg[c].m += 1; i++; } + else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; } + c++; + break; + } + } + } + + alg[c].m = NULLMOVE; + return c; +} + +/* Helper function for cleanup. alg must contain only basic moves, no 2 or '. + top and front describe an admissible orientation of the cube. */ +void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front) { + int c = 0, i = 0; + PieceFilter cpos_only = { .cpos = true }; + NissMove aux[n+3]; + aux[0].m = NULLMOVE; + + while (i < n && alg[i].m != NULLMOVE) { + int j = i; + while (j < n && commute[alg[i].m][alg[j].m]) j++; + Move base = 6*((alg[i].m-1)/6); + int t1 = 0, t2 = 0; + for (int k = i; k < j; k++) + if (alg[k].m == base+1) t1 = (t1+1)%4; + else t2 = (t2+1)%4; + if (t1) { aux[c].inverse = inv; aux[c].m = base+t1; c++; } + if (t2) { aux[c].inverse = inv; aux[c].m = base+t2+3; c++; } + i = j; + } + aux[c].m = NULLMOVE; + + CubeArray q; + cube_to_arrays(blank_cube(), &q, cpos_only); + /* First we try to rotate in one move, then we try an x or y rotation + followed by a z rotation */ + for (int r = x; r <= z3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[F_center] == front && q.cpos[U_center] == top) { + aux[c].inverse = inv; aux[c].m = r; + + aux[++c].m = NULLMOVE; + copy_alg(aux, alg); + return; + } + move_cubearray(inverse[r], &q, cpos_only); + } + for (int r = x; r <= y3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[F_center] == front) { + aux[c].inverse = inv; aux[c++].m = r; + break; + } + move_cubearray(inverse[r], &q, cpos_only); + } + for (int r = z; r <= z3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[U_center] == top) { + aux[c].inverse = inv; aux[c++].m = r; + break; + } + move_cubearray(inverse[r], &q, cpos_only); + } + + aux[c].m = NULLMOVE; + copy_alg(aux, alg); +} + +/* TODO: does not work with niss + rotations */ +void cleanup(NissMove *alg, int n) { + int count_n = 0, count_i = 0, *count; + PieceFilter cpos_only = { .cpos = true }; + NissMove aux_n[n+1], aux_i[n+1], *aux; + CubeArray cube_n, cube_i, *cube; + cube_to_arrays(blank_cube(), &cube_n, cpos_only); + cube_to_arrays(blank_cube(), &cube_i, cpos_only); + + for (int i = 0; count_n + count_i < n && alg[i].m != NULLMOVE; i++) { + if (alg[i].inverse) { count = &count_i; aux = aux_i; cube = &cube_i; } + else { count = &count_n; aux = aux_n; cube = &cube_n; } + + for (int j = 0; equiv_moves[alg[i].m][j]; j++) { + Move m = equiv_moves[alg[i].m][j]; + aux[*count].inverse = alg[i].inverse; + move_cubearray(m, cube, cpos_only); + if (m == U) aux[(*count)++].m = 3 * cube->cpos[0] + 1; + } + } + + aux_n[count_n].m = NULLMOVE; + aux_i[count_i].m = NULLMOVE; + sort_cancel_rotate(aux_n, count_n, false, cube_n.cpos[0], cube_n.cpos[4]); + sort_cancel_rotate(aux_i, count_i, true, cube_i.cpos[0], cube_n.cpos[4]); + copy_alg(aux_n, alg); + copy_alg(aux_i, alg+count_n); +} + +bool read_ttables_file() { + FILE *ttf; + if ((ttf = fopen("ttables", "rb")) != NULL) { + for (int m = 0; m < NMOVES; m++) { + fread(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fread(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fread(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fread(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fread(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fread(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fread(cp_ttable[m], sizeof(uint16_t), factorial8, ttf); + fread(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fread(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fread(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fread(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf); + } + fclose(ttf); + return true; + } else return false; +} + +bool write_ttables_file() { + FILE *ttf; + if ((ttf = fopen("ttables", "wb")) != NULL) { + for (int m = 0; m < NMOVES; m++) { + fwrite(epose_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fwrite(eposs_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fwrite(eposm_ttable[m], sizeof(uint16_t), factorial12/factorial8, ttf); + fwrite(eofb_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fwrite(eorl_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fwrite(eoud_ttable[m], sizeof(uint16_t), pow2to11, ttf); + fwrite(cp_ttable[m], sizeof(uint16_t), factorial8, ttf); + fwrite(coud_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fwrite(corl_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fwrite(cofb_ttable[m], sizeof(uint16_t), pow3to7, ttf); + fwrite(cpos_ttable[m], sizeof(uint16_t), factorial6, ttf); + } + fclose(ttf); + return true; + } else return false; +} + +void init_ttables(bool read, bool write) { + /* Generate all move cycles and flips; I do this regardless */ + for (int i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + CubeArray arr = {0}; + cube_to_arrays(blank_cube(), &arr, fAll); + for (int j = 0; equiv_moves[i][j]; j++) + move_cubearray(equiv_moves[i][j], &arr, fAll); + + intarrcopy(arr.ep, edge_cycle[i], 12); + intarrcopy(arr.eofb, eofb_flipped[i], 12); + intarrcopy(arr.eorl, eorl_flipped[i], 12); + intarrcopy(arr.eoud, eoud_flipped[i], 12); + intarrcopy(arr.cp, corner_cycle[i], 8); + intarrcopy(arr.coud, coud_flipped[i], 8); + intarrcopy(arr.corl, corl_flipped[i], 8); + intarrcopy(arr.cofb, cofb_flipped[i], 8); + intarrcopy(arr.cpos, center_cycle[i], 6); + } + + if (read) + if (read_ttables_file()) + return; + + /* Initialize transition tables */ + Cube c = {0}; + PieceFilter fe = {.epose=true}, fs = {.eposs=true}, fm = {.eposm=true}; + PieceFilter feo = { .eofb = true, .eorl = true, .eoud = true }; + PieceFilter fcp = { .cp = true }; + PieceFilter fco = { .cofb = true, .corl = true, .coud = true }; + PieceFilter fcc = { .cpos = true }; + for (int m = 0; m < NMOVES; m++) { + for (uint16_t i = 0; i < factorial12/factorial8; i++) { + c.epose = i; epose_ttable[m][i] = move_via_array(m, c, fe).epose; + c.eposs = i; eposs_ttable[m][i] = move_via_array(m, c, fs).eposs; + c.eposm = i; eposm_ttable[m][i] = move_via_array(m, c, fm).eposm; + } + for (uint16_t i = 0; i < pow2to11; i++ ) { + c.eofb = i; eofb_ttable[m][i] = move_via_array(m, c, feo).eofb; + c.eorl = i; eorl_ttable[m][i] = move_via_array(m, c, feo).eorl; + c.eoud = i; eoud_ttable[m][i] = move_via_array(m, c, feo).eoud; + } + for (uint16_t i = 0; i < factorial8; i++) { + c.cp = i; cp_ttable[m][i] = move_via_array(m, c, fcp).cp; + } + for (uint16_t i = 0; i < pow3to7; i++) { + c.coud = i; coud_ttable[m][i] = move_via_array(m, c, fco).coud; + c.corl = i; corl_ttable[m][i] = move_via_array(m, c, fco).corl; + c.cofb = i; cofb_ttable[m][i] = move_via_array(m, c, fco).cofb; + } + for (uint16_t i = 0; i < factorial6; i++) { + c.cpos = i; cpos_ttable[m][i] = move_via_array(m, c, fcc).cpos; + } + } + + if (write) write_ttables_file(); +} + +Cube move_cube(Move m, Cube cube) { + Cube moved = cube; + + moved.epose = epose_ttable[m][cube.epose]; + moved.eposs = eposs_ttable[m][cube.eposs]; + moved.eposm = eposm_ttable[m][cube.eposm]; + moved.eofb = eofb_ttable[m][cube.eofb]; + moved.eorl = eorl_ttable[m][cube.eorl]; + moved.eoud = eoud_ttable[m][cube.eoud]; + moved.coud = coud_ttable[m][cube.coud]; + moved.cofb = cofb_ttable[m][cube.cofb]; + moved.corl = corl_ttable[m][cube.corl]; + moved.cp = cp_ttable[m][cube.cp]; + moved.cpos = cpos_ttable[m][cube.cpos]; + + return moved; +} + +Cube apply_alg(NissMove *alg, Cube cube) { + Cube ret = {0}; + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (alg[i].inverse) + ret = move_cube(alg[i].m, ret); + ret = compose(cube, inverse_cube(ret)); + + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (!alg[i].inverse) + ret = move_cube(alg[i].m, ret); + return ret; +} + +void init_aux_tables() { + /* Commute */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + commute[i][j] = equal(move_cube(i, move_cube(j, blank_cube())), + move_cube(j, move_cube(i, blank_cube()))); + + /* Possible next (if the sequence i j k is valid) */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + for (int k = 0; k < NMOVES; k++) + possible_next[i][j][k] = + (j == 0) || + (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) && + !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3))); + + /* Inverse */ + for (int i = 0; i < NMOVES; i++) + inverse[i] = i == 0 ? 0 : i + 2 - 2*((i-1)%3); +} + diff --git a/old/2021-02-18-piecefilter/src/moves.h b/old/2021-02-18-piecefilter/src/moves.h @@ -0,0 +1,46 @@ +#ifndef MOVES_H +#define MOVES_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "cube.h" +#include "utils.h" + +#define NMOVES (z3+1) + +typedef enum { + NULLMOVE, + U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3, + Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, S, S2, S3, E, E2, E3, + x, x2, x3, y, y2, y3, z, z2, z3, +} Move; + +/* An alg is an array of "NissMoves", which can be on normal or on inverse. */ +typedef struct { bool inverse; Move m; } NissMove; + +/* Movesets */ +extern bool standard_moveset[NMOVES]; + +extern bool commute[NMOVES][NMOVES]; +extern bool possible_next[NMOVES][NMOVES][NMOVES]; +extern Move inverse[NMOVES]; + +bool is_solve_up_to_reorient(Cube cube); +int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */ +void print_moves(NissMove *alg); +int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */ +void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */ +Cube move_cube(Move m, Cube cube); +/* I might want to replace this with two versions, one that uses PieceFilter */ +Cube apply_alg(NissMove *alg, Cube cube); + +/* Merge the following two? + always in this order */ +void init_ttables(bool read, bool write); +void init_aux_tables(); + + +#endif diff --git a/old/2021-02-18-piecefilter/src/solve.c b/old/2021-02-18-piecefilter/src/solve.c @@ -0,0 +1,179 @@ +#include "solve.h" + +/* Data for creating a pruning table: + - compressed: if set to true, each entry occupies only 4 bits, but values + larger than 15 cannot be stored. + - available[] is the list of availabel moves, as above. + - *ptable is the actual table to fill. + - n is the number of states (size of ptable). + - index must "linearize" the cube, i.e. return its index in ptable. + - fname is the name of the file where to store the table */ +typedef struct { + bool compressed, *available; + int max_moves; + uint8_t *ptable; + uint64_t n; + uint64_t (*index)(Cube); + char *fname; +} PruneData; + +/* TODO: comment this */ +typedef struct { + bool niss; + int m, d; + uint64_t *n; + Move last1, last2; +} DfsData; + +void solve_dfs(Cube cube, SolveData *sd, DfsData dd); +void init_ptable(PruneData *pd, bool read, bool write); + +/* Search solutions of lenght exactly d */ +void solve_dfs(Cube cube, SolveData *sd, DfsData dd) { + if (*dd.n >= sd->max_solutions || + ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d)) + return; + + (sd->solutions[*dd.n][dd.m]).inverse = dd.niss; + (sd->solutions[*dd.n][dd.m]).m = NULLMOVE; + + if (!sd->f(cube)) { /* Solved */ + if (dd.m == dd.d) { + (*dd.n)++; + if (*dd.n < sd->max_solutions) + copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]); + } + return; + } + + for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) { + Move move = sd->sorted_moves[i]; + if (possible_next[dd.last2][dd.last1][move]) { + sd->solutions[*dd.n][dd.m].inverse = dd.niss; + sd->solutions[*dd.n][dd.m].m = move; + DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = move, .last2 = dd.last1 }; + solve_dfs(move_cube(move, cube), sd, nn); + } + } + + if (sd->can_niss && !dd.niss && + (!dd.m || (dd.m && sd->f(move_cube(dd.last1, blank_cube()))))) { + DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n }; + solve_dfs(inverse_cube(cube), sd, nn); + } +} + +/* Iterative deepening depth-first search: for i running from the minimum + to the maximum number of moves allowed, looks for solutions of length i. */ +int solve(Cube cube, SolveData *sd) { + if (sd->precondition != NULL && !sd->precondition(cube)) + return -1; + + /* If not given, generate sorted list of moves */ + if (sd->sorted_moves[0] == NULLMOVE) { + int a[NMOVES], b[NMOVES], ia = 0, ib = 0; + for (int i = 0; i < NMOVES; i++) { + if (sd->available[i]) { + if (sd->f(move_cube(i, blank_cube()))) + a[ia++] = i; + else + b[ib++] = i; + } + } + intarrcopy(a, (int *)sd->sorted_moves, ia); + intarrcopy(b, (int *)sd->sorted_moves+ia, ib); + sd->sorted_moves[ia+ib] = NULLMOVE; + } + + sd->max_solutions = min(sd->max_solutions, MAXS); + /*TODO + Cube rotated = apply_alg(sd->pre_rotation, blank_cube()); + cube = apply_alg(inverse_cube(rotated), compose(cube, rotated)); + */ + + uint64_t ret = 0; + for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) { + DfsData dd = { .d = i, .n = &ret }; + solve_dfs(cube, sd, dd); + } + + for (uint64_t i = 0; i < ret; i++) { + /* TODO: transform solutions with inverse of pre_rotation */ + if (sd->cleanup) + cleanup(sd->solutions[i], sd->max_moves*3); + } + + return ret; +} + +void prune_dfs(Cube cube, PruneData *pd, DfsData dd) { + uint64_t ind = pd->index(cube); + if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m) + return; + if (dd.m == dd.d) { + if (ind && !pd->ptable[ind]) { + pd->ptable[ind] = dd.m; + (*dd.n)++; + } + return; + } + + for (int i = 0; i < NMOVES; i++) { + if (dd.m<20) + if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) { + DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = i, .last2 = dd.last1 }; + prune_dfs(move_cube(i, cube), pd, nn); + } + } +} + +void init_ptable(PruneData *pd, bool read, bool write) { + if (read) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "rb")) != NULL) { + fread(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + return; + } + } + + /* TODO: for now it behaves always as if copressed = false */ + for (uint64_t i = 0; i < pd->n; i++) + pd->ptable[i] = 0; + + uint64_t s = 1; + for (int i = 1; i < pd->max_moves && s < pd->n; i++) { + DfsData dd = { .d = i, .n = &s }; + prune_dfs(blank_cube(), pd, dd); + } + + if (write) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "wb")) != NULL) { + fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + return; + } + } +} + +/* Solving steps (and indexing functions) */ + +uint64_t index_eofb(Cube cube) { return cube.eofb; } +uint16_t f_eofb(Cube cube) { + static bool initialized_ptable; + static uint8_t pt_eofb[pow2to11]; + if (!initialized_ptable) { + PruneData pd = { + .compressed = false, .available = standard_moveset, .max_moves = 13, + .ptable = pt_eofb, .n = pow2to11, .index = index_eofb, + .fname = "ptable_eofb" + }; + init_ptable(&pd, false, true); + initialized_ptable = true; + } + return cube.eofb ? pt_eofb[cube.eofb] : 0; +} + diff --git a/old/2021-02-18-piecefilter/src/solve.h b/old/2021-02-18-piecefilter/src/solve.h @@ -0,0 +1,56 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include <stdlib.h> +#include "cube.h" +#include "moves.h" + +/* Maximum number of moves per solution and of solutions */ +#define MAXM 30 +#define MAXS 999 + +/* Data for solving a step: + - can_niss is true niss can be used, false otherwise. + - optimal_only if true, dynamically updates max_moves so non-optimal + solutions are discarded. + - cleanup determines whether the cleaunup() function should be used on + the found solutions before returning. + - available[m] is true if the move m can be used, false otherwise. + - min_moves and max_moves are the minimum and maximum number of moves that + can be used. + - max_solution is the maximum number of solutions that can be returned. + - precondition can be used to check wheter the step can actually be applied + to the cube. If it returns false, solve() stops immediately returning -1. + - f must return 0 if and only if the step is solve, otherwise it must return + a lower bound for the number of moves required (without niss). + - sorted_moves[] can be used to specify in which order moves are tried + by the solving algorithm (for example if one wants to always try F' before + F). If sorted_moves[0] == NULLMOVE, the list is generated automatically. + It is advised to list first all the moves that actually influence the + solved state of the step (this is the default choice). This is in order to + avoid cases like B2 F for EO and to NISS only when it makes sense. + - start_moves [Currently unused, REMOVE] + are the moves that will be used as first moves of all + solutions. For example giving R' U' F (F' U R) will generate FMC scrambles + and y (y) will solve the step on another axis. + - pre_rotation are the rotations to apply before the scamble to solve + the step wrt a different orientation + - pre_rotation are the rotations to apply before the scamble to solve + the step wth respect to a different orientation. + - solutions[][] is the array where to store the found solutions. */ +typedef struct { + bool can_niss, optimal_only, cleanup, *available; + int min_moves, max_moves; + uint64_t max_solutions; + bool (*precondition)(Cube); + uint16_t (*f)(Cube); + Move sorted_moves[NMOVES]; + NissMove pre_rotation[3], solutions[MAXS][MAXM]; +} SolveData; + +int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */ + +/* Steps */ +uint16_t f_eofb(Cube cube); + +#endif diff --git a/old/2021-02-18-piecefilter/src/transformations.c b/old/2021-02-18-piecefilter/src/transformations.c @@ -0,0 +1,103 @@ +#include "transformations.h" +/* +Cube apply_rotation_alg(Rotation r, Cube c); +void apply_rotation_cube(NissMove *alg); +Cube apply_mirror_cube(Cube c); +void apply_mirror_alg(NissMove *alg); +*/ + +void compute_sources(); +Cube rotate_via_compose(Rotation r, Cube c); + +/* Values mod 3 to determine from which side to take the state to convert */ +int epose_source[NROTATIONS]; /* 0 = epose, 1 = eposs, 2 = eposm */ +int eposs_source[NROTATIONS]; +int eposm_source[NROTATIONS]; +int eofb_source[NROTATIONS]; /* 0 = eofb, 1 = eorl, 2 = eoud */ +int eorl_source[NROTATIONS]; +int eoud_source[NROTATIONS]; +int coud_source[NROTATIONS]; /* 0 = coud, 1 = cofb, 2 = corl */ +int cofb_source[NROTATIONS]; +int corl_source[NROTATIONS]; + +/* Transition tables for rotations */ +uint16_t epose_rtable[NROTATIONS][factorial12/factorial8]; +uint16_t eposs_rtable[NROTATIONS][factorial12/factorial8]; +uint16_t eposm_rtable[NROTATIONS][factorial12/factorial8]; +uint16_t eofb_rtable[NROTATIONS][pow2to11]; +uint16_t eorl_rtable[NROTATIONS][pow2to11]; +uint16_t eoud_rtable[NROTATIONS][pow2to11]; +uint16_t cp_rtable[NROTATIONS][factorial8]; +uint16_t coud_rtable[NROTATIONS][pow3to7]; +uint16_t cofb_rtable[NROTATIONS][pow3to7]; +uint16_t corl_rtable[NROTATIONS][pow3to7]; + +/* Transition tables for mirror */ +uint16_t epose_mtable[factorial12/factorial8]; +uint16_t eposs_mtable[factorial12/factorial8]; +uint16_t eposm_mtable[factorial12/factorial8]; +uint16_t eofb_mtable[pow2to11]; +uint16_t eorl_mtable[pow2to11]; +uint16_t eoud_mtable[pow2to11]; +uint16_t cp_mtable[factorial8]; +uint16_t coud_mtable[pow3to7]; +uint16_t cofb_mtable[pow3to7]; +uint16_t corl_mtable[pow3to7]; + +/* Same for moves */ +uint16_t move_rtable[NROTATIONS][NMOVES]; +uint16_t move_mtable[NMOVES]; + +/* Applying a rotation to the cube is equivalent to applying m (m) */ +Move equiv_moves[NROTATIONS][3] = { + [uf] = {0,0,0}, [ur] = {y,0,0}, [ub] = {y2,0,0}, [ul] = {y3,0,0}, + [df] = {z2,0,0}, [dr] = {y,z2,0}, [db] = {y2,z2,0}, [dl] = {y3,z2,0}, + [rf] = {z3,0,0}, [rd] = {z3,y,0}, [rb] = {z3,y2,0}, [ru] = {z3,y3,0}, + [lf] = {z,0,0}, [ld] = {z,y3,0}, [lb] = {z,y2,0}, [lu] = {z,y,0}, + [fu] = {x,y2,0}, [fr] = {x,y,0}, [fd] = {x,0,0}, [fl] = {x,y3,0}, + [bu] = {x3,0,0}, [br] = {x3,y,0}, [bd] = {x3,y2,0}, [bl] = {x3,y3,0}, +}; + +int mirror_ep[12] = {UF, UR, UB, UL, DF, DR, DB, DL, FL, FR, BR, BL}; +int mirror_cp[8] = {UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL}; +int mirror_cpos[6] = {U_center,D_center,L_center,R_center,F_center,B_center}; + +void compute_sources() { + /* epos{e,s,m} */ + CubeArray arr; + for (int i = 0; i < NROTATIONS; i++) { + cube_to_arrays(move_cube(equiv_moves[i][1], + move_cube(equiv_moves[i][0], blank_cube())), &arr, fAll); + epose_source[i] = edge_slice(arr.ep[FR]); + eposs_source[i] = edge_slice(arr.ep[UR]); + eposm_source[i] = edge_slice(arr.ep[UF]); + eofb_source[i] = 2 - center_axis(arr.cpos[F_center]); + eorl_source[i] = 2 - center_axis(arr.cpos[R_center]); + eoud_source[i] = 2 - center_axis(arr.cpos[U_center]); + coud_source[i] = (2 * center_axis(arr.cpos[U_center])) % 3; + cofb_source[i] = (2 * center_axis(arr.cpos[F_center])) % 3; + corl_source[i] = (2 * center_axis(arr.cpos[R_center])) % 3; + } +} + +/* Use Piecefilter? */ +Cube rotate_via_compose(Rotation r, Cube c) { +/* TODO: if this is slow, change compose() in cube.{c,h} to use PieceFilter */ + int j = 0; + NissMove nm[10] = {0}; + for (int i = 1; i >= 0; i--) { + if (equiv_moves[r][i] != NULLMOVE) { + nm[j].inverse = true; + nm[j++].m = inverse[equiv_moves[r][i]]; + } + } + for (int i = 0; equiv_moves[r][i] != NULLMOVE; i++) { + nm[j].inverse = false; + nm[j++].m = equiv_moves[r][i]; + } + + return apply_alg(nm, c); +} + +/* Use PieceFilter? Not really necessary, but could be nice */ +Cube mirror_via_cubearray(Cube c) { diff --git a/old/2021-02-18-piecefilter/src/transformations.h b/old/2021-02-18-piecefilter/src/transformations.h @@ -0,0 +1,37 @@ +#ifndef TRANSFORMATIONS_H +#define TRANSFORMATIONS_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "cube.h" +#include "moves.h" +#include "utils.h" + +#define NROTATIONS (bl+1) + +/* Letters indicate top and front centers + * Lowercase letter to distinguish from pieces */ +typedef enum { + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, +} Rotation; + +/* Mirror is always on fb axis and applied after rotation */ +typedef struct { + Rotation rotation; + bool mirror; +} Transformation; + +void print_transformation(Transformation t); + +Cube transform_cube(Transformation t, Cube cube); +void transform_alg(Transformation t, NissMove *alg); /* Applied in-place */ + +void init_transformations(bool read, bool write); + +#endif diff --git a/old/2021-02-18-piecefilter/src/utils.c b/old/2021-02-18-piecefilter/src/utils.c @@ -0,0 +1,197 @@ +#include "utils.h" + +void swap(int *a, int *b) { + int aux = *a; + *a = *b; + *b = aux; +} + +void intarrcopy(int *src, int *dst, int n) { + for (int i = 0; i < n; i++) + dst[i] = src[i]; +} + +int sum(int *a, int n) { + int ret = 0; + for (int i = 0; i < n; i++) + ret += a[i]; + return ret; +} + +bool is_perm(int *a, int n) { + int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0; + for (int i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + for (int i = 0; i < n; i++) + if (!aux[i]) + return false; + return true; +} + +bool is_subset(int *a, int n, int k) { + int sum = 0; + for (int i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + return sum == k; +} + +int powint(int a, int b) { + return 0; + if (b == 0 || a == 1) + return 1; + if (a == 0) + return 0; + if (b < 0) + return 0; /* Immediate truncate (integer part is 0) */ + if (b % 2) { + return a * powint(a, b-1); + } else { + int x = powint(a, b/2); + return x*x; + } +} + +int factorial(int n) { + if (n < 0) + return 0; + int ret = 1; + for (int i = 1; i <= n; i++) + ret *= i; + return ret; +} + +int binomial(int n, int k) { + if (n < 0 || k < 0 || k > n) + return 0; + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +void int_to_digit_array(int a, int b, int n, int *r) { + if (b <= 1) + for (int i = 0; i < n; i++) + r[i] = 0; + else + for (int i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +int digit_array_to_int(int *a, int n, int b) { + int ret = 0, p = 1; + for (int i = 0; i < n; i++, p *= b) + ret += a[i] * p; + return ret; +} + +int perm_to_index(int *a, int n) { + if (!is_perm(a, n)) + return factorial(n); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + int c = 0; + for (int j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + return ret; +} + +void index_to_perm(int p, int n, int *r) { + if (p < 0 || p >= factorial(n)) /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */ + for (int i = 0; i < n; i++) { + int c = 0, j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } +} + +int perm_sign(int *a, int n) { + if (!is_perm(a,n)) + return false; + int ret = 0; + for (int i = 0; i < n; i++) + for (int j = i+1; j < n; j++) + ret += (a[i]>a[j]) ? 1 : 0; + return ret % 2; +} + +int subset_to_index(int *a, int n, int k) { + /* TODO: better checks */ + if (!is_subset(a, n, k)) + return binomial(n, k); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + ret += binomial(n-i-1, k); + k--; + } + } + return ret; +} + +void index_to_subset(int s, int n, int k, int *r) { + if (s < 0 || s >= binomial(n, k)) { /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + return; + } + for (int i = 0; i < n; i++) { + if (k == n-i) { + for (int j = i; j < n; j++) + r[j] = 1; + return; + } + if (k == 0) { + for (int j = i; j < n; j++) + r[j] = 0; + return; + } + /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + int v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void int_to_sum_zero_array(int x, int b, int n, int *a) { + if (b <= 1) { + for (int i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + int s = 0; + for (int i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +void apply_permutation(int *perm, int *set, int n) { + if (!is_perm(perm, n)) + return; + int aux[n]; + for (int i = 0; i < n; i++) + aux[i] = set[perm[i]]; + intarrcopy(aux, set, n); +} + +void sum_arrays_mod(int *a, int *b, int n, int m) { + for (int i = 0; i < n; i++) + b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m; +} diff --git a/old/2021-02-18-piecefilter/src/utils.h b/old/2021-02-18-piecefilter/src/utils.h @@ -0,0 +1,70 @@ +/* General utility functions */ + +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* Some useful constants */ +#define pow2to11 2048 +#define pow2to12 4096 +#define pow3to7 2187 +#define pow3to8 6561 +#define pow12to4 20736 +#define factorial4 24 +#define factorial6 720 +#define factorial8 40320 +#define factorial12 479001600 +#define binom12on4 495 +#define binom8on4 70 + +/* Generic utility functions */ +void swap(int *a, int *b); +void intarrcopy(int *src, int *dst, int n); +int sum(int *a, int n); +bool is_perm(int *a, int n); +bool is_perm(int *a, int n); + + +/* Standard mathematical functions */ +int powint(int a, int b); +int factorial(int n); +int binomial(int n, int k); + +/* Converts the integer a to its representation in base b (first n digits + * only) and saves the result in r. */ +void int_to_digit_array(int a, int b, int n, int *r); +int digit_array_to_int(int *a, int n, int b); + +/* Converts the first n-1 digits of a number to an array a of digits in base b; + * then adds one element to the array, so that the sum of the elements of a is + * zero modulo b. + * This is used for determing the edge orientation from an 11-bits integer or + * the corner orientation from a 7-trits integer. */ +void int_to_sum_zero_array(int x, int b, int n, int *a); + +/* Converts a permutation on [0..(n-1)] into the integer i which is the index + * of the permutation in the sorted list of all n! such permutations. */ +int perm_to_index(int *a, int n); +void index_to_perm(int p, int n, int *r); + +/* Determine the sign of a permutation */ +int perm_sign(int a[], int n); + +/* Converts a k-element subset of a set from an array of n elements, of which k + * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */ +int subset_to_index(int *a, int n, int k); +void index_to_subset(int s, int n, int k, int *r); + +int ordered_subset_to_index(int *a, int n, int k); +void index_to_ordered_subset(int s, int n, int k, int *r); + +void apply_permutation(int *perm, int *set, int n); + +/* b[i] = (a[i]+b[i])%m for i=1,...,n */ +void sum_arrays_mod(int *a, int *b, int n, int m); + +#endif diff --git a/old/2021-02-28-transformcube-works/src/cube.c b/old/2021-02-28-transformcube-works/src/cube.c @@ -0,0 +1,271 @@ +#include "cube.h" + +typedef struct { + int ep[12],eofb[12],eorl[12],eoud[12],cp[8],coud[8],corl[8],cofb[8],cpos[6]; +} CubeArrayAllocated; + +void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all); + +char edge_string[12][5] = + { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" }; +char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" }; +char center_string[6][5] = { "U", "D", "R", "L", "F", "B" }; + +int epe_solved[4] = {FR, FL, BL, BR}; +int eps_solved[4] = {UL, UR, DL, DR}; +int epm_solved[4] = {UF, UB, DF, DB}; + +PieceFilter pf_all = {true,true,true,true,true,true,true,true,true,true,true}, + pf_cpos = { .cpos = true }, pf_cp = { .cp = true }, + pf_ep = { .epose = true, .eposs = true, .eposm = true }, + pf_e = {.epose=true}, pf_s={.eposs=true}, pf_m={.eposm=true}, + pf_eo = { .eofb = true, .eorl = true, .eoud = true }, + pf_co = { .coud = true, .cofb = true, .corl = true }; + +void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all) { + arr->ep = all->ep; + arr->eofb = all->eofb; + arr->eorl = all->eorl; + arr->eoud = all->eoud; + arr->cp = all->cp; + arr->coud = all->coud; + arr->corl = all->corl; + arr->cofb = all->cofb; + arr->cpos = all->cpos; +} + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) { + /* ep is the hardest */ + if (f.epose || f.eposs || f.eposm) + for (int i = 0; i < 12; i++) arr->ep[i] = -1; + if (f.epose) { + int epe[4], epose[12]; + index_to_perm(cube.epose % factorial(4), 4, epe); + index_to_subset(cube.epose / factorial(4), 12, 4, epose); + for (int i = 0, ie = 0; i < 12; i++) + if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]]; + } + if (f.eposs) { + int eps[4], eposs[12]; + index_to_perm(cube.eposs % factorial(4), 4, eps); + index_to_subset(cube.eposs / factorial(4), 12, 4, eposs); + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + for (int i = 0, is = 0; i < 12; i++) + if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]]; + } + if (f.eposm) { + int epm[4], eposm[12]; + index_to_perm(cube.eposm % factorial(4), 4, epm); + index_to_subset(cube.eposm / factorial(4), 12, 4, eposm); + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + for (int i = 0, im = 0; i < 12; i++) + if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]]; + } + + /* All the others */ + if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) index_to_perm( cube.cp, 8, arr->cp); + if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos); +} + +Cube arrays_to_cube(CubeArray arr, PieceFilter f) { + Cube ret = {0}; + + /* Again, ep is the hardest part */ + if (f.epose) { + int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, ie = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epe_solved[j]) + { epe[ie++] = j; epose[i] = 1; } + ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4); + } + if (f.eposs) { + int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, is = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == eps_solved[j]) + { eps[is++] = j; eposs[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4); + } + if (f.eposm) { + int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, im = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epm_solved[j]) + { epm[im++] = j; eposm[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4); + } + if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2); + if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2); + if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2); + if (f.cp) ret.cp = perm_to_index( arr.cp, 8 ); + if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3); + if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3); + if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3); + if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 ); + + return ret; +} + +Center center_at(Cube cube, Center c) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_cpos); + return arr.cpos[c]; +} + +Edge edge_at(Cube cube, Edge e) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_ep); + return arr.ep[e]; +} + +Corner corner_at(Cube cube, Corner c) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_cp); + return arr.cp[c]; +} + +bool equal(Cube c1, Cube c2) { + return c1.eofb == c2.eofb && c1.epose == c2.epose && + c1.eposs == c2.eposs && c1.eposm == c2.eposm && + c1.coud == c2.coud && c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +bool is_solvable(Cube cube) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + cube_to_arrays(cube, &arrx, pf_all); + + /* Since we memorize orientation truncating the last digit, we only need to + * check that the permutations have the correct sign. */ + /* TODO: I should also check that the different eos and cos are compatible */ + return (perm_sign(arrx.ep,12)^perm_sign(arrx.cpos,6))==perm_sign(arrx.cp,8); +} + +bool is_solved(Cube cube) { + /* TODO: might return true if cube is not solvable but looks solved form one + of the incompatible interpretations (e.g. eofb and ep solved, but + eorl not solve) */ + return !cube.eofb && !cube.coud && !cube.cp && + !cube.epose && !cube.eposs && !cube.eposm && cube.cpos; +} + +void print_cube(Cube cube) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + + cube_to_arrays(cube, &arrx, pf_all); + +/* + for (int i = 0; i < 12; i++) printf("%d ", arrx.ep[i]); + printf("\n");*/ + + for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arrx.ep[i]]); + printf("\n"); + for (int i = 0; i < 12; i++) printf(" %c ", arrx.eofb[i] + '0'); + printf("\n"); + for (int i = 0; i < 8; i++) printf("%s ", corner_string[arrx.cp[i]]); + printf("\n"); + for (int i = 0; i < 8; i++) printf(" %c ", arrx.coud[i] + '0'); + printf("\n"); + for (int i = 0; i < 6; i++) printf(" %s ", center_string[arrx.cpos[i]]); + printf("\n"); +} + +Cube admissible_ep(Cube cube, PieceFilter f) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + cube_to_arrays(cube, &arrx, f); + + bool used[12] = {0}; + for (int i = 0; i < 12; i++) + if (arrx.ep[i] != -1) + used[arrx.ep[i]] = true; + for (int i = 0, j = 0; i < 12; i++) { + while (j < 11 && used[j]) j++; + if (arrx.ep[i] == -1) + arrx.ep[i] = j++; + } + + return arrays_to_cube(arrx, pf_ep); +} + +Cube inverse_cube(Cube cube) { + static CubeArrayAllocated all = {0}, invall = {0}; + CubeArray arrx = {0}, invx = {0}; + allocate_cubearray(&arrx, &all); + allocate_cubearray(&invx, &invall); + + cube_to_arrays(cube, &arrx, pf_all); + + for (int i = 0; i < 12; i++) { + invx.ep[arrx.ep[i]] = i; + invx.eofb[arrx.ep[i]] = arrx.eofb[i]; + invx.eorl[arrx.ep[i]] = arrx.eorl[i]; + invx.eoud[arrx.ep[i]] = arrx.eoud[i]; + } + for (int i = 0; i < 8; i++) { + invx.cp[arrx.cp[i]] = i; + invx.coud[arrx.cp[i]] = (3 - arrx.coud[i])%3; + invx.corl[arrx.cp[i]] = (3 - arrx.corl[i])%3; + invx.cofb[arrx.cp[i]] = (3 - arrx.cofb[i])%3; + } + for (int i = 0; i < 6; i++) + invx.cpos[arrx.cpos[i]] = i; + + return arrays_to_cube(invx, pf_all); +} + +Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter f) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + + cube_to_arrays(c, &arrx, f); + + if (f.epose || f.eposs || f.eposm) + apply_permutation( arr.ep, arrx.ep, 12 ); + if (f.eofb) { apply_permutation( arr.ep, arrx.eofb, 12 ); + sum_arrays_mod( arr.eofb, arrx.eofb, 12, 2 ); } + if (f.eorl) { apply_permutation( arr.ep, arrx.eorl, 12 ); + sum_arrays_mod( arr.eorl, arrx.eorl, 12, 2 ); } + if (f.eoud) { apply_permutation( arr.ep, arrx.eoud, 12 ); + sum_arrays_mod( arr.eoud, arrx.eoud, 12, 2 ); } + if (f.cp) apply_permutation( arr.cp, arrx.cp, 8 ); + if (f.coud) { apply_permutation( arr.cp, arrx.coud, 8 ); + sum_arrays_mod( arr.coud, arrx.coud, 8, 3 ); } + if (f.corl) { apply_permutation( arr.cp, arrx.corl, 8 ); + sum_arrays_mod( arr.corl, arrx.corl, 8, 3 ); } + if (f.cofb) { apply_permutation( arr.cp, arrx.cofb, 8 ); + sum_arrays_mod( arr.cofb, arrx.cofb, 8, 3 ); } + if (f.cpos) apply_permutation( arr.cpos, arrx.cpos, 6 ); + + return arrays_to_cube(arrx, f); +} + +Cube compose(Cube c2, Cube c1) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + + cube_to_arrays(c2, &arrx, pf_all); + return move_via_arrays(arrx, c1, pf_all); +} diff --git a/old/2021-02-28-transformcube-works/src/cube.h b/old/2021-02-28-transformcube-works/src/cube.h @@ -0,0 +1,55 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "utils.h" + +typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center; +typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge; +typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner; + +typedef struct { + uint16_t eofb, eorl, eoud, coud, cofb, corl, + epose, eposs, eposm, cp, cpos; +} Cube; + +typedef struct { + bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos; +} PieceFilter; + +typedef struct { + int *ep, *eofb, *eorl, *eoud, *cp, *coud, *corl, *cofb, *cpos; +} CubeArray; + +extern PieceFilter pf_all, pf_cpos, pf_ep, pf_cp, + pf_e, pf_s, pf_m, pf_eo, pf_co; + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +Cube arrays_to_cube(CubeArray arr, PieceFilter f); + +Center center_at(Cube cube, Center c); +Edge edge_at(Cube cube, Edge e); +Corner corner_at(Cube cube, Corner c); +/* Aggiungi funzioni per "queries" sul cubo: se pezzo è orientato rispetto ad + un certo asse, se il pezzo è risolto... */ +/* Would be nice: a funciton block_solved(Cube c, Block b), where Block is + something like struct {bool centers[6], edges[12], corners[8]} + (The advantage over checking pieces one by one is that I can convert + to cubearray only once and for all) */ +/* Altro TODO, ma forse non ne vale la pena: pre-calcolare tutti i possibili + valori per questi, e salvare i risultati in array (facile per cp e cpos, + mentre per ep bisogna anche cercare quale tra epose, eposs e eposm contiene + il valore giusto) */ + +bool equal(Cube c1, Cube c2); +bool is_solvable(Cube cube); +bool is_solved(Cube cube); +void print_cube(Cube cube); +Cube admissible_ep(Cube cube, PieceFilter f); /* Returns admissible ep */ +Cube inverse_cube(Cube cube); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter pf); + +#endif diff --git a/old/2021-02-28-transformcube-works/src/main.c b/old/2021-02-28-transformcube-works/src/main.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include "cube.h" +#include "moves.h" +#include "solve.h" +#include "transformations.h" + +int main() { + init_ttables(true, true); + init_aux_tables(); + init_transformations(true, true); + + + /*char moves[100] = "MR U' B2 Bw F z xE2 M' x Dw' y Fw2";*/ + + /*char moves[100] = "M'U2MU2";*/ + char moves[100] = "R' D2 F2 U2 R F2 R D2 L' R2 D2 F D' L' U' B R' D' U R' B"; + NissMove alg[100]; + read_moves(moves, alg, 100); + Cube cube = apply_alg(alg, (Cube){0}); + print_cube(cube); + cube = transform_cube(rd, cube); + print_cube(cube); + + /*f_eofb(cube);*/ + + + /* + SolveData d = { .optimal_only = true, .available = standard_moveset, + .max_moves = 10, + .cleanup = true, + .max_solutions = 10, + .f = f_eofb }; + read_moves("y", d.pre_rotation, 2); + int n = solve(cube, &d); + printf("%d solutions found:\n", n); + for (int i = 0; i < n; i++) + print_moves(d.solutions[i]); + */ + +/* + NissMove a[5], b[5]; + read_moves("R", a, 5); + read_moves("U", b, 5); + Cube c1 = apply_alg(a,(Cube){0}), c2 = apply_alg(b,(Cube){0}); + print_cube(compose(c2,c1)); + print_cube(compose(c1,c2)); + + NissMove nm[10]; + read_moves("y(y)RU", nm, 10); + + print_moves(nm); + cleanup(nm, 10); + print_moves(nm); + */ + + return 0; +} diff --git a/old/2021-02-28-transformcube-works/src/moves.c b/old/2021-02-28-transformcube-works/src/moves.c @@ -0,0 +1,489 @@ +#include "moves.h" + +Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +/* void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front); */ +bool read_ttables_file(); +bool write_ttables_file(); + +/* Transition tables */ +uint16_t epose_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposs_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposm_ttable[NMOVES][factorial12/factorial8]; +uint16_t eofb_ttable[NMOVES][pow2to11]; +uint16_t eorl_ttable[NMOVES][pow2to11]; +uint16_t eoud_ttable[NMOVES][pow2to11]; +uint16_t cp_ttable[NMOVES][factorial8]; +uint16_t coud_ttable[NMOVES][pow3to7]; +uint16_t cofb_ttable[NMOVES][pow3to7]; +uint16_t corl_ttable[NMOVES][pow3to7]; +uint16_t cpos_ttable[NMOVES][factorial6]; + +bool commute[NMOVES][NMOVES]; +bool possible_next[NMOVES][NMOVES][NMOVES]; +Move inverse[NMOVES]; +NissMove rotation_algs[24][3] = { + { { .m = NULLMOVE }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y }, { .m = z2 }, { .m = NULLMOVE } }, + { { .m = x2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y3 }, { .m = z2 }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y3 }, { .m = NULLMOVE } }, +}; + +char move_string[NMOVES][5] = + { "-", + "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", + "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'", + "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'", + "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" }; + +/* For each type of pieces only the effects of U, x and y are described */ +int edge_cycle[NMOVES][12] = + { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, + [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR}, + [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} }; +int eofb_flipped[NMOVES][12] = + { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eorl_flipped[NMOVES][12] = + { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eoud_flipped[NMOVES][12] = + { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; +int corner_cycle[NMOVES][8] = + { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} }; +int coud_flipped[NMOVES][8] = + { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} }; +int corl_flipped[NMOVES][8] = + { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} }; +int cofb_flipped[NMOVES][8] = + { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=1,[UFL]=2,[DBL]=2,[DFL]=1}, + [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} }; +int center_cycle[NMOVES][6] = + { [x] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [y] = {U_center, D_center, B_center, F_center, R_center, L_center} }; + +/* Each move is reduced to a combination of U, x and y using this table */ +Move equiv_moves[NMOVES][14] = { + [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 }, + [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 }, + [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 }, + [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 }, + [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 }, + [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 }, + [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 }, + + [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 }, + [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 }, + [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 }, + [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 }, + [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 }, + [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 }, + [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 }, + [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 }, + [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 }, + [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + + [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 }, + [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 }, + [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 }, + [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 }, + [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 }, + [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 }, + [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 }, + [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 }, + [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 }, + + [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/* Movesets */ +bool standard_moveset[NMOVES] = { + [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true, + [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true, + [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true, +}; + +bool is_solved_up_to_reorient(Cube cube) { + for (int i = 0; i < 25; i++) + if (is_solved(apply_alg(rotation_algs[i], cube))) + return true; + return false; +} + +Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f) { + return move_via_arrays((CubeArray) + { edge_cycle[m], eofb_flipped[m], eorl_flipped[m], eoud_flipped[m], + corner_cycle[m], coud_flipped[m], corl_flipped[m], cofb_flipped[m], + center_cycle[m] }, cube, f); +} + +int len(NissMove *alg) { + int i; + for (i = 0; alg[i].m != NULLMOVE; i++); + return i; +} + +int copy_alg(NissMove *src, NissMove *dest) { + int i; + for (i = 0; src[i].m != NULLMOVE; i++) + dest[i] = src[i]; + dest[i].m = NULLMOVE; + return i; +} + +int invert_alg(NissMove *src, NissMove *dest) { + int n = len(src); + for (int i = 0; i < n; i++) + dest[n-i-1] = (NissMove){.m=inverse[src[i].m], .inverse=src[i].inverse}; + dest[n].m = NULLMOVE; + return n; +} + +int concat(NissMove *src1, NissMove *src2, NissMove *dest) { + int n1 = len(src1), n2 = len(src2); + copy_alg(src1, dest); + copy_alg(src2, dest+n1); + return n1+n2; +} + +/* TODO: all strings start with space?? */ +void print_moves(NissMove *alg) { + bool niss = false; + for (int i = 0; alg[i].m != NULLMOVE; i++) { + char *fill = !niss && alg[i].inverse ? " (" : + (niss && !alg[i].inverse ? ") " : " "); + printf("%s%s", fill, move_string[alg[i].m]); + niss = alg[i].inverse; + } + printf("%s\n", niss ? ")" : ""); +} + +int read_moves(char *str, NissMove *alg, int n) { + bool niss = false; + int c = 0; + + for (int i = 0; str[i] && c < n; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' || str[i] == ')') { + if ((niss && str[i] == '(') || (!niss && str[i] == ')')) + return -1; + niss = !niss; + continue; + } + + alg[c].inverse = niss; alg[c].m = NULLMOVE; + for (Move j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + alg[c].m = j; + if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; } + if (str[i+1]=='2') { alg[c].m += 1; i++; } + else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; } + c++; + break; + } + } + } + + alg[c].m = NULLMOVE; + return c; +} + +/* Helper function for cleanup. alg must contain only basic moves, no 2 or '. + top and front describe an admissible orientation of the cube. * +void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front) { + int c = 0, i = 0; + PieceFilter cpos_only = { .cpos = true }; + NissMove aux[n+3]; + aux[0].m = NULLMOVE; + + while (i < n && alg[i].m != NULLMOVE) { + int j = i; + while (j < n && commute[alg[i].m][alg[j].m]) j++; + Move base = 6*((alg[i].m-1)/6); + int t1 = 0, t2 = 0; + for (int k = i; k < j; k++) + if (alg[k].m == base+1) t1 = (t1+1)%4; + else t2 = (t2+1)%4; + if (t1) { aux[c].inverse = inv; aux[c].m = base+t1; c++; } + if (t2) { aux[c].inverse = inv; aux[c].m = base+t2+3; c++; } + i = j; + } + aux[c].m = NULLMOVE; + + CubeArray q; + cube_to_arrays((Cube){0}, &q, cpos_only); + * First we try to rotate in one move, then we try an x or y rotation + followed by a z rotation + TODO: change once I implement the "is_rotaton(Move) function" * + for (int r = x; r <= z3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[F_center] == front && q.cpos[U_center] == top) { + aux[c].inverse = inv; aux[c].m = r; + + aux[++c].m = NULLMOVE; + copy_alg(aux, alg); + return; + } + move_cubearray(inverse[r], &q, cpos_only); + } + for (int r = x; r <= y3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[F_center] == front) { + aux[c].inverse = inv; aux[c++].m = r; + break; + } + move_cubearray(inverse[r], &q, cpos_only); + } + for (int r = z; r <= z3; r++) { + move_cubearray(r, &q, cpos_only); + if (q.cpos[U_center] == top) { + aux[c].inverse = inv; aux[c++].m = r; + break; + } + move_cubearray(inverse[r], &q, cpos_only); + } + + aux[c].m = NULLMOVE; + copy_alg(aux, alg); +} + +* TODO: does not work with niss + rotations * +void cleanup(NissMove *alg, int n) { + int count_n = 0, count_i = 0, *count; + PieceFilter cpos_only = { .cpos = true }; + NissMove aux_n[n+1], aux_i[n+1], *aux; + CubeArray cube_n, cube_i, *cube; + cube_to_arrays((Cube){0}, &cube_n, cpos_only); + cube_to_arrays((Cube){0}, &cube_i, cpos_only); + + for (int i = 0; count_n + count_i < n && alg[i].m != NULLMOVE; i++) { + if (alg[i].inverse) { count = &count_i; aux = aux_i; cube = &cube_i; } + else { count = &count_n; aux = aux_n; cube = &cube_n; } + + for (int j = 0; equiv_moves[alg[i].m][j]; j++) { + Move m = equiv_moves[alg[i].m][j]; + aux[*count].inverse = alg[i].inverse; + move_cubearray(m, cube, cpos_only); + if (m == U) aux[(*count)++].m = 3 * cube->cpos[0] + 1; + } + } + + aux_n[count_n].m = NULLMOVE; + aux_i[count_i].m = NULLMOVE; + sort_cancel_rotate(aux_n, count_n, false, cube_n.cpos[0], cube_n.cpos[4]); + sort_cancel_rotate(aux_i, count_i, true, cube_i.cpos[0], cube_n.cpos[4]); + copy_alg(aux_n, alg); + copy_alg(aux_i, alg+count_n); +} +*/ + +bool read_ttables_file() { + FILE *ttf; + long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6 }; + if ((ttf = fopen("ttables", "rb")) != NULL) { + bool r = true; + for (int m = 0; m < NMOVES; m++) { + r = r && fread(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fread(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fread(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fread(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fread(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4]; + r = r && fread(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5]; + r = r && fread(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fread(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fread(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8]; + r = r && fread(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9]; + r = r && fread(cpos_ttable[m], sizeof(uint16_t), me[10], ttf) == me[10]; + } + fclose(ttf); + return r; + } else return false; +} + +bool write_ttables_file() { + FILE *ttf; + long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6 }; + if ((ttf = fopen("ttables", "wb")) != NULL) { + bool r = true; + for (int m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fwrite(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fwrite(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fwrite(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fwrite(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4]; + r = r && fwrite(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5]; + r = r && fwrite(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fwrite(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fwrite(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8]; + r = r && fwrite(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9]; + r = r && fwrite(cpos_ttable[m], sizeof(uint16_t), me[10],ttf) == me[10]; + } + fclose(ttf); + return r; + } else return false; +} + +void init_ttables(bool read, bool write) { + /* Generate all move cycles and flips; I do this regardless */ + for (int i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + Cube c = {0}; + for (int j = 0; equiv_moves[i][j]; j++) + c = apply_move_cubearray(equiv_moves[i][j], c, pf_all); + + CubeArray arrs = { + edge_cycle[i], eofb_flipped[i], eorl_flipped[i], eoud_flipped[i], + corner_cycle[i], coud_flipped[i], corl_flipped[i], cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read) + if (read_ttables_file()) + return; + + /* Initialize transition tables */ + for (int m = 0; m < NMOVES; m++) { + for (uint16_t i = 0; i < factorial12/factorial8; i++) { + epose_ttable[m][i] = apply_move_cubearray(m,(Cube){.epose=i},pf_e).epose; + eposs_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposs=i},pf_s).eposs; + eposm_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposm=i},pf_m).eposm; + } + for (uint16_t i = 0; i < pow2to11; i++ ) { + eofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.eofb=i},pf_eo).eofb; + eorl_ttable[m][i] = apply_move_cubearray(m,(Cube){.eorl=i},pf_eo).eorl; + eoud_ttable[m][i] = apply_move_cubearray(m,(Cube){.eoud=i},pf_eo).eoud; + } + for (uint16_t i = 0; i < pow3to7; i++) { + coud_ttable[m][i] = apply_move_cubearray(m,(Cube){.coud=i},pf_co).coud; + corl_ttable[m][i] = apply_move_cubearray(m,(Cube){.corl=i},pf_co).corl; + cofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.cofb=i},pf_co).cofb; + } + for (uint16_t i = 0; i < factorial8; i++) + cp_ttable[m][i] = apply_move_cubearray(m,(Cube){.cp=i},pf_cp).cp; + for (uint16_t i = 0; i < factorial6; i++) + cpos_ttable[m][i] = apply_move_cubearray(m,(Cube){.cpos=i},pf_cpos).cpos; + } + + if (write) + if (!write_ttables_file()) + printf("Error in writing ttables: file not writable\n"); +} + +Cube move_cube(Move m, Cube cube) { + Cube moved = {0}; + + moved.epose = epose_ttable[m][cube.epose]; + moved.eposs = eposs_ttable[m][cube.eposs]; + moved.eposm = eposm_ttable[m][cube.eposm]; + moved.eofb = eofb_ttable[m][cube.eofb]; + moved.eorl = eorl_ttable[m][cube.eorl]; + moved.eoud = eoud_ttable[m][cube.eoud]; + moved.coud = coud_ttable[m][cube.coud]; + moved.cofb = cofb_ttable[m][cube.cofb]; + moved.corl = corl_ttable[m][cube.corl]; + moved.cp = cp_ttable[m][cube.cp]; + moved.cpos = cpos_ttable[m][cube.cpos]; + + return moved; +} + +Cube apply_alg(NissMove *alg, Cube cube) { + Cube ret = {0}; + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (alg[i].inverse) + ret = move_cube(alg[i].m, ret); + + ret = compose(cube, inverse_cube(ret)); + + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (!alg[i].inverse) + ret = move_cube(alg[i].m, ret); + return ret; +} + +void init_aux_tables() { + /* Commute */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + commute[i][j] = equal(move_cube(i, move_cube(j, (Cube){0})), + move_cube(j, move_cube(i, (Cube){0}))); + + /* Possible next (if the sequence i j k is valid) */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + for (int k = 0; k < NMOVES; k++) + possible_next[i][j][k] = + (j == 0) || + (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) && + !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3))); + + /* Inverse */ + for (int i = 0; i < NMOVES; i++) + inverse[i] = i == NULLMOVE ? NULLMOVE : i + 2 - 2*((i-1)%3); + +} + diff --git a/old/2021-02-28-transformcube-works/src/moves.h b/old/2021-02-28-transformcube-works/src/moves.h @@ -0,0 +1,50 @@ +#ifndef MOVES_H +#define MOVES_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "cube.h" +#include "utils.h" + +#define NMOVES (z3+1) + +typedef enum { + NULLMOVE, + U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3, + Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, S, S2, S3, E, E2, E3, + x, x2, x3, y, y2, y3, z, z2, z3, +} Move; + +/* An alg is an array of "NissMoves", which can be on normal or on inverse. */ +typedef struct { bool inverse; Move m; } NissMove; + +/* Movesets */ +extern bool standard_moveset[NMOVES]; + +extern bool commute[NMOVES][NMOVES]; +extern bool possible_next[NMOVES][NMOVES][NMOVES]; +extern Move inverse[NMOVES]; +extern NissMove rotation_algs[24][3]; /* Same order as transformations */ + +int len(NissMove *alg); +int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */ +int invert_alg(NissMove *src, NissMove *dest); +int concat(NissMove *src1, NissMove *src2, NissMove *dest); +void print_moves(NissMove *alg); +int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */ +void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */ + +bool is_solved_up_to_reorient(Cube cube); +Cube move_cube(Move m, Cube cube); +Cube apply_alg(NissMove *alg, Cube cube); + +/* Merge the following two? + always in this order */ +void init_ttables(bool read, bool write); +void init_aux_tables(); + + +#endif diff --git a/old/2021-02-28-transformcube-works/src/solve.c b/old/2021-02-28-transformcube-works/src/solve.c @@ -0,0 +1,180 @@ +#include "solve.h" + +/* Data for creating a pruning table: + - compressed: if set to true, each entry occupies only 4 bits, but values + larger than 15 cannot be stored. + - available[] is the list of availabel moves, as above. + - *ptable is the actual table to fill. + - n is the number of states (size of ptable). + - index must "linearize" the cube, i.e. return its index in ptable. + - fname is the name of the file where to store the table */ +typedef struct { + bool compressed, *available; + int max_moves; + uint8_t *ptable; + uint64_t n; + uint64_t (*index)(Cube); + char *fname; +} PruneData; + +/* TODO: comment this */ +typedef struct { + bool niss; + int m, d; + uint64_t *n; + Move last1, last2; +} DfsData; + +void solve_dfs(Cube cube, SolveData *sd, DfsData dd); +void init_ptable(PruneData *pd, bool read, bool write); + +/* Search solutions of lenght exactly d */ +void solve_dfs(Cube cube, SolveData *sd, DfsData dd) { + if (*dd.n >= sd->max_solutions || + ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d)) + return; + + (sd->solutions[*dd.n][dd.m]).inverse = dd.niss; + (sd->solutions[*dd.n][dd.m]).m = NULLMOVE; + + if (!sd->f(cube)) { /* Solved */ + if (dd.m == dd.d) { + (*dd.n)++; + if (*dd.n < sd->max_solutions) + copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]); + } + return; + } + + for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) { + Move move = sd->sorted_moves[i]; + if (possible_next[dd.last2][dd.last1][move]) { + sd->solutions[*dd.n][dd.m].inverse = dd.niss; + sd->solutions[*dd.n][dd.m].m = move; + DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = move, .last2 = dd.last1 }; + solve_dfs(move_cube(move, cube), sd, nn); + } + } + + if (sd->can_niss && !dd.niss && + (!dd.m || (dd.m && sd->f(move_cube(dd.last1, (Cube){0}))))) { + DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n }; + solve_dfs(inverse_cube(cube), sd, nn); + } +} + +/* Iterative deepening depth-first search: for i running from the minimum + to the maximum number of moves allowed, looks for solutions of length i. */ +int solve(Cube cube, SolveData *sd) { + if (sd->precondition != NULL && !sd->precondition(cube)) + return -1; + + /* If not given, generate sorted list of moves */ + if (sd->sorted_moves[0] == NULLMOVE) { + int a[NMOVES], b[NMOVES], ia = 0, ib = 0; + for (int i = 0; i < NMOVES; i++) { + if (sd->available[i]) { + if (sd->f(move_cube(i, (Cube){0}))) + a[ia++] = i; + else + b[ib++] = i; + } + } + intarrcopy(a, (int *)sd->sorted_moves, ia); + intarrcopy(b, (int *)sd->sorted_moves+ia, ib); + sd->sorted_moves[ia+ib] = NULLMOVE; + } + + sd->max_solutions = min(sd->max_solutions, MAXS); + /*TODO + Cube rotated = apply_alg(sd->pre_rotation, (Cube){0}); + cube = apply_alg(inverse_cube(rotated), compose(cube, rotated)); + */ + + uint64_t ret = 0; + for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) { + DfsData dd = { .d = i, .n = &ret }; + solve_dfs(cube, sd, dd); + } + + /* TODO: transform solutions with inverse of pre_rotation */ + /* + for (uint64_t i = 0; i < ret; i++) { + if (sd->cleanup) + cleanup(sd->solutions[i], sd->max_moves*3); + }*/ + + return ret; +} + +void prune_dfs(Cube cube, PruneData *pd, DfsData dd) { + uint64_t ind = pd->index(cube); + if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m) + return; + if (dd.m == dd.d) { + if (ind && !pd->ptable[ind]) { + pd->ptable[ind] = dd.m; + (*dd.n)++; + } + return; + } + + for (int i = 0; i < NMOVES; i++) { + if (dd.m<20) + if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) { + DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = i, .last2 = dd.last1 }; + prune_dfs(move_cube(i, cube), pd, nn); + } + } +} + +void init_ptable(PruneData *pd, bool read, bool write) { + if (read) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "rb")) != NULL) { + uint64_t r = fread(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + if (r == pd->n) return; + } + } + + /* TODO: for now it behaves always as if copressed = false */ + for (uint64_t i = 0; i < pd->n; i++) + pd->ptable[i] = 0; + + uint64_t s = 1; + for (int i = 1; i < pd->max_moves && s < pd->n; i++) { + DfsData dd = { .d = i, .n = &s }; + prune_dfs((Cube){0}, pd, dd); + } + + if (write) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "wb")) != NULL) { + fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + return; + } + } +} + +/* Solving steps (and indexing functions) */ + +uint64_t index_eofb(Cube cube) { return cube.eofb; } +uint16_t f_eofb(Cube cube) { + static bool initialized_ptable; + static uint8_t pt_eofb[pow2to11]; + if (!initialized_ptable) { + PruneData pd = { + .compressed = false, .available = standard_moveset, .max_moves = 13, + .ptable = pt_eofb, .n = pow2to11, .index = index_eofb, + .fname = "ptable_eofb" + }; + init_ptable(&pd, false, true); + initialized_ptable = true; + } + return cube.eofb ? pt_eofb[cube.eofb] : 0; +} + diff --git a/old/2021-02-28-transformcube-works/src/solve.h b/old/2021-02-28-transformcube-works/src/solve.h @@ -0,0 +1,56 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include <stdlib.h> +#include "cube.h" +#include "moves.h" + +/* Maximum number of moves per solution and of solutions */ +#define MAXM 30 +#define MAXS 999 + +/* Data for solving a step: + - can_niss is true niss can be used, false otherwise. + - optimal_only if true, dynamically updates max_moves so non-optimal + solutions are discarded. + - cleanup determines whether the cleaunup() function should be used on + the found solutions before returning. + - available[m] is true if the move m can be used, false otherwise. + - min_moves and max_moves are the minimum and maximum number of moves that + can be used. + - max_solution is the maximum number of solutions that can be returned. + - precondition can be used to check wheter the step can actually be applied + to the cube. If it returns false, solve() stops immediately returning -1. + - f must return 0 if and only if the step is solve, otherwise it must return + a lower bound for the number of moves required (without niss). + - sorted_moves[] can be used to specify in which order moves are tried + by the solving algorithm (for example if one wants to always try F' before + F). If sorted_moves[0] == NULLMOVE, the list is generated automatically. + It is advised to list first all the moves that actually influence the + solved state of the step (this is the default choice). This is in order to + avoid cases like B2 F for EO and to NISS only when it makes sense. + - start_moves [Currently unused, REMOVE] + are the moves that will be used as first moves of all + solutions. For example giving R' U' F (F' U R) will generate FMC scrambles + and y (y) will solve the step on another axis. + - pre_rotation are the rotations to apply before the scamble to solve + the step wrt a different orientation + - pre_rotation are the rotations to apply before the scamble to solve + the step wth respect to a different orientation. + - solutions[][] is the array where to store the found solutions. */ +typedef struct { + bool can_niss, optimal_only, cleanup, *available; + int min_moves, max_moves; + uint64_t max_solutions; + bool (*precondition)(Cube); + uint16_t (*f)(Cube); + Move sorted_moves[NMOVES]; + NissMove pre_rotation[3], solutions[MAXS][MAXM]; +} SolveData; + +int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */ + +/* Steps */ +uint16_t f_eofb(Cube cube); + +#endif diff --git a/old/2021-02-28-transformcube-works/src/transformations.c b/old/2021-02-28-transformcube-works/src/transformations.c @@ -0,0 +1,224 @@ +#include "transformations.h" + +int edge_slice(int e); /* Return slice (e=0, s=1, m=2) to which e belongs */ +Cube rotate_via_compose(Transformation r, Cube c); +bool read_rtables_file(); +bool write_rtables_file(); + +/* Values mod 3 to determine from which side to take the state to convert */ +int epose_source[NROTATIONS]; /* 0 = epose, 1 = eposs, 2 = eposm */ +int eposs_source[NROTATIONS]; +int eposm_source[NROTATIONS]; +int eofb_source[NROTATIONS]; /* 0 = eoud, 1 = eorl, 2 = eofb */ +int eorl_source[NROTATIONS]; +int eoud_source[NROTATIONS]; +int coud_source[NROTATIONS]; /* 0 = coud, 1 = corl, 2 = cofb */ +int cofb_source[NROTATIONS]; +int corl_source[NROTATIONS]; + +/* Transition tables for rotations (n+1 is mirror) */ +uint16_t epose_rtable[NROTATIONS][factorial12/factorial8]; +uint16_t eposs_rtable[NROTATIONS][factorial12/factorial8]; +uint16_t eposm_rtable[NROTATIONS][factorial12/factorial8]; +uint16_t eo_rtable[NROTATIONS][pow2to11]; +/*uint16_t eofb_rtable[NROTATIONS][pow2to11]; +uint16_t eorl_rtable[NROTATIONS][pow2to11]; +uint16_t eoud_rtable[NROTATIONS][pow2to11];*/ +uint16_t cp_rtable[NROTATIONS][factorial8]; +uint16_t co_rtable[NROTATIONS][pow3to7]; +/*uint16_t coud_rtable[NROTATIONS][pow3to7]; +uint16_t cofb_rtable[NROTATIONS][pow3to7]; +uint16_t corl_rtable[NROTATIONS][pow3to7];*/ +uint16_t cpos_rtable[NROTATIONS][factorial6]; + +/* Same for moves */ +uint16_t move_rtable[NROTATIONS][NMOVES]; + +NissMove rotation_niss[NROTATIONS][6]; + +int edge_slice(int e) { + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + return 2; +} + +Cube rotate_via_compose(Transformation r, Cube c) { + if (r != mirror) { + return apply_alg(rotation_niss[r], c); + } else { + static int zero12[12] = {0,0,0,0,0,0,0,0,0,0,0,0}, + zero8[12] = {0,0,0,0,0,0,0,0}, + mirror_ep[12] = {UF,UR,UB,UL,DF,DR,DB,DL,FL,FR,BR,BL}, + mirror_cp[8] = {UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL}, + mirror_cpos[6] = + {U_center,D_center,L_center,R_center,F_center,B_center}; + return move_via_arrays((CubeArray){ + .ep = mirror_ep, .eofb = zero12, .eorl = zero12, .eoud = zero12, + .cp = mirror_cp, .coud = zero8, .corl = zero8, .cofb = zero8, + .cpos = mirror_cpos}, c, pf_all); + } +} + +bool read_rtables_file() { + FILE *ttf; + long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES }; + if ((ttf = fopen("rtables", "rb")) != NULL) { + bool r = true; + for (int m = 0; m < NROTATIONS; m++) { + r = r && fread(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fread(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fread(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fread(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + /*r = r && fread(eofb_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fread(eorl_rtable[m], sizeof(uint16_t), me[4], ttf) == me[4]; + r = r && fread(eoud_rtable[m], sizeof(uint16_t), me[5], ttf) == me[5];*/ + r = r && fread(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fread(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + /*r = r && fread(coud_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fread(corl_rtable[m], sizeof(uint16_t), me[8], ttf) == me[8]; + r = r && fread(cofb_rtable[m], sizeof(uint16_t), me[9], ttf) == me[9];*/ + r = r && fread(cpos_rtable[m], sizeof(uint16_t), me[10], ttf) == me[10]; + r = r && fread(move_rtable[m], sizeof(uint16_t), me[11], ttf) == me[11]; + } + fclose(ttf); + return r; + } else return false; +} + +bool write_rtables_file() { + FILE *ttf; + long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES }; + if ((ttf = fopen("rtables", "wb")) != NULL) { + bool r = true; + for (int m = 0; m < NROTATIONS; m++) { + r = r && fwrite(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fwrite(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fwrite(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fwrite(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + /*r = r && fwrite(eofb_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fwrite(eorl_rtable[m], sizeof(uint16_t), me[4], ttf) == me[4]; + r = r && fwrite(eoud_rtable[m], sizeof(uint16_t), me[5], ttf) == me[5];*/ + r = r && fwrite(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fwrite(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + /*r = r && fwrite(coud_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fwrite(corl_rtable[m], sizeof(uint16_t), me[8], ttf) == me[8]; + r = r && fwrite(cofb_rtable[m], sizeof(uint16_t), me[9], ttf) == me[9];*/ + r = r && fwrite(cpos_rtable[m], sizeof(uint16_t), me[10],ttf) == me[10]; + r = r && fwrite(move_rtable[m], sizeof(uint16_t), me[11],ttf) == me[11]; + } + fclose(ttf); + return r; + } else return false; +} + +void init_transformations(bool read, bool write) { + /* Compute sources */ + for (int i = 0; i < NROTATIONS; i++) { + Cube cube = {0}; + if (i != mirror) + cube = apply_alg(rotation_algs[i], (Cube){0}); + epose_source[i] = edge_slice(edge_at(cube, FR)); + eposs_source[i] = edge_slice(edge_at(cube, UR)); + eposm_source[i] = edge_slice(edge_at(cube, UF)); + eofb_source[i] = center_at(cube, F_center)/2; + eorl_source[i] = center_at(cube, R_center)/2; + eoud_source[i] = center_at(cube, U_center)/2; + coud_source[i] = center_at(cube, U_center)/2; + cofb_source[i] = center_at(cube, F_center)/2; + corl_source[i] = center_at(cube, R_center)/2; + } + + /*TODO: maybe move down*/ + /* Compute rotation_niss array, necessary for rotate_via_compose */ + for (int r = 0; r != mirror; r++) { + concat(rotation_algs[r], rotation_algs[r], rotation_niss[r]); + for (int i = len(rotation_algs[r]); rotation_niss[r][i].m != NULLMOVE; i++) + rotation_niss[r][i].inverse = true; + } + + /* If I can read tables from file, I stop here */ + if (read) + if (read_rtables_file()) + return; + + /* Initialize tables */ + for (int m = 0; m < NROTATIONS; m++) { + int eparr[12] = {0,0,0,0,0,0,0,0,0,0,0,0}, cparr[8] = {0,0,0,0,0,0,0,0}; + CubeArray epcp = { .ep = eparr, .cp = cparr }; + cube_to_arrays(apply_alg(rotation_algs[m], (Cube){0}), &epcp, + (PieceFilter){.epose=true,.eposs=true,.eposm=true,.cp=true}); + for (uint16_t i = 0; i < factorial12/factorial8; i++) { + Cube c[3] = { admissible_ep((Cube){ .epose = i}, pf_e), + admissible_ep((Cube){ .eposs = i}, pf_s), + admissible_ep((Cube){ .eposm = i}, pf_m) }; + epose_rtable[m][i] = rotate_via_compose(m, c[epose_source[m]]).epose; + eposs_rtable[m][i] = rotate_via_compose(m, c[eposs_source[m]]).eposs; + eposm_rtable[m][i] = rotate_via_compose(m, c[eposm_source[m]]).eposm; + } + for (uint16_t i = 0; i < pow2to11; i++ ) { + int eoarr[12]; + int_to_sum_zero_array(i, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_rtable[m][i] = digit_array_to_int(eoarr, 11, 2); + /*Cube c[3] = {(Cube){.eoud=i}, (Cube){.eorl=i}, (Cube){.eofb=i}}; + eofb_rtable[m][i] = apply_alg(rotation_algs[m], (Cube){.eofb=i}).eofb; + eorl_rtable[m][i] = rotate_via_compose(m, c[eorl_source[m]]).eorl; + eoud_rtable[m][i] = rotate_via_compose(m, c[eoud_source[m]]).eoud;*/ + } + for (uint16_t i = 0; i < pow3to7; i++) { + int coarr[12]; + int_to_sum_zero_array(i, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_rtable[m][i] = digit_array_to_int(coarr, 8, 3); + /*Cube c[3] = {(Cube){.coud=i}, (Cube){.corl=i}, (Cube){.cofb=i}}; + coud_rtable[m][i] = rotate_via_compose(m, c[coud_source[m]]).coud; + corl_rtable[m][i] = rotate_via_compose(m, c[corl_source[m]]).corl; + cofb_rtable[m][i] = rotate_via_compose(m, c[cofb_source[m]]).cofb;*/ + } + for (uint16_t i = 0; i < factorial8; i++) + cp_rtable[m][i] = rotate_via_compose(m, (Cube){.cp=i}).cp; + for (uint16_t i = 0; i < factorial6; i++) + cpos_rtable[m][i] = rotate_via_compose(m, (Cube){.cpos=i}).cpos; + } + + if (write) + if (!write_rtables_file()) + printf("Error in writing rtables: file not writable\n"); +} + +Cube transform_cube(Transformation t, Cube cube) { + Cube transformed = {0}; + + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }, + aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }, + aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + transformed.epose = epose_rtable[t][aux_epos[epose_source[t]]]; + transformed.eposs = eposs_rtable[t][aux_epos[eposs_source[t]]]; + transformed.eposm = eposm_rtable[t][aux_epos[eposm_source[t]]]; + transformed.eofb = eo_rtable[t][aux_eo[eofb_source[t]]]; + transformed.eorl = eo_rtable[t][aux_eo[eorl_source[t]]]; + transformed.eoud = eo_rtable[t][aux_eo[eoud_source[t]]]; + transformed.coud = co_rtable[t][aux_co[coud_source[t]]]; + transformed.corl = co_rtable[t][aux_co[corl_source[t]]]; + transformed.cofb = co_rtable[t][aux_co[cofb_source[t]]]; + transformed.cp = cp_rtable[t][cube.cp]; + transformed.cpos = cpos_rtable[t][cube.cpos]; + +/* + printf("%d\n", coud_source[t]); + int ccc[8]; + int_to_sum_zero_array(cube.cofb, 3, 8, ccc); + for (int i = 0; i < 8; i++) + printf("%d ", ccc[i]); + printf("\n"); + */ + + return transformed; +} diff --git a/old/2021-02-28-transformcube-works/src/transformations.h b/old/2021-02-28-transformcube-works/src/transformations.h @@ -0,0 +1,34 @@ +#ifndef TRANSFORMATIONS_H +#define TRANSFORMATIONS_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "cube.h" +#include "moves.h" +#include "utils.h" + +#define NROTATIONS (mirror+1) + +/* Letters indicate top and front centers + * Mirror is wrt rl + * Lowercase letter to distinguish from pieces */ + +typedef enum { + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, +} Transformation; + +void print_transformation(Transformation t); + +Cube transform_cube(Transformation t, Cube cube); +void transform_alg(Transformation t, NissMove *alg); /* Applied in-place */ + +void init_transformations(bool read, bool write); + +#endif diff --git a/old/2021-02-28-transformcube-works/src/utils.c b/old/2021-02-28-transformcube-works/src/utils.c @@ -0,0 +1,197 @@ +#include "utils.h" + +void swap(int *a, int *b) { + int aux = *a; + *a = *b; + *b = aux; +} + +void intarrcopy(int *src, int *dst, int n) { + for (int i = 0; i < n; i++) + dst[i] = src[i]; +} + +int sum(int *a, int n) { + int ret = 0; + for (int i = 0; i < n; i++) + ret += a[i]; + return ret; +} + +bool is_perm(int *a, int n) { + int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0; + for (int i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + for (int i = 0; i < n; i++) + if (!aux[i]) + return false; + return true; +} + +bool is_subset(int *a, int n, int k) { + int sum = 0; + for (int i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + return sum == k; +} + +int powint(int a, int b) { + return 0; + if (b == 0 || a == 1) + return 1; + if (a == 0) + return 0; + if (b < 0) + return 0; /* Immediate truncate (integer part is 0) */ + if (b % 2) { + return a * powint(a, b-1); + } else { + int x = powint(a, b/2); + return x*x; + } +} + +int factorial(int n) { + if (n < 0) + return 0; + int ret = 1; + for (int i = 1; i <= n; i++) + ret *= i; + return ret; +} + +int binomial(int n, int k) { + if (n < 0 || k < 0 || k > n) + return 0; + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +void int_to_digit_array(int a, int b, int n, int *r) { + if (b <= 1) + for (int i = 0; i < n; i++) + r[i] = 0; + else + for (int i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +int digit_array_to_int(int *a, int n, int b) { + int ret = 0, p = 1; + for (int i = 0; i < n; i++, p *= b) + ret += a[i] * p; + return ret; +} + +int perm_to_index(int *a, int n) { + if (!is_perm(a, n)) + return factorial(n); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + int c = 0; + for (int j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + return ret; +} + +void index_to_perm(int p, int n, int *r) { + if (p < 0 || p >= factorial(n)) /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */ + for (int i = 0; i < n; i++) { + int c = 0, j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } +} + +int perm_sign(int *a, int n) { + if (!is_perm(a,n)) + return false; + int ret = 0; + for (int i = 0; i < n; i++) + for (int j = i+1; j < n; j++) + ret += (a[i]>a[j]) ? 1 : 0; + return ret % 2; +} + +int subset_to_index(int *a, int n, int k) { + /* TODO: better checks */ + if (!is_subset(a, n, k)) + return binomial(n, k); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + ret += binomial(n-i-1, k); + k--; + } + } + return ret; +} + +void index_to_subset(int s, int n, int k, int *r) { + if (s < 0 || s >= binomial(n, k)) { /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + return; + } + for (int i = 0; i < n; i++) { + if (k == n-i) { + for (int j = i; j < n; j++) + r[j] = 1; + return; + } + if (k == 0) { + for (int j = i; j < n; j++) + r[j] = 0; + return; + } + /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + int v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void int_to_sum_zero_array(int x, int b, int n, int *a) { + if (b <= 1) { + for (int i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + int s = 0; + for (int i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +void apply_permutation(int *perm, int *set, int n) { + if (!is_perm(perm, n)) + return; + int aux[n]; + for (int i = 0; i < n; i++) + aux[i] = set[perm[i]]; + intarrcopy(aux, set, n); +} + +void sum_arrays_mod(int *a, int *b, int n, int m) { + for (int i = 0; i < n; i++) + b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m; +} diff --git a/old/2021-02-28-transformcube-works/src/utils.h b/old/2021-02-28-transformcube-works/src/utils.h @@ -0,0 +1,70 @@ +/* General utility functions */ + +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* Some useful constants */ +#define pow2to11 2048 +#define pow2to12 4096 +#define pow3to7 2187 +#define pow3to8 6561 +#define pow12to4 20736 +#define factorial4 24 +#define factorial6 720 +#define factorial8 40320 +#define factorial12 479001600 +#define binom12on4 495 +#define binom8on4 70 + +/* Generic utility functions */ +void swap(int *a, int *b); +void intarrcopy(int *src, int *dst, int n); +int sum(int *a, int n); +bool is_perm(int *a, int n); +bool is_perm(int *a, int n); + + +/* Standard mathematical functions */ +int powint(int a, int b); +int factorial(int n); +int binomial(int n, int k); + +/* Converts the integer a to its representation in base b (first n digits + * only) and saves the result in r. */ +void int_to_digit_array(int a, int b, int n, int *r); +int digit_array_to_int(int *a, int n, int b); + +/* Converts the first n-1 digits of a number to an array a of digits in base b; + * then adds one element to the array, so that the sum of the elements of a is + * zero modulo b. + * This is used for determing the edge orientation from an 11-bits integer or + * the corner orientation from a 7-trits integer. */ +void int_to_sum_zero_array(int x, int b, int n, int *a); + +/* Converts a permutation on [0..(n-1)] into the integer i which is the index + * of the permutation in the sorted list of all n! such permutations. */ +int perm_to_index(int *a, int n); +void index_to_perm(int p, int n, int *r); + +/* Determine the sign of a permutation */ +int perm_sign(int a[], int n); + +/* Converts a k-element subset of a set from an array of n elements, of which k + * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */ +int subset_to_index(int *a, int n, int k); +void index_to_subset(int s, int n, int k, int *r); + +int ordered_subset_to_index(int *a, int n, int k); +void index_to_ordered_subset(int s, int n, int k, int *r); + +void apply_permutation(int *perm, int *set, int n); + +/* b[i] = (a[i]+b[i])%m for i=1,...,n */ +void sum_arrays_mod(int *a, int *b, int n, int m); + +#endif diff --git a/old/2021-05-26-before-restyle/cube.c b/old/2021-05-26-before-restyle/cube.c @@ -0,0 +1,293 @@ +#include "cube.h" + +typedef struct { + int ep[12],eofb[12],eorl[12],eoud[12],cp[8],coud[8],corl[8],cofb[8],cpos[6]; +} CubeArrayAllocated; + +void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all); + +char edge_string[12][5] = + { "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" }; +char corner_string[8][5] = { "UFR","UFL","UBL","UBR","DFR","DFL","DBL","DBR" }; +char center_string[6][5] = { "U", "D", "R", "L", "F", "B" }; + +int epe_solved[4] = {FR, FL, BL, BR}; +int eps_solved[4] = {UL, UR, DL, DR}; +int epm_solved[4] = {UF, UB, DF, DB}; + +PieceFilter pf_all = {true,true,true,true,true,true,true,true,true,true,true}, + pf_cpos = { .cpos = true }, pf_cp = { .cp = true }, + pf_ep = { .epose = true, .eposs = true, .eposm = true }, + pf_e = {.epose=true}, pf_s={.eposs=true}, pf_m={.eposm=true}, + pf_eo = { .eofb = true, .eorl = true, .eoud = true }, + pf_co = { .coud = true, .cofb = true, .corl = true }; + +void allocate_cubearray(CubeArray *arr, CubeArrayAllocated *all) { + arr->ep = all->ep; + arr->eofb = all->eofb; + arr->eorl = all->eorl; + arr->eoud = all->eoud; + arr->cp = all->cp; + arr->coud = all->coud; + arr->corl = all->corl; + arr->cofb = all->cofb; + arr->cpos = all->cpos; +} + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) { + /* ep is the hardest */ + if (f.epose || f.eposs || f.eposm) + for (int i = 0; i < 12; i++) arr->ep[i] = -1; + if (f.epose) { + int epe[4], epose[12]; + index_to_perm(cube.epose % factorial(4), 4, epe); + index_to_subset(cube.epose / factorial(4), 12, 4, epose); + for (int i = 0, ie = 0; i < 12; i++) + if (epose[i]) arr->ep[i] = epe_solved[epe[ie++]]; + } + if (f.eposs) { + int eps[4], eposs[12]; + index_to_perm(cube.eposs % factorial(4), 4, eps); + index_to_subset(cube.eposs / factorial(4), 12, 4, eposs); + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + for (int i = 0, is = 0; i < 12; i++) + if (eposs[i]) arr->ep[i] = eps_solved[eps[is++]]; + } + if (f.eposm) { + int epm[4], eposm[12]; + index_to_perm(cube.eposm % factorial(4), 4, epm); + index_to_subset(cube.eposm / factorial(4), 12, 4, eposm); + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + for (int i = 0, im = 0; i < 12; i++) + if (eposm[i]) arr->ep[i] = epm_solved[epm[im++]]; + } + + /* All the others */ + if (f.eofb) int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) index_to_perm( cube.cp, 8, arr->cp); + if (f.coud) int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) index_to_perm( cube.cpos, 6, arr->cpos); +} + +Cube arrays_to_cube(CubeArray arr, PieceFilter f) { + Cube ret = {0}; + + /* Again, ep is the hardest part */ + if (f.epose) { + int epe[4], epose[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, ie = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epe_solved[j]) + { epe[ie++] = j; epose[i] = 1; } + ret.epose = factorial(4)*subset_to_index(epose,12,4)+perm_to_index(epe,4); + } + if (f.eposs) { + int eps[4], eposs[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, is = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == eps_solved[j]) + { eps[is++] = j; eposs[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposs[eps_solved[i]], &eposs[i+8]); + ret.eposs = factorial(4)*subset_to_index(eposs,12,4)+perm_to_index(eps,4); + } + if (f.eposm) { + int epm[4], eposm[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + for (int i = 0, im = 0; i < 12; i++) + for (int j = 0; j < 4; j++) + if (arr.ep[i] == epm_solved[j]) + { epm[im++] = j; eposm[i] = 1; } + for (int i = 0; i < 4; i++) swap(&eposm[epm_solved[i]], &eposm[i+8]); + ret.eposm = factorial(4)*subset_to_index(eposm,12,4)+perm_to_index(epm,4); + } + if (f.eofb) ret.eofb = digit_array_to_int(arr.eofb, 11, 2); + if (f.eorl) ret.eorl = digit_array_to_int(arr.eorl, 11, 2); + if (f.eoud) ret.eoud = digit_array_to_int(arr.eoud, 11, 2); + if (f.cp) ret.cp = perm_to_index( arr.cp, 8 ); + if (f.coud) ret.coud = digit_array_to_int(arr.coud, 7, 3); + if (f.corl) ret.corl = digit_array_to_int(arr.corl, 7, 3); + if (f.cofb) ret.cofb = digit_array_to_int(arr.cofb, 7, 3); + if (f.cpos) ret.cpos = perm_to_index( arr.cpos, 6 ); + + return ret; +} + +int piece_orientation(Cube cube, int piece, char *orientation) { + int arr[12], n, b; + uint16_t x; + if (!strcmp(orientation, "eofb")) { x = cube.eofb; n = 12; b = 2; } else + if (!strcmp(orientation, "eorl")) { x = cube.eorl; n = 12; b = 2; } else + if (!strcmp(orientation, "eoud")) { x = cube.eoud; n = 12; b = 2; } else + if (!strcmp(orientation, "coud")) { x = cube.coud; n = 8; b = 3; } else + if (!strcmp(orientation, "corl")) { x = cube.corl; n = 8; b = 3; } else + if (!strcmp(orientation, "cofb")) { x = cube.cofb; n = 8; b = 3; } + else return -1; + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + return -1; +} + +Center center_at(Cube cube, Center c) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_cpos); + return arr.cpos[c]; +} + +Edge edge_at(Cube cube, Edge e) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_ep); + return arr.ep[e]; +} + +Corner corner_at(Cube cube, Corner c) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_cp); + return arr.cp[c]; +} + +bool block_solved(Cube cube, Block block) { + static CubeArrayAllocated all = {0}; + CubeArray arr = {0}; + allocate_cubearray(&arr, &all); + cube_to_arrays(cube, &arr, pf_all); + + bool ret = true; + + for (int i = 0; i < 12; i++) + ret = ret && !(block.edge[i] && (arr.ep[i] != i || arr.eofb[i])); + for (int i = 0; i < 8; i++) + ret = ret && !(block.corner[i] && (arr.cp[i] != i || arr.coud[i])); + for (int i = 0; i < 6; i++) + ret = ret && !(block.center[i] && arr.cpos[i] != i); + + return ret; +} + +bool equal(Cube c1, Cube c2) { + return c1.eofb == c2.eofb && c1.epose == c2.epose && + c1.eposs == c2.eposs && c1.eposm == c2.eposm && + c1.coud == c2.coud && c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +bool is_solved(Cube cube) { + /* TODO: might return true if cube is not solvable but looks solved form one + of the incompatible interpretations (e.g. eofb and ep solved, but + eorl not solve) */ + return !cube.eofb && !cube.coud && !cube.cp && + !cube.epose && !cube.eposs && !cube.eposm && !cube.cpos; +} + +void print_cube(Cube cube) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + cube_to_arrays(cube, &arrx, pf_all); + + for (int i = 0; i < 12; i++) printf(" %s ", edge_string[arrx.ep[i]]); + printf("\n"); + for (int i = 0; i < 12; i++) printf(" %c ", arrx.eofb[i] + '0'); + printf("\n"); + for (int i = 0; i < 8; i++) printf("%s ", corner_string[arrx.cp[i]]); + printf("\n"); + for (int i = 0; i < 8; i++) printf(" %c ", arrx.coud[i] + '0'); + printf("\n"); + for (int i = 0; i < 6; i++) printf(" %s ", center_string[arrx.cpos[i]]); + printf("\n"); +} + +Cube admissible_ep(Cube cube, PieceFilter f) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + cube_to_arrays(cube, &arrx, f); + + bool used[12] = {0}; + for (int i = 0; i < 12; i++) + if (arrx.ep[i] != -1) + used[arrx.ep[i]] = true; + for (int i = 0, j = 0; i < 12; i++) { + while (j < 11 && used[j]) j++; + if (arrx.ep[i] == -1) + arrx.ep[i] = j++; + } + + return arrays_to_cube(arrx, pf_ep); +} + +Cube inverse_cube(Cube cube) { + static CubeArrayAllocated all = {0}, invall = {0}; + CubeArray arrx = {0}, invx = {0}; + allocate_cubearray(&arrx, &all); + allocate_cubearray(&invx, &invall); + + cube_to_arrays(cube, &arrx, pf_all); + + for (int i = 0; i < 12; i++) { + invx.ep[arrx.ep[i]] = i; + invx.eofb[arrx.ep[i]] = arrx.eofb[i]; + invx.eorl[arrx.ep[i]] = arrx.eorl[i]; + invx.eoud[arrx.ep[i]] = arrx.eoud[i]; + } + for (int i = 0; i < 8; i++) { + invx.cp[arrx.cp[i]] = i; + invx.coud[arrx.cp[i]] = (3 - arrx.coud[i])%3; + invx.corl[arrx.cp[i]] = (3 - arrx.corl[i])%3; + invx.cofb[arrx.cp[i]] = (3 - arrx.cofb[i])%3; + } + for (int i = 0; i < 6; i++) + invx.cpos[arrx.cpos[i]] = i; + + return arrays_to_cube(invx, pf_all); +} + +Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter f) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + + cube_to_arrays(c, &arrx, f); + + if (f.epose || f.eposs || f.eposm) + apply_permutation( arr.ep, arrx.ep, 12 ); + if (f.eofb) { apply_permutation( arr.ep, arrx.eofb, 12 ); + sum_arrays_mod( arr.eofb, arrx.eofb, 12, 2 ); } + if (f.eorl) { apply_permutation( arr.ep, arrx.eorl, 12 ); + sum_arrays_mod( arr.eorl, arrx.eorl, 12, 2 ); } + if (f.eoud) { apply_permutation( arr.ep, arrx.eoud, 12 ); + sum_arrays_mod( arr.eoud, arrx.eoud, 12, 2 ); } + if (f.cp) apply_permutation( arr.cp, arrx.cp, 8 ); + if (f.coud) { apply_permutation( arr.cp, arrx.coud, 8 ); + sum_arrays_mod( arr.coud, arrx.coud, 8, 3 ); } + if (f.corl) { apply_permutation( arr.cp, arrx.corl, 8 ); + sum_arrays_mod( arr.corl, arrx.corl, 8, 3 ); } + if (f.cofb) { apply_permutation( arr.cp, arrx.cofb, 8 ); + sum_arrays_mod( arr.cofb, arrx.cofb, 8, 3 ); } + if (f.cpos) apply_permutation( arr.cpos, arrx.cpos, 6 ); + + return arrays_to_cube(arrx, f); +} + +Cube compose_filtered(Cube c2, Cube c1, PieceFilter f) { + static CubeArrayAllocated all = {0}; + CubeArray arrx = {0}; + allocate_cubearray(&arrx, &all); + + cube_to_arrays(c2, &arrx, f); + return move_via_arrays(arrx, c1, f); +} + +Cube compose(Cube c2, Cube c1) { + return compose_filtered(c2, c1, pf_all); +} diff --git a/old/2021-05-26-before-restyle/cube.h b/old/2021-05-26-before-restyle/cube.h @@ -0,0 +1,65 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include "utils.h" + +typedef enum {U_center,D_center,R_center,L_center,F_center,B_center} Center; +typedef enum { UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR } Edge; +typedef enum { UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR } Corner; + +typedef struct { + uint16_t eofb, eorl, eoud, coud, cofb, corl, + epose, eposs, eposm, cp, cpos; +} Cube; + +typedef struct { + bool edge[12], corner[8], center[6]; +} Block; + +typedef struct { + bool epose, eposs, eposm, eofb, eorl, eoud, cp, coud, cofb, corl, cpos; +} PieceFilter; + +typedef struct { + int *ep, *eofb, *eorl, *eoud, *cp, *coud, *corl, *cofb, *cpos; +} CubeArray; + +extern PieceFilter pf_all, pf_cpos, pf_ep, pf_cp, + pf_e, pf_s, pf_m, pf_eo, pf_co; + +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +Cube arrays_to_cube(CubeArray arr, PieceFilter f); + +/* piece can be edge or corner and orientation is any of the following: + "eofb", "eorl", "eoud", "coud", "corl", "cofb" + Return either 0 (oriented) or 1 for edges and 0, 1 or 2 for corners */ +int piece_orientation(Cube cube, int piece, char *orientation); +Center center_at(Cube cube, Center c); +Edge edge_at(Cube cube, Edge e); +Corner corner_at(Cube cube, Corner c); +bool block_solved(Cube cube, Block); +/* Aggiungi funzioni per "queries" sul cubo: se pezzo è orientato rispetto ad + un certo asse, se il pezzo è risolto... */ +/* Would be nice: a funciton block_solved(Cube c, Block b), where Block is + something like struct {bool centers[6], edges[12], corners[8]} + (The advantage over checking pieces one by one is that I can convert + to cubearray only once and for all) */ +/* Altro TODO, ma forse non ne vale la pena: pre-calcolare tutti i possibili + valori per questi, e salvare i risultati in array (facile per cp e cpos, + mentre per ep bisogna anche cercare quale tra epose, eposs e eposm contiene + il valore giusto) */ + +bool equal(Cube c1, Cube c2); +bool is_solved(Cube cube); +void print_cube(Cube cube); +Cube admissible_ep(Cube cube, PieceFilter f); /* Returns admissible ep */ +Cube inverse_cube(Cube cube); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter pf); + +#endif diff --git a/old/2021-05-26-before-restyle/main.c b/old/2021-05-26-before-restyle/main.c @@ -0,0 +1,24 @@ +#include <stdio.h> +#include "cube.h" +#include "moves.h" +#include "solve.h" +#include "transformations.h" + +int main() { + init_ttables(true, true); + init_aux_tables(); + init_transformations(true, true); + + + char moves[100] = "R' D2 F2 U2 R F2 R D2 L' R2 D2 F D' L' U' B R' D' U R' B"; + NissMove alg[100]; + read_moves(moves, alg, 100); + Cube cube = apply_alg(alg, (Cube){0}); + print_cube(transform_cube(rd, cube)); + + transform_alg(rd, alg); + print_alg(alg); + + + return 0; +} diff --git a/old/2021-05-26-before-restyle/moves.c b/old/2021-05-26-before-restyle/moves.c @@ -0,0 +1,412 @@ +#include "moves.h" + +Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +/* void sort_cancel_rotate(NissMove *alg, int n, bool inv, int top, int front); */ +bool read_ttables_file(); +bool write_ttables_file(); + +/* Transition tables */ +uint16_t epose_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposs_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposm_ttable[NMOVES][factorial12/factorial8]; +uint16_t eofb_ttable[NMOVES][pow2to11]; +uint16_t eorl_ttable[NMOVES][pow2to11]; +uint16_t eoud_ttable[NMOVES][pow2to11]; +uint16_t cp_ttable[NMOVES][factorial8]; +uint16_t coud_ttable[NMOVES][pow3to7]; +uint16_t cofb_ttable[NMOVES][pow3to7]; +uint16_t corl_ttable[NMOVES][pow3to7]; +uint16_t cpos_ttable[NMOVES][factorial6]; + +bool commute[NMOVES][NMOVES]; +bool possible_next[NMOVES][NMOVES][NMOVES]; +Move inverse[NMOVES]; +NissMove rotation_algs[24][3] = { + { { .m = NULLMOVE }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y }, { .m = z2 }, { .m = NULLMOVE } }, + { { .m = x2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y3 }, { .m = z2 }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y3 }, { .m = NULLMOVE } }, +}; + +char move_string[NMOVES][5] = + { "-", + "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", + "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", "Rw", "Rw2", "Rw\'", + "Lw", "Lw2", "Lw\'", "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", "S", "S2", "S\'", "E", "E2", "E\'", + "x", "x2", "x\'", "y", "y2", "y\'", "z", "z2", "z\'" }; + +/* For each type of pieces only the effects of U, x and y are described */ +int edge_cycle[NMOVES][12] = + { [U] = {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, + [x] = {DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR}, + [y] = {UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL} }; +int eofb_flipped[NMOVES][12] = + { [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eorl_flipped[NMOVES][12] = + { [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } }; +int eoud_flipped[NMOVES][12] = + { [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }; +int corner_cycle[NMOVES][8] = + { [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} }; +int coud_flipped[NMOVES][8] = + { [x] = {[UFR]=2,[UBR]=1,[DBR]=2,[DFR]=1,[UFL]=1,[UBL]=2,[DBL]=1,[DFL]=2} }; +int corl_flipped[NMOVES][8] = + { [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = {[UFR]=1,[UBR]=2,[UBL]=1,[UFL]=2,[DFR]=2,[DBR]=1,[DBL]=2,[DFL]=1} }; +int cofb_flipped[NMOVES][8] = + { [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = {[UFR]=1,[UBR]=2,[DFR]=2,[DBR]=1,[UBL]=1,[UFL]=2,[DBL]=2,[DFL]=1}, + [y] = {[UFR]=2,[UBR]=1,[UBL]=2,[UFL]=1,[DFR]=1,[DBR]=2,[DBL]=1,[DFL]=2} }; +int center_cycle[NMOVES][6] = + { [x] = {F_center, B_center, R_center, L_center, D_center, U_center}, + [y] = {U_center, D_center, B_center, F_center, R_center, L_center} }; + +/* Each move is reduced to a combination of U, x and y using this table */ +Move equiv_moves[NMOVES][14] = { + [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 }, + [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 }, + [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 }, + [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 }, + [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 }, + [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 }, + [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 }, + + [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 }, + [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 }, + [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 }, + [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 }, + [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 }, + [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 }, + [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 }, + [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 }, + [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 }, + [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + + [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 }, + [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 }, + [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 }, + [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 }, + [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 }, + [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 }, + [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 }, + [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 }, + [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 }, + + [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/* Movesets */ +bool standard_moveset[NMOVES] = { + [U] = true, [U2] = true, [U3] = true, [D] = true, [D2] = true, [D3] = true, + [R] = true, [R2] = true, [R3] = true, [L] = true, [L2] = true, [L3] = true, + [F] = true, [F2] = true, [F3] = true, [B] = true, [B2] = true, [B3] = true, +}; + +bool is_solved_up_to_reorient(Cube cube) { + for (int i = 0; i < 25; i++) + if (is_solved(apply_alg(rotation_algs[i], cube))) + return true; + return false; +} + +Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f) { + return move_via_arrays((CubeArray) + { edge_cycle[m], eofb_flipped[m], eorl_flipped[m], eoud_flipped[m], + corner_cycle[m], coud_flipped[m], corl_flipped[m], cofb_flipped[m], + center_cycle[m] }, cube, f); +} + +int len(NissMove *alg) { + int i; + for (i = 0; alg[i].m != NULLMOVE; i++); + return i; +} + +int copy_alg(NissMove *src, NissMove *dest) { + int i; + for (i = 0; src[i].m != NULLMOVE; i++) + dest[i] = src[i]; + dest[i].m = NULLMOVE; + return i; +} + +int invert_alg(NissMove *src, NissMove *dest) { + int n = len(src); + for (int i = 0; i < n; i++) + dest[n-i-1] = (NissMove){.m=inverse[src[i].m], .inverse=src[i].inverse}; + dest[n].m = NULLMOVE; + return n; +} + +int concat(NissMove *src1, NissMove *src2, NissMove *dest) { + int n1 = len(src1), n2 = len(src2); + copy_alg(src1, dest); + copy_alg(src2, dest+n1); + return n1+n2; +} + +/* TODO: all strings start with space?? */ +void print_alg(NissMove *alg) { + bool niss = false; + for (int i = 0; alg[i].m != NULLMOVE; i++) { + char *fill = !niss && alg[i].inverse ? " (" : + (niss && !alg[i].inverse ? ") " : " "); + printf("%s%s", fill, move_string[alg[i].m]); + niss = alg[i].inverse; + } + printf("%s\n", niss ? ")" : ""); +} + +int read_moves(char *str, NissMove *alg, int n) { + bool niss = false; + int c = 0; + + for (int i = 0; str[i] && c < n; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' || str[i] == ')') { + if ((niss && str[i] == '(') || (!niss && str[i] == ')')) + return -1; + niss = !niss; + continue; + } + + alg[c].inverse = niss; alg[c].m = NULLMOVE; + for (Move j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + alg[c].m = j; + if (alg[c].m <= B && str[i+1]=='w') { alg[c].m += Uw - U; i++; } + if (str[i+1]=='2') { alg[c].m += 1; i++; } + else if (str[i+1]=='\'' || str[i+1]=='3') { alg[c].m += 2; i++; } + c++; + break; + } + } + } + + alg[c].m = NULLMOVE; + return c; +} + +/* TODO: rewrite cleanup(). Idea: + 1. split alg in normal + inverse part, rewriting each with equiv_moves + 2. shift rotations to the end while transforming all moves in between + ARGH! this uses transformation! One more reason to merge everything in + one file. + 3. cancellations +*/ + +bool read_ttables_file() { + FILE *ttf; + long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6 }; + if ((ttf = fopen("ttables", "rb")) != NULL) { + bool r = true; + for (int m = 0; m < NMOVES; m++) { + r = r && fread(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fread(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fread(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fread(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fread(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4]; + r = r && fread(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5]; + r = r && fread(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fread(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fread(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8]; + r = r && fread(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9]; + r = r && fread(cpos_ttable[m], sizeof(uint16_t), me[10], ttf) == me[10]; + } + fclose(ttf); + return r; + } else return false; +} + +bool write_ttables_file() { + FILE *ttf; + long unsigned int me[11] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6 }; + if ((ttf = fopen("ttables", "wb")) != NULL) { + bool r = true; + for (int m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_ttable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fwrite(eposs_ttable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fwrite(eposm_ttable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fwrite(eofb_ttable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fwrite(eorl_ttable[m], sizeof(uint16_t), me[4], ttf) == me[4]; + r = r && fwrite(eoud_ttable[m], sizeof(uint16_t), me[5], ttf) == me[5]; + r = r && fwrite(cp_ttable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fwrite(coud_ttable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fwrite(corl_ttable[m], sizeof(uint16_t), me[8], ttf) == me[8]; + r = r && fwrite(cofb_ttable[m], sizeof(uint16_t), me[9], ttf) == me[9]; + r = r && fwrite(cpos_ttable[m], sizeof(uint16_t), me[10],ttf) == me[10]; + } + fclose(ttf); + return r; + } else return false; +} + +void init_ttables(bool read, bool write) { + /* Generate all move cycles and flips; I do this regardless */ + for (int i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + Cube c = {0}; + for (int j = 0; equiv_moves[i][j]; j++) + c = apply_move_cubearray(equiv_moves[i][j], c, pf_all); + + CubeArray arrs = { + edge_cycle[i], eofb_flipped[i], eorl_flipped[i], eoud_flipped[i], + corner_cycle[i], coud_flipped[i], corl_flipped[i], cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read) + if (read_ttables_file()) + return; + + /* Initialize transition tables */ + for (int m = 0; m < NMOVES; m++) { + for (uint16_t i = 0; i < factorial12/factorial8; i++) { + epose_ttable[m][i] = apply_move_cubearray(m,(Cube){.epose=i},pf_e).epose; + eposs_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposs=i},pf_s).eposs; + eposm_ttable[m][i] = apply_move_cubearray(m,(Cube){.eposm=i},pf_m).eposm; + } + for (uint16_t i = 0; i < pow2to11; i++ ) { + eofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.eofb=i},pf_eo).eofb; + eorl_ttable[m][i] = apply_move_cubearray(m,(Cube){.eorl=i},pf_eo).eorl; + eoud_ttable[m][i] = apply_move_cubearray(m,(Cube){.eoud=i},pf_eo).eoud; + } + for (uint16_t i = 0; i < pow3to7; i++) { + coud_ttable[m][i] = apply_move_cubearray(m,(Cube){.coud=i},pf_co).coud; + corl_ttable[m][i] = apply_move_cubearray(m,(Cube){.corl=i},pf_co).corl; + cofb_ttable[m][i] = apply_move_cubearray(m,(Cube){.cofb=i},pf_co).cofb; + } + for (uint16_t i = 0; i < factorial8; i++) + cp_ttable[m][i] = apply_move_cubearray(m,(Cube){.cp=i},pf_cp).cp; + for (uint16_t i = 0; i < factorial6; i++) + cpos_ttable[m][i] = apply_move_cubearray(m,(Cube){.cpos=i},pf_cpos).cpos; + } + + if (write) + if (!write_ttables_file()) + printf("Error in writing ttables: file not writable\n"); +} + +Cube move_cube(Move m, Cube cube) { + Cube moved = {0}; + + moved.epose = epose_ttable[m][cube.epose]; + moved.eposs = eposs_ttable[m][cube.eposs]; + moved.eposm = eposm_ttable[m][cube.eposm]; + moved.eofb = eofb_ttable[m][cube.eofb]; + moved.eorl = eorl_ttable[m][cube.eorl]; + moved.eoud = eoud_ttable[m][cube.eoud]; + moved.coud = coud_ttable[m][cube.coud]; + moved.cofb = cofb_ttable[m][cube.cofb]; + moved.corl = corl_ttable[m][cube.corl]; + moved.cp = cp_ttable[m][cube.cp]; + moved.cpos = cpos_ttable[m][cube.cpos]; + + return moved; +} + +Cube apply_alg_filtered(NissMove *alg, Cube cube, PieceFilter f) { + Cube ret = {0}; + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (alg[i].inverse) + ret = move_cube(alg[i].m, ret); + + ret = compose_filtered(cube, inverse_cube(ret), f); + + for (int i = 0; alg[i].m != NULLMOVE; i++) + if (!alg[i].inverse) + ret = move_cube(alg[i].m, ret); + return ret; +} + +Cube apply_alg(NissMove *alg, Cube cube) { + return apply_alg_filtered(alg, cube, pf_all); +} + +void init_aux_tables() { + /* Commute */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + commute[i][j] = equal(move_cube(i, move_cube(j, (Cube){0})), + move_cube(j, move_cube(i, (Cube){0}))); + + /* Possible next (if the sequence i j k is valid) */ + for (int i = 0; i < NMOVES; i++) + for (int j = 0; j < NMOVES; j++) + for (int k = 0; k < NMOVES; k++) + possible_next[i][j][k] = + (j == 0) || + (j != 0 && (j-(j-1)%3) != (k-(k-1)%3) && + !(i != 0 && commute[i][j] && (i-(i-1)%3) == (k-(k-1)%3))); + + /* Inverse */ + for (int i = 0; i < NMOVES; i++) + inverse[i] = i == NULLMOVE ? NULLMOVE : i + 2 - 2*((i-1)%3); + +} + diff --git a/old/2021-05-26-before-restyle/moves.h b/old/2021-05-26-before-restyle/moves.h @@ -0,0 +1,51 @@ +#ifndef MOVES_H +#define MOVES_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "cube.h" +#include "utils.h" + +#define NMOVES (z3+1) + +typedef enum { + NULLMOVE, + U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, Rw, Rw2, Rw3, + Lw, Lw2, Lw3, Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, S, S2, S3, E, E2, E3, + x, x2, x3, y, y2, y3, z, z2, z3, +} Move; + +/* An alg is an array of "NissMoves", which can be on normal or on inverse. */ +typedef struct { bool inverse; Move m; } NissMove; + +/* Movesets */ +extern bool standard_moveset[NMOVES]; + +extern bool commute[NMOVES][NMOVES]; +extern bool possible_next[NMOVES][NMOVES][NMOVES]; +extern Move inverse[NMOVES]; +extern NissMove rotation_algs[24][3]; /* Same order as transformations */ + +int len(NissMove *alg); +int copy_alg(NissMove *src, NissMove *dest); /*return number of moves copied */ +int invert_alg(NissMove *src, NissMove *dest); +int concat(NissMove *src1, NissMove *src2, NissMove *dest); +void print_alg(NissMove *alg); +int read_moves(char *str, NissMove *alg, int n); /* reads at most n moves */ +void cleanup(NissMove *src, int n); /* rewrites using basic moves, at most n */ + +bool is_solved_up_to_reorient(Cube cube); +Cube move_cube(Move m, Cube cube); +Cube apply_alg(NissMove *alg, Cube cube); +Cube apply_alg_filtered(NissMove *alg, Cube cube, PieceFilter f); + +/* Merge the following two? + always in this order */ +void init_ttables(bool read, bool write); +void init_aux_tables(); + + +#endif diff --git a/old/2021-05-26-before-restyle/solve.c b/old/2021-05-26-before-restyle/solve.c @@ -0,0 +1,180 @@ +#include "solve.h" + +/* Data for creating a pruning table: + - compressed: if set to true, each entry occupies only 4 bits, but values + larger than 15 cannot be stored. + - available[] is the list of availabel moves, as above. + - *ptable is the actual table to fill. + - n is the number of states (size of ptable). + - index must "linearize" the cube, i.e. return its index in ptable. + - fname is the name of the file where to store the table */ +typedef struct { + bool compressed, *available; + int max_moves; + uint8_t *ptable; + uint64_t n; + uint64_t (*index)(Cube); + char *fname; +} PruneData; + +/* TODO: comment this */ +typedef struct { + bool niss; + int m, d; + uint64_t *n; + Move last1, last2; +} DfsData; + +void solve_dfs(Cube cube, SolveData *sd, DfsData dd); +void init_ptable(PruneData *pd, bool read, bool write); + +/* Search solutions of lenght exactly d */ +void solve_dfs(Cube cube, SolveData *sd, DfsData dd) { + if (*dd.n >= sd->max_solutions || + ((!sd->can_niss || dd.niss) && dd.m + sd->f(cube) > dd.d)) + return; + + (sd->solutions[*dd.n][dd.m]).inverse = dd.niss; + (sd->solutions[*dd.n][dd.m]).m = NULLMOVE; + + if (!sd->f(cube)) { /* Solved */ + if (dd.m == dd.d) { + (*dd.n)++; + if (*dd.n < sd->max_solutions) + copy_alg(sd->solutions[*dd.n-1], sd->solutions[*dd.n]); + } + return; + } + + for (int i = 0; i < NMOVES && sd->sorted_moves[i] != NULLMOVE; i++) { + Move move = sd->sorted_moves[i]; + if (possible_next[dd.last2][dd.last1][move]) { + sd->solutions[*dd.n][dd.m].inverse = dd.niss; + sd->solutions[*dd.n][dd.m].m = move; + DfsData nn = { .niss = dd.niss, .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = move, .last2 = dd.last1 }; + solve_dfs(move_cube(move, cube), sd, nn); + } + } + + if (sd->can_niss && !dd.niss && + (!dd.m || (dd.m && sd->f(move_cube(dd.last1, (Cube){0}))))) { + DfsData nn = { .niss = true, .m = dd.m, .d = dd.d, .n = dd.n }; + solve_dfs(inverse_cube(cube), sd, nn); + } +} + +/* Iterative deepening depth-first search: for i running from the minimum + to the maximum number of moves allowed, looks for solutions of length i. */ +int solve(Cube cube, SolveData *sd) { + if (sd->precondition != NULL && !sd->precondition(cube)) + return -1; + + /* If not given, generate sorted list of moves */ + if (sd->sorted_moves[0] == NULLMOVE) { + int a[NMOVES], b[NMOVES], ia = 0, ib = 0; + for (int i = 0; i < NMOVES; i++) { + if (sd->available[i]) { + if (sd->f(move_cube(i, (Cube){0}))) + a[ia++] = i; + else + b[ib++] = i; + } + } + intarrcopy(a, (int *)sd->sorted_moves, ia); + intarrcopy(b, (int *)sd->sorted_moves+ia, ib); + sd->sorted_moves[ia+ib] = NULLMOVE; + } + + sd->max_solutions = min(sd->max_solutions, MAXS); + /*TODO + Cube rotated = apply_alg(sd->pre_rotation, (Cube){0}); + cube = apply_alg(inverse_cube(rotated), compose(cube, rotated)); + */ + + uint64_t ret = 0; + for (int i=sd->min_moves; i<=sd->max_moves&&!(ret&&sd->optimal_only); i++) { + DfsData dd = { .d = i, .n = &ret }; + solve_dfs(cube, sd, dd); + } + + /* TODO: transform solutions with inverse of pre_rotation */ + /* + for (uint64_t i = 0; i < ret; i++) { + if (sd->cleanup) + cleanup(sd->solutions[i], sd->max_moves*3); + }*/ + + return ret; +} + +void prune_dfs(Cube cube, PruneData *pd, DfsData dd) { + uint64_t ind = pd->index(cube); + if ((!ind || pd->ptable[ind]) && pd->ptable[ind] != dd.m) + return; + if (dd.m == dd.d) { + if (ind && !pd->ptable[ind]) { + pd->ptable[ind] = dd.m; + (*dd.n)++; + } + return; + } + + for (int i = 0; i < NMOVES; i++) { + if (dd.m<20) + if (possible_next[dd.last2][dd.last1][i] && pd->available[i]) { + DfsData nn = { .m = dd.m+1, .d = dd.d, .n = dd.n, + .last1 = i, .last2 = dd.last1 }; + prune_dfs(move_cube(i, cube), pd, nn); + } + } +} + +void init_ptable(PruneData *pd, bool read, bool write) { + if (read) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "rb")) != NULL) { + uint64_t r = fread(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + if (r == pd->n) return; + } + } + + /* TODO: for now it behaves always as if copressed = false */ + for (uint64_t i = 0; i < pd->n; i++) + pd->ptable[i] = 0; + + uint64_t s = 1; + for (int i = 1; i < pd->max_moves && s < pd->n; i++) { + DfsData dd = { .d = i, .n = &s }; + prune_dfs((Cube){0}, pd, dd); + } + + if (write) { + FILE *ptf; + if ((ptf = fopen(pd->fname, "wb")) != NULL) { + fwrite(pd->ptable, sizeof(uint8_t), pd->n, ptf); + fclose(ptf); + return; + } + } +} + +/* Solving steps (and indexing functions) */ + +uint64_t index_eofb(Cube cube) { return cube.eofb; } +int f_eofb(Cube cube) { + static bool initialized_ptable; + static uint8_t pt_eofb[pow2to11]; + if (!initialized_ptable) { + PruneData pd = { + .compressed = false, .available = standard_moveset, .max_moves = 13, + .ptable = pt_eofb, .n = pow2to11, .index = index_eofb, + .fname = "ptable_eofb" + }; + init_ptable(&pd, false, true); + initialized_ptable = true; + } + return cube.eofb ? pt_eofb[cube.eofb] : 0; +} + diff --git a/old/2021-05-26-before-restyle/solve.h b/old/2021-05-26-before-restyle/solve.h @@ -0,0 +1,59 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include <stdlib.h> +#include "cube.h" +#include "moves.h" +#include "transformations.h" + +/* Maximum number of moves per solution and of solutions */ +#define MAXM 30 +#define MAXS 999 + +/* Data for solving a step: + - can_niss is true niss can be used, false otherwise. + - optimal_only if true, dynamically updates max_moves so non-optimal + solutions are discarded. + - cleanup determines whether the cleaunup() function should be used on + the found solutions before returning. + - available[m] is true if the move m can be used, false otherwise. + (Rename to moveset[]?) + - min_moves and max_moves are the minimum and maximum number of moves that + can be used. + - max_solution is the maximum number of solutions that can be returned. + - precondition can be used to check wheter the step can actually be applied + to the cube. If it returns false, solve() stops immediately returning -1. + - f must return 0 if and only if the step is solve, otherwise it must return + a lower bound for the number of moves required (without niss). + - sorted_moves[] can be used to specify in which order moves are tried + by the solving algorithm (for example if one wants to always try F' before + F). If sorted_moves[0] == NULLMOVE, the list is generated automatically. + It is advised to list first all the moves that actually influence the + solved state of the step (this is the default choice). This is in order to + avoid cases like B2 F for EO and to NISS only when it makes sense. + - start_moves [Currently unused, REMOVE] + are the moves that will be used as first moves of all + solutions. For example giving R' U' F (F' U R) will generate FMC scrambles + and y (y) will solve the step on another axis. + - pre_rotation are the rotations to apply before the scamble to solve + the step wrt a different orientation + - pre_rotation are the rotations to apply before the scamble to solve + the step wth respect to a different orientation. + TODO: cange name + - solutions[][] is the array where to store the found solutions. */ +typedef struct { + bool can_niss, optimal_only, cleanup, *available; + int min_moves, max_moves, max_solutions; + int (*f)(Cube); + bool (*precondition)(Cube); + Move sorted_moves[NMOVES]; + Transformation pre_rotation; + NissMove solutions[MAXS][MAXM]; +} SolveData; + +int solve(Cube cube, SolveData *data); /* Returns the number of solutions. */ + +/* Steps */ +int f_eofb(Cube cube); + +#endif diff --git a/old/2021-05-26-before-restyle/transformations.c b/old/2021-05-26-before-restyle/transformations.c @@ -0,0 +1,200 @@ +#include "transformations.h" + +int edge_slice(int e); /* Return slice (e=0, s=1, m=2) to which e belongs */ +Cube rotate_via_compose(Transformation r, Cube c, PieceFilter f); +bool read_rtables_file(); +bool write_rtables_file(); + +/* Values mod 3 to determine from which side to take the state to convert */ +int epose_source[NTRANS]; /* 0 = epose, 1 = eposs, 2 = eposm */ +int eposs_source[NTRANS]; +int eposm_source[NTRANS]; +int eofb_source[NTRANS]; /* 0 = eoud, 1 = eorl, 2 = eofb */ +int eorl_source[NTRANS]; +int eoud_source[NTRANS]; +int coud_source[NTRANS]; /* 0 = coud, 1 = corl, 2 = cofb */ +int cofb_source[NTRANS]; +int corl_source[NTRANS]; + +/* Transition tables for rotations (n+1 is mirror) */ +uint16_t epose_rtable[NTRANS][factorial12/factorial8]; +uint16_t eposs_rtable[NTRANS][factorial12/factorial8]; +uint16_t eposm_rtable[NTRANS][factorial12/factorial8]; +uint16_t eo_rtable[NTRANS][pow2to11]; +uint16_t cp_rtable[NTRANS][factorial8]; +uint16_t co_rtable[NTRANS][pow3to7]; +uint16_t cpos_rtable[NTRANS][factorial6]; + +/* Same for moves */ +uint16_t moves_rtable[NTRANS][NMOVES]; + +NissMove rotation_niss[NTRANS][6]; + +int edge_slice(int e) { + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + return 2; +} + +Cube rotate_via_compose(Transformation r, Cube c, PieceFilter f) { + if (r != mirror) { + return apply_alg_filtered(rotation_niss[r], c, f); + } else { + static int zero12[12] = {0,0,0,0,0,0,0,0,0,0,0,0}, + zero8[12] = {0,0,0,0,0,0,0,0}, + mirror_ep[12] = {UF,UR,UB,UL,DF,DR,DB,DL,FL,FR,BR,BL}, + mirror_cp[8] = {UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL}, + mirror_cpos[6] = + {U_center,D_center,L_center,R_center,F_center,B_center}; + return move_via_arrays((CubeArray){ + .ep = mirror_ep, .eofb = zero12, .eorl = zero12, .eoud = zero12, + .cp = mirror_cp, .coud = zero8, .corl = zero8, .cofb = zero8, + .cpos = mirror_cpos}, c, f); + } +} + +bool read_rtables_file() { + FILE *ttf; + long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES }; + if ((ttf = fopen("rtables", "rb")) != NULL) { + bool r = true; + for (int m = 0; m < NTRANS; m++) { + r = r && fread(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fread(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fread(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fread(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fread(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fread(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fread(cpos_rtable[m], sizeof(uint16_t), me[10], ttf) == me[10]; + r = r && fread(moves_rtable[m], sizeof(uint16_t), me[11], ttf) == me[11]; + } + fclose(ttf); + return r; + } else return false; +} + +bool write_rtables_file() { + FILE *ttf; + long unsigned int me[12] = { factorial12/factorial8, factorial12/factorial8, + factorial12/factorial8, pow2to11, pow2to11, pow2to11, + factorial8, pow3to7, pow3to7, pow3to7, factorial6, NMOVES }; + if ((ttf = fopen("rtables", "wb")) != NULL) { + bool r = true; + for (int m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_rtable[m], sizeof(uint16_t), me[0], ttf) == me[0]; + r = r && fwrite(eposs_rtable[m], sizeof(uint16_t), me[1], ttf) == me[1]; + r = r && fwrite(eposm_rtable[m], sizeof(uint16_t), me[2], ttf) == me[2]; + r = r && fwrite(eo_rtable[m], sizeof(uint16_t), me[3], ttf) == me[3]; + r = r && fwrite(cp_rtable[m], sizeof(uint16_t), me[6], ttf) == me[6]; + r = r && fwrite(co_rtable[m], sizeof(uint16_t), me[7], ttf) == me[7]; + r = r && fwrite(cpos_rtable[m], sizeof(uint16_t), me[10],ttf) == me[10]; + r = r && fwrite(moves_rtable[m], sizeof(uint16_t), me[11],ttf) == me[11]; + } + fclose(ttf); + return r; + } else return false; +} + +void init_transformations(bool read, bool write) { + /* Compute sources */ + for (int i = 0; i < NTRANS; i++) { + Cube cube = {0}; + if (i != mirror) + cube = apply_alg(rotation_algs[i], (Cube){0}); + epose_source[i] = edge_slice(edge_at(cube, FR)); + eposs_source[i] = edge_slice(edge_at(cube, UR)); + eposm_source[i] = edge_slice(edge_at(cube, UF)); + eofb_source[i] = center_at(cube, F_center)/2; + eorl_source[i] = center_at(cube, R_center)/2; + eoud_source[i] = center_at(cube, U_center)/2; + coud_source[i] = center_at(cube, U_center)/2; + cofb_source[i] = center_at(cube, F_center)/2; + corl_source[i] = center_at(cube, R_center)/2; + } + + /*TODO: maybe move down*/ + /* Compute rotation_niss array, necessary for rotate_via_compose */ + for (int r = 0; r != mirror; r++) { + concat(rotation_algs[r], rotation_algs[r], rotation_niss[r]); + for (int i = len(rotation_algs[r]); rotation_niss[r][i].m != NULLMOVE; i++) + rotation_niss[r][i].inverse = true; + } + + /* If I can read tables from file, I stop here */ + if (read) + if (read_rtables_file()) + return; + + /* Initialize tables */ + for (int m = 0; m < NTRANS; m++) { + int eparr[12] = {0,0,0,0,0,0,0,0,0,0,0,0}, cparr[8] = {0,0,0,0,0,0,0,0}; + CubeArray epcp = { .ep = eparr, .cp = cparr }; + cube_to_arrays(apply_alg(rotation_algs[m], (Cube){0}), &epcp, + (PieceFilter){.epose=true,.eposs=true,.eposm=true,.cp=true}); + for (uint16_t i = 0; i < factorial12/factorial8; i++) { + Cube c[3] = { admissible_ep((Cube){ .epose = i}, pf_e), + admissible_ep((Cube){ .eposs = i}, pf_s), + admissible_ep((Cube){ .eposm = i}, pf_m) }; + epose_rtable[m][i]=rotate_via_compose(m,c[epose_source[m]],pf_ep).epose; + eposs_rtable[m][i]=rotate_via_compose(m,c[eposs_source[m]],pf_ep).eposs; + eposm_rtable[m][i]=rotate_via_compose(m,c[eposm_source[m]],pf_ep).eposm; + } + for (uint16_t i = 0; i < pow2to11; i++ ) { + int eoarr[12]; + int_to_sum_zero_array(i, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_rtable[m][i] = digit_array_to_int(eoarr, 11, 2); + } + for (uint16_t i = 0; i < pow3to7; i++) { + int coarr[12]; + int_to_sum_zero_array(i, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_rtable[m][i] = digit_array_to_int(coarr, 7, 3); + } + for (uint16_t i = 0; i < factorial8; i++) + cp_rtable[m][i] = rotate_via_compose(m, (Cube){.cp=i}, pf_cp).cp; + for (uint16_t i = 0; i < factorial6; i++) + cpos_rtable[m][i] = rotate_via_compose(m, (Cube){.cpos=i}, pf_cpos).cpos; + for (Move i = 0; i < NMOVES; i++) { + Cube aux = transform_cube(m, move_cube(i, (Cube){0})); + for (Move move = 0; move < NMOVES; move++) + if (is_solved(move_cube(inverse[move], aux))) + moves_rtable[m][i] = move; + } + } + + if (write) + if (!write_rtables_file()) + printf("Error in writing rtables: file not writable\n"); +} + +Cube transform_cube(Transformation t, Cube cube) { + Cube transformed = {0}; + + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }, + aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }, + aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + transformed.epose = epose_rtable[t][aux_epos[epose_source[t]]]; + transformed.eposs = eposs_rtable[t][aux_epos[eposs_source[t]]]; + transformed.eposm = eposm_rtable[t][aux_epos[eposm_source[t]]]; + transformed.eofb = eo_rtable[t][aux_eo[eofb_source[t]]]; + transformed.eorl = eo_rtable[t][aux_eo[eorl_source[t]]]; + transformed.eoud = eo_rtable[t][aux_eo[eoud_source[t]]]; + transformed.coud = co_rtable[t][aux_co[coud_source[t]]]; + transformed.corl = co_rtable[t][aux_co[corl_source[t]]]; + transformed.cofb = co_rtable[t][aux_co[cofb_source[t]]]; + transformed.cp = cp_rtable[t][cube.cp]; + transformed.cpos = cpos_rtable[t][cube.cpos]; + + return transformed; +} + +void transform_alg(Transformation t, NissMove *alg) { + for (int i = 0; alg[i].m; i++) + alg[i].m = moves_rtable[t][alg[i].m]; +} diff --git a/old/2021-05-26-before-restyle/transformations.h b/old/2021-05-26-before-restyle/transformations.h @@ -0,0 +1,34 @@ +#ifndef TRANSFORMATIONS_H +#define TRANSFORMATIONS_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include "cube.h" +#include "moves.h" +#include "utils.h" + +#define NTRANS (mirror+1) + +/* Letters indicate top and front centers + * Mirror is wrt rl + * Lowercase letter to distinguish from pieces */ + +typedef enum { + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, +} Transformation; + +void print_transformation(Transformation t); + +Cube transform_cube(Transformation t, Cube cube); +void transform_alg(Transformation t, NissMove *alg); /* Applied in-place */ + +void init_transformations(bool read, bool write); + +#endif diff --git a/old/2021-05-26-before-restyle/utils.c b/old/2021-05-26-before-restyle/utils.c @@ -0,0 +1,197 @@ +#include "utils.h" + +void swap(int *a, int *b) { + int aux = *a; + *a = *b; + *b = aux; +} + +void intarrcopy(int *src, int *dst, int n) { + for (int i = 0; i < n; i++) + dst[i] = src[i]; +} + +int sum(int *a, int n) { + int ret = 0; + for (int i = 0; i < n; i++) + ret += a[i]; + return ret; +} + +bool is_perm(int *a, int n) { + int aux[n]; for (int i = 0; i < n; i++) aux[i] = 0; + for (int i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + for (int i = 0; i < n; i++) + if (!aux[i]) + return false; + return true; +} + +bool is_subset(int *a, int n, int k) { + int sum = 0; + for (int i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + return sum == k; +} + +int powint(int a, int b) { + return 0; + if (b == 0 || a == 1) + return 1; + if (a == 0) + return 0; + if (b < 0) + return 0; /* Immediate truncate (integer part is 0) */ + if (b % 2) { + return a * powint(a, b-1); + } else { + int x = powint(a, b/2); + return x*x; + } +} + +int factorial(int n) { + if (n < 0) + return 0; + int ret = 1; + for (int i = 1; i <= n; i++) + ret *= i; + return ret; +} + +int binomial(int n, int k) { + if (n < 0 || k < 0 || k > n) + return 0; + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +void int_to_digit_array(int a, int b, int n, int *r) { + if (b <= 1) + for (int i = 0; i < n; i++) + r[i] = 0; + else + for (int i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +int digit_array_to_int(int *a, int n, int b) { + int ret = 0, p = 1; + for (int i = 0; i < n; i++, p *= b) + ret += a[i] * p; + return ret; +} + +int perm_to_index(int *a, int n) { + if (!is_perm(a, n)) + return factorial(n); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + int c = 0; + for (int j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + return ret; +} + +void index_to_perm(int p, int n, int *r) { + if (p < 0 || p >= factorial(n)) /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + int a[n]; for (int j = 0; j < n; j++) a[j] = 0; /* picked elements */ + for (int i = 0; i < n; i++) { + int c = 0, j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } +} + +int perm_sign(int *a, int n) { + if (!is_perm(a,n)) + return false; + int ret = 0; + for (int i = 0; i < n; i++) + for (int j = i+1; j < n; j++) + ret += (a[i]>a[j]) ? 1 : 0; + return ret % 2; +} + +int subset_to_index(int *a, int n, int k) { + /* TODO: better checks */ + if (!is_subset(a, n, k)) + return binomial(n, k); /* Error */ + int ret = 0; + for (int i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + /*ret += factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + ret += binomial(n-i-1, k); + k--; + } + } + return ret; +} + +void index_to_subset(int s, int n, int k, int *r) { + if (s < 0 || s >= binomial(n, k)) { /* Error */ + for (int i = 0; i < n; i++) + r[i] = -1; + return; + } + for (int i = 0; i < n; i++) { + if (k == n-i) { + for (int j = i; j < n; j++) + r[j] = 1; + return; + } + if (k == 0) { + for (int j = i; j < n; j++) + r[j] = 0; + return; + } + /*int v = factorial(n-i-1) / (factorial(k) * factorial(n-i-1-k));*/ + int v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void int_to_sum_zero_array(int x, int b, int n, int *a) { + if (b <= 1) { + for (int i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + int s = 0; + for (int i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +void apply_permutation(int *perm, int *set, int n) { + if (!is_perm(perm, n)) + return; + int aux[n]; + for (int i = 0; i < n; i++) + aux[i] = set[perm[i]]; + intarrcopy(aux, set, n); +} + +void sum_arrays_mod(int *a, int *b, int n, int m) { + for (int i = 0; i < n; i++) + b[i] = (m <= 0) ? 0 : (a[i] + b[i]) % m; +} diff --git a/old/2021-05-26-before-restyle/utils.h b/old/2021-05-26-before-restyle/utils.h @@ -0,0 +1,70 @@ +/* General utility functions */ + +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* Some useful constants */ +#define pow2to11 2048 +#define pow2to12 4096 +#define pow3to7 2187 +#define pow3to8 6561 +#define pow12to4 20736 +#define factorial4 24 +#define factorial6 720 +#define factorial8 40320 +#define factorial12 479001600 +#define binom12on4 495 +#define binom8on4 70 + +/* Generic utility functions */ +void swap(int *a, int *b); +void intarrcopy(int *src, int *dst, int n); +int sum(int *a, int n); +bool is_perm(int *a, int n); +bool is_perm(int *a, int n); + + +/* Standard mathematical functions */ +int powint(int a, int b); +int factorial(int n); +int binomial(int n, int k); + +/* Converts the integer a to its representation in base b (first n digits + * only) and saves the result in r. */ +void int_to_digit_array(int a, int b, int n, int *r); +int digit_array_to_int(int *a, int n, int b); + +/* Converts the first n-1 digits of a number to an array a of digits in base b; + * then adds one element to the array, so that the sum of the elements of a is + * zero modulo b. + * This is used for determing the edge orientation from an 11-bits integer or + * the corner orientation from a 7-trits integer. */ +void int_to_sum_zero_array(int x, int b, int n, int *a); + +/* Converts a permutation on [0..(n-1)] into the integer i which is the index + * of the permutation in the sorted list of all n! such permutations. */ +int perm_to_index(int *a, int n); +void index_to_perm(int p, int n, int *r); + +/* Determine the sign of a permutation */ +int perm_sign(int a[], int n); + +/* Converts a k-element subset of a set from an array of n elements, of which k + * are 1 and n-k are 0, to its index in the sorted list of all such subsets. */ +int subset_to_index(int *a, int n, int k); +void index_to_subset(int s, int n, int k, int *r); + +int ordered_subset_to_index(int *a, int n, int k); +void index_to_ordered_subset(int s, int n, int k, int *r); + +void apply_permutation(int *perm, int *set, int n); + +/* b[i] = (a[i]+b[i])%m for i=1,...,n */ +void sum_arrays_mod(int *a, int *b, int n, int m); + +#endif diff --git a/old/2021-06-02-cleanedup/cube.c b/old/2021-06-02-cleanedup/cube.c @@ -0,0 +1,2010 @@ +#include "cube.h" + +/* Constants and macros *****************************************************/ + +#define pow2to11 2048 +#define pow2to12 4096 +#define pow3to7 2187 +#define pow3to8 6561 +#define pow12to4 20736 +#define factorial4 24 +#define factorial6 720 +#define factorial8 40320 +#define factorial12 479001600 +#define binom12on4 495 +#define binom8on4 70 + +#define BASEALGLEN 50 +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* Local types ***************************************************************/ + +typedef struct cubearray CubeArray; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; + +struct +cubearray +{ + int *ep; + int *eofb; + int *eorl; + int *eoud; + int *cp; + int *coud; + int *corl; + int *cofb; + int *cpos; +}; + +struct +dfsdata +{ + int d; + int m; + bool niss; + Move last1; + Move last2; + AlgList *sols; + Alg current_alg; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static CubeArray * alloc_cubearray(CubeArray *arr, PieceFilter f); +static void append_alg(AlgList *l, Alg alg); +static void append_move(Alg alg, NissMove m); +static Cube apply_alg_filtered(Alg alg, Cube cube, PieceFilter f); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static Cube arrays_to_cube(CubeArray arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static uint16_t epos_from_arrays(int *epos, int *ep); +static int factorial(int n); +static void free_alglist(AlgList *l); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void init_auxtables(); +static void init_moves(); +static void init_trans(); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static void intarrcopy(int *src, int *dst, int n); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray arr, Cube c, PieceFilter pf); +static Move * moveset_to_movelist(bool *moveset, Step step); +static AlgList * new_alglist(); +static int perm_sign(int a[], int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static bool read_rtables_file(); +static bool read_ttables_file(); +static bool realloc_alg(Alg alg, int n); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static void solve_dfs(Cube c, Step s, SolveOptions opts, DfsData dd); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_rtables_file(); +static bool write_ttables_file(); + +/* Aux tables ****************************************************************/ + +bool commute[NMOVES][NMOVES]; +bool possible_next[NMOVES][NMOVES][NMOVES]; +Move inverse_move_aux[NMOVES]; +Trans inverse_trans_aux[NTRANS]; + +/* Some utility constants ****************************************************/ + +static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +static int epe_solved[4] = { FR, FL, BL, BR }; +static int eps_solved[4] = { UL, UR, DL, DR }; +static int epm_solved[4] = { UF, UB, DF, DB }; + +static int ep_mirror[12] = { UF, UR, UB, UL, DF, DR, DB, DL, FL, FR, BR, BL }; +static int cp_mirror[8] = { UFL, UFR, UBR, UBL, DFL, DFR, DBR, DBL }; +static int cpos_mirror[6] = { + U_center, D_center, + L_center, R_center, + F_center, B_center +}; + +static long unsigned int me[12] = { + factorial12/factorial8, + factorial12/factorial8, + factorial12/factorial8, + pow2to11, + pow2to11, + pow2to11, + factorial8, + pow3to7, + pow3to7, + pow3to7, + factorial6, + NMOVES +}; + +static PieceFilter pf_all = { + true, true, true, true, true, true, true, true, true, true, true +}; +static PieceFilter pf_epcp = { + .epose = true, .eposs = true, .eposm = true, .cp = true +}; +static PieceFilter pf_cpos = { .cpos = true }; +static PieceFilter pf_cp = { .cp = true }; +static PieceFilter pf_ep = { .epose = true, .eposs = true, .eposm = true }; +static PieceFilter pf_e = { .epose = true }; +static PieceFilter pf_s = { .eposs = true }; +static PieceFilter pf_m = { .eposm = true }; +static PieceFilter pf_eo = { .eofb = true, .eorl = true, .eoud = true }; +static PieceFilter pf_co = { .coud = true, .cofb = true, .corl = true }; + +/* Strings for moves and pieces **********************************************/ + +char move_string[NMOVES][5] = { + "-", + "U", "U2", "U\'", "D", "D2", "D\'", + "R", "R2", "R\'", "L", "L2", "L\'", + "F", "F2", "F\'", "B", "B2", "B\'", + "Uw", "Uw2", "Uw\'", "Dw", "Dw2", "Dw\'", + "Rw", "Rw2", "Rw\'", "Lw", "Lw2", "Lw\'", + "Fw", "Fw2", "Fw\'", "Bw", "Bw2", "Bw\'", + "M", "M2", "M\'", + "S", "S2", "S\'", + "E", "E2", "E\'", + "x", "x2", "x\'", + "y", "y2", "y\'", + "z", "z2", "z\'" +}; +char edge_string[12][5] = { + "UF", "UL", "UB", "UR", + "DF", "DL", "DB", "DR", + "FR", "FL", "BL", "BR" +}; +char corner_string[8][5] = { + "UFR", "UFL", "UBL", "UBR", + "DFR", "DFL", "DBL", "DBR" +}; +char center_string[6][5] = { + "U", "D", + "R", "L", + "F", "B" +}; + +/* Transition tables for transformations and moves ***************************/ + +uint16_t epose_rtable[NTRANS][factorial12/factorial8]; +uint16_t eposs_rtable[NTRANS][factorial12/factorial8]; +uint16_t eposm_rtable[NTRANS][factorial12/factorial8]; +uint16_t eo_rtable[NTRANS][pow2to11]; +uint16_t cp_rtable[NTRANS][factorial8]; +uint16_t co_rtable[NTRANS][pow3to7]; +uint16_t cpos_rtable[NTRANS][factorial6]; +Move moves_rtable[NTRANS][NMOVES]; + +uint16_t epose_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposs_ttable[NMOVES][factorial12/factorial8]; +uint16_t eposm_ttable[NMOVES][factorial12/factorial8]; +uint16_t eofb_ttable[NMOVES][pow2to11]; +uint16_t eorl_ttable[NMOVES][pow2to11]; +uint16_t eoud_ttable[NMOVES][pow2to11]; +uint16_t cp_ttable[NMOVES][factorial8]; +uint16_t coud_ttable[NMOVES][pow3to7]; +uint16_t cofb_ttable[NMOVES][pow3to7]; +uint16_t corl_ttable[NMOVES][pow3to7]; +uint16_t cpos_ttable[NMOVES][factorial6]; + +/* Used to compute the full transition tables for moves and transformations **/ + +int epose_source[NTRANS]; /* 0 = epose, 1 = eposs, 2 = eposm */ +int eposs_source[NTRANS]; +int eposm_source[NTRANS]; +int eofb_source[NTRANS]; /* 0 = eoud, 1 = eorl, 2 = eofb */ +int eorl_source[NTRANS]; +int eoud_source[NTRANS]; +int coud_source[NTRANS]; /* 0 = coud, 1 = corl, 2 = cofb */ +int cofb_source[NTRANS]; +int corl_source[NTRANS]; + +NissMove rotation_niss[NTRANS][6]; +NissMove rotation_algs[NROTATIONS][3] = { + { { .m = NULLMOVE }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y }, { .m = z2 }, { .m = NULLMOVE } }, + { { .m = x2 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = y3 }, { .m = z2 }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = z3 }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = z }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = x }, { .m = y3 }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = NULLMOVE }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y2 }, { .m = NULLMOVE } }, + { { .m = x3 }, { .m = y3 }, { .m = NULLMOVE } }, +}; + +/* For each type of pieces only the effects of U, x and y are described */ +int edge_cycle[NMOVES][12] = { + [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, + [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, + [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL } +}; +int eofb_flipped[NMOVES][12] = { + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; +int eorl_flipped[NMOVES][12] = { + [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; +int eoud_flipped[NMOVES][12] = { + [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } +}; +int corner_cycle[NMOVES][8] = { + [U] = {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, + [x] = {DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR}, + [y] = {UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL} +}; +int coud_flipped[NMOVES][8] = { + [x] = { + [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, + [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 + } +}; +int corl_flipped[NMOVES][8] = { + [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + } +}; +int cofb_flipped[NMOVES][8] = { + [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + }, + [y] = { + [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1, + [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2 + } +}; +int center_cycle[NMOVES][6] = { + [x] = { F_center, B_center, R_center, L_center, D_center, U_center }, + [y] = { U_center, D_center, B_center, F_center, R_center, L_center } +}; + +/* Each move is reduced to a combination of U, x and y using this table */ +Move equiv_moves[NMOVES][14] = { + [U] = { U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U2] = { U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [U3] = { U, U, U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D] = { x, x, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D2] = { x, x, U, U, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [D3] = { x, x, U, U, U, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [R] = { y, x, U, x, x, x, y, y, y, 0, 0, 0, 0, 0 }, + [R2] = { y, x, U, U, x, x, x, y, y, y, 0, 0, 0, 0 }, + [R3] = { y, x, U, U, U, x, x, x, y, y, y, 0, 0, 0 }, + [L] = { y, y, y, x, U, x, x, x, y, 0, 0, 0, 0, 0 }, + [L2] = { y, y, y, x, U, U, x, x, x, y, 0, 0, 0, 0 }, + [L3] = { y, y, y, x, U, U, U, x, x, x, y, 0, 0, 0 }, + [F] = { x, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F2] = { x, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [F3] = { x, U, U, U, x, x, x, 0, 0, 0, 0, 0, 0, 0 }, + [B] = { x, x, x, U, x, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B2] = { x, x, x, U, U, x, 0, 0, 0, 0, 0, 0, 0, 0 }, + [B3] = { x, x, x, U, U, U, x, 0, 0, 0, 0, 0, 0, 0 }, + + [Uw] = { x, x, U, x, x, y, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Uw2] = { x, x, U, U, x, x, y, y, 0, 0, 0, 0, 0, 0 }, + [Uw3] = { x, x, U, U, U, x, x, y, y, y, 0, 0, 0, 0 }, + [Dw] = { U, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw2] = { U, U, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Dw3] = { U, U, U, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [Rw] = { y, y, y, x, U, x, x, x, y, x, 0, 0, 0, 0 }, + [Rw2] = { y, y, y, x, U, U, x, x, x, y, x, x, 0, 0 }, + [Rw3] = { y, y, y, x, U, U, U, y, x, x, x, y, 0, 0 }, + [Lw] = { y, x, U, x, x, x, y, y, y, x, x, x, 0, 0 }, + [Lw2] = { y, x, U, U, x, x, x, y, y, y, x, x, 0, 0 }, + [Lw3] = { y, x, U, U, U, x, x, x, y, y, y, x, 0, 0 }, + [Fw] = { x, x, x, U, y, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw2] = { x, x, x, U, U, y, y, x, 0, 0, 0, 0, 0, 0 }, + [Fw3] = { x, x, x, U, U, U, y, x, 0, 0, 0, 0, 0, 0 }, + [Bw] = { x, U, y, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw2] = { x, U, U, y, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + [Bw3] = { x, U, U, U, y, x, x, x, 0, 0, 0, 0, 0, 0 }, + + [M] = { y, x, U, x, x, U, U, U, y, x, y, y, y, 0 }, + [M2] = { y, x, U, U, x, x, U, U, x, x, x, y, 0, 0 }, + [M3] = { y, x, U, U, U, x, x, U, y, x, x, x, y, 0 }, + [S] = { x, U, U, U, x, x, U, y, y, y, x, 0, 0, 0 }, + [S2] = { x, U, U, x, x, U, U, y, y, x, 0, 0, 0, 0 }, + [S3] = { x, U, x, x, U, U, U, y, x, 0, 0, 0, 0, 0 }, + [E] = { U, x, x, U, U, U, x, x, y, y, y, 0, 0, 0 }, + [E2] = { U, U, x, x, U, U, x, x, y, y, 0, 0, 0, 0 }, + [E3] = { U, U, U, x, x, U, x, x, y, 0, 0, 0, 0, 0 }, + + [x] = { x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x2] = { x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [x3] = { x, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y] = { y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y2] = { y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [y3] = { y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z] = { y, y, y, x, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z2] = { y, y, x, x, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + [z3] = { y, x, y, y, y, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/* Local functions implementation ********************************************/ + +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray arr; + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + alloc_cubearray(&arr, f); + cube_to_arrays(cube, &arr, f); + + for (i = 0; i < 12; i++) + if (arr.ep[i] != -1) + used[arr.ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr.ep[i] == -1) + arr.ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(&arr, f); + + return ret; +} + +static CubeArray * +alloc_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + return arr; +} + +static void +append_alg(AlgList *l, Alg alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + + node->alg = new_alg(""); + copy_alg(alg, node->alg); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg alg, NissMove m) +{ + int n = len(alg); + + if (!realloc_alg(alg, n)) + return; + + alg[n] = m; + alg[n+1] = (NissMove) { .m = NULLMOVE }; +} + +static Cube +apply_alg_filtered(Alg alg, Cube cube, PieceFilter f) +{ + Cube ret = {0}; + int i; + + for (i = 0; alg[i].m != NULLMOVE; i++) + if (alg[i].inverse) + ret = apply_move(alg[i].m, ret); + + ret = compose_filtered(cube, inverse_cube(ret), f); + + for (i = 0; alg[i].m != NULLMOVE; i++) + if (!alg[i].inverse) + ret = apply_move(alg[i].m, ret); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + intarrcopy(aux, set, n); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(m_arr, cube, f); +} + +static Cube +arrays_to_cube(CubeArray arr, PieceFilter f) +{ + Cube ret = {0}; + int epose[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eposs[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eposm[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int epe[4], eps[4], epm[4]; + int i, ie, is, im, j; + + /* Again, ep is the hardest part */ + if (f.epose) { + for (i = 0, ie = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (arr.ep[i] == epe_solved[j]) { + epe[ie++] = j; + epose[i] = 1; + } + } + } + ret.epose = epos_from_arrays(epose, epe); + } + + if (f.eposs) { + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (arr.ep[i] == eps_solved[j]) { + eps[is++] = j; + eposs[i] = 1; + } + } + } + for (i = 0; i < 4; i++) + swap(&eposs[eps_solved[i]], &eposs[i+8]); + ret.eposs = epos_from_arrays(eposs, eps); + } + + if (f.eposm) { + for (i = 0, im = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (arr.ep[i] == epm_solved[j]) { + epm[im++] = j; + eposm[i] = 1; + } + } + } + for (i = 0; i < 4; i++) + swap(&eposm[epm_solved[i]], &eposm[i+8]); + ret.eposm = epos_from_arrays(eposm, epm); + } + + if (f.eofb) + ret.eofb = digit_array_to_int(arr.eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr.eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr.eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr.cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr.coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr.corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr.cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr.cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray arr; + Cube ret; + + alloc_cubearray(&arr, f); + cube_to_arrays(c2, &arr, f); + ret = move_via_arrays(arr, c1, f); + free_cubearray(&arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int epose[12], eposs[12], eposm[12]; + int epe[4], eps[4], epm[4]; + int i, ie, is, im; + + /* ep is the hardest */ + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) { + index_to_perm(cube.epose % factorial(4), 4, epe); + index_to_subset(cube.epose / factorial(4), 12, 4, epose); + for (i = 0, ie = 0; i < 12; i++) + if (epose[i]) + arr->ep[i] = epe_solved[epe[ie++]]; + } + + if (f.eposs) { + index_to_perm(cube.eposs % factorial(4), 4, eps); + index_to_subset(cube.eposs / factorial(4), 12, 4, eposs); + for (i = 0; i < 4; i++) + swap(&eposs[eps_solved[i]], &eposs[i+8]); + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + arr->ep[i] = eps_solved[eps[is++]]; + } + + if (f.eposm) { + index_to_perm(cube.eposm % factorial(4), 4, epm); + index_to_subset(cube.eposm / factorial(4), 12, 4, eposm); + for (i = 0; i < 4; i++) + swap(&eposm[epm_solved[i]], &eposm[i+8]); + for (i = 0, im = 0; i < 12; i++) + if (eposm[i]) + arr->ep[i] = epm_solved[epm[im++]]; + } + + /* All the others */ + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static uint16_t +epos_from_arrays(int *epos, int *ep) +{ + return factorial4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +static void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free(i); + i = aux; + } + free(l); + + return; +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +init_auxtables() +{ + Cube c1, c2; + int i, j, k; + bool cij, p1, p2; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2); + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && (j-(j-1)%3) == (k-(k-1)%3); + p2 = i && (i-(i-1)%3) == (k-(k-1)%3); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = + i == NULLMOVE ? NULLMOVE : i + 2 - 2*((i-1)%3); + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[mirror] = mirror; +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i, j; + uint16_t ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = (Cube){0}; + for (j = 0; equiv_moves[i][j]; j++) + c = apply_move_cubearray(equiv_moves[i][j], c, pf_all); + + arrs = (CubeArray){ + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "ttables not found, generating them.\n"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < factorial12/factorial8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_ttable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_ttable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_ttable[m][ui] = c.eposm; + } + for (ui = 0; ui < pow2to11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_ttable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_ttable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_ttable[m][ui] = c.eoud; + } + for (ui = 0; ui < pow3to7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_ttable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_ttable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_ttable[m][ui] = c.cofb; + } + for (ui = 0; ui < factorial8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_ttable[m][ui] = c.cp; + } + for (ui = 0; ui < factorial6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_ttable[m][ui] = c.cpos; + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans() { + Cube aux, cube, c[3]; + CubeArray epcp; + int eparr[12], eoarr[12]; + int cparr[8], coarr[8]; + int i; + bool b1, b2, b3; + uint16_t ui; + Move mi, move; + Trans r, m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + if (i == mirror) + cube = (Cube){0}; + else + cube = apply_alg(rotation_algs[i], (Cube){0}); + + epose_source[i] = edge_slice(edge_at(cube, FR)); + eposs_source[i] = edge_slice(edge_at(cube, UR)); + eposm_source[i] = edge_slice(edge_at(cube, UF)); + eofb_source[i] = center_at(cube, F_center)/2; + eorl_source[i] = center_at(cube, R_center)/2; + eoud_source[i] = center_at(cube, U_center)/2; + coud_source[i] = center_at(cube, U_center)/2; + cofb_source[i] = center_at(cube, F_center)/2; + corl_source[i] = center_at(cube, R_center)/2; + } + + /* Compute rotation_niss array, necessary for rotate_via_compose */ + for (r = 0; r != mirror; r++) { + concat(rotation_algs[r], rotation_algs[r], rotation_niss[r]); + for (i = len(rotation_algs[r]); rotation_niss[r][i].m; i++) + rotation_niss[r][i].inverse = true; + } + + if (read_rtables_file()) + return; + + fprintf(stderr, "rtables not found, generating them.\n"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + if (m == mirror) { + intarrcopy(ep_mirror, eparr, 12); + intarrcopy(cp_mirror, cparr, 8); + } else { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_algs[m], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + } + + for (ui = 0; ui < factorial12/factorial8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_rtable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_rtable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_rtable[m][ui] = cube.eposm; + } + for (ui = 0; ui < pow2to11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_rtable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < pow3to7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_rtable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m == mirror) + co_rtable[m][ui] = + invert_digits(co_rtable[m][ui], 3, 7); + } + for (ui = 0; ui < factorial8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_rtable[m][ui] = cube.cp; + } + for (ui = 0; ui < factorial6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_rtable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + if (m == mirror) { + b1 = (mi >= U && mi <= Bw3); + b2 = (mi >= S && mi <= E3); + b3 = (mi >= x && mi <= z3); + if (b1 || b2 || b3) + moves_rtable[m][mi] = + inverse_move_aux[mi]; + else + moves_rtable[m][mi] = mi; + + if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3) + moves_rtable[m][mi] += 3; + if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3) + moves_rtable[m][mi] -= 3; + } else { + aux = apply_trans(m, apply_move(mi,(Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move( + inverse_move_aux[move], aux); + if (is_solved(cube, false)) + moves_rtable[m][mi] = move; + } + } + } + } + + if (!write_rtables_file()) + fprintf(stderr, "Error writing rtables\n"); +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static void +intarrcopy(int *src, int *dst, int n) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = src[i]; +} + +static int +invert_digits(int a, int b, int n) +{ + int i, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + return digit_array_to_int(r, n, b); +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray arr, Cube c, PieceFilter f) +{ + CubeArray arrc; + Cube ret; + + alloc_cubearray(&arrc, f); + cube_to_arrays(c, &arrc, f); + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr.ep, arrc.ep, 12); + + if (f.eofb) { + apply_permutation(arr.ep, arrc.eofb, 12); + sum_arrays_mod(arr.eofb, arrc.eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr.ep, arrc.eorl, 12); + sum_arrays_mod(arr.eorl, arrc.eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr.ep, arrc.eoud, 12); + sum_arrays_mod(arr.eoud, arrc.eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr.cp, arrc.cp, 8); + + if (f.coud) { + apply_permutation(arr.cp, arrc.coud, 8); + sum_arrays_mod(arr.coud, arrc.coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr.cp, arrc.corl, 8); + sum_arrays_mod(arr.corl, arrc.corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr.cp, arrc.cofb, 8); + sum_arrays_mod(arr.cofb, arrc.cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr.cpos, arrc.cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(&arrc, f); + + return ret; +} + +static Move * +moveset_to_movelist(bool *moveset, Step step) +{ + Cube c; + int *a = malloc(NMOVES * sizeof(int)), b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (moveset != NULL) { + for (i = NULLMOVE+1; i < NMOVES; i++) { + if (moveset[i]) { + c = apply_move(i, (Cube){0}); + if (step.f != NULL && step.f(c)) { + a[na++] = i; + } else { + b[nb++] = i; + } + } + } + } + + intarrcopy(b, a+na, nb); + a[na+nb] = NULLMOVE; + + return (Move *)a; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; /* Truncate */ + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static bool +read_rtables_file() +{ + FILE *ttf; + int b = sizeof(uint16_t); + bool r = true; + Move m; + + if ((ttf = fopen("rtables", "rb")) != NULL) { + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_rtable[m],b,me[0],ttf) == me[0]; + r = r && fread(eposs_rtable[m],b,me[1],ttf) == me[1]; + r = r && fread(eposm_rtable[m],b,me[2],ttf) == me[2]; + r = r && fread(eo_rtable[m],b,me[3],ttf) == me[3]; + r = r && fread(cp_rtable[m],b,me[6],ttf) == me[6]; + r = r && fread(co_rtable[m],b,me[7],ttf) == me[7]; + r = r && fread(cpos_rtable[m],b,me[10],ttf) == me[10]; + r = r && fread(moves_rtable[m],b,me[11],ttf) == me[11]; + } + fclose(ttf); + return r; + } else { + return false; + } +} + +static bool +read_ttables_file() +{ + FILE *ttf; + int m, b = sizeof(uint16_t); + bool r = true; + + if ((ttf = fopen("ttables", "rb")) != NULL) { + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_ttable[m],b,me[0],ttf) == me[0]; + r = r && fread(eposs_ttable[m],b,me[1],ttf) == me[1]; + r = r && fread(eposm_ttable[m],b,me[2],ttf) == me[2]; + r = r && fread(eofb_ttable[m],b,me[3],ttf) == me[3]; + r = r && fread(eorl_ttable[m],b,me[4],ttf) == me[4]; + r = r && fread(eoud_ttable[m],b,me[5],ttf) == me[5]; + r = r && fread(cp_ttable[m],b,me[6],ttf) == me[6]; + r = r && fread(coud_ttable[m],b,me[7],ttf) == me[7]; + r = r && fread(corl_ttable[m],b,me[8],ttf) == me[8]; + r = r && fread(cofb_ttable[m],b,me[9],ttf) == me[9]; + r = r && fread(cpos_ttable[m],b,me[10],ttf) == me[10]; + } + fclose(ttf); + return r; + } else { + return false; + } +} + +static bool +realloc_alg(Alg alg, int n) +{ + if (n+1 >= BASEALGLEN) { + if (reallocarray(alg, n+2, sizeof(NissMove)) == NULL) { + fprintf(stderr, "Error reallocating alg\n"); + return false; + } + } + + return true; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + Cube ret; + CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + if (r != mirror) { + ret = apply_alg_filtered(rotation_niss[r], c, f); + } else { + ret = move_via_arrays(ma, (Cube){0}, f); + ret = compose_filtered(c, ret, f); + ret = move_via_arrays(ma, ret, f); + } + + return ret; +} + +static void +solve_dfs(Cube c, Step s, SolveOptions opts, DfsData dd) +{ + DfsData newdd; + Move move; + NissMove nm; + int i; + bool found_many, prune, niss_makes_sense; + + found_many = dd.sols->len >= opts.max_solutions; + prune = (!opts.can_niss || dd.niss) && dd.m + s.f(c) > dd.d; + if (found_many || prune) + return; + + if (!s.f(c)) { + if (dd.m == dd.d) + append_alg(dd.sols, dd.current_alg); + return; + } + + for (i = 0; opts.sorted_moves[i] != NULLMOVE; i++) { + move = opts.sorted_moves[i]; + if (possible_next[dd.last2][dd.last1][move]) { + nm = (NissMove){ .m = move, .inverse = dd.niss }; + newdd = (DfsData) { + .d = dd.d, + .m = dd.m + 1, + .niss = dd.niss, + .last1 = move, + .last2 = dd.last1, + .sols = dd.sols, + .current_alg = dd.current_alg, + }; + + append_move(dd.current_alg, nm); + solve_dfs(apply_move(move, c), s, opts, newdd); + remove_last_moves(dd.current_alg, 1); + } + } + + niss_makes_sense = !dd.m || (s.f(apply_move(dd.last1, (Cube){0}))); + if (opts.can_niss && !dd.niss && niss_makes_sense) { + newdd = (DfsData) { + .d = dd.d, + .m = dd.m, + .niss = true, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = dd.sols, + .current_alg = dd.current_alg, + }; + solve_dfs(inverse_cube(c), s, opts, newdd); + } +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_rtables_file() +{ + FILE *ttf; + bool r = true; + int b = sizeof(uint16_t); + Move m; + + if ((ttf = fopen("rtables", "wb")) != NULL) { + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_rtable[m],b,me[0], ttf) == me[0]; + r = r && fwrite(eposs_rtable[m],b,me[1], ttf) == me[1]; + r = r && fwrite(eposm_rtable[m],b,me[2], ttf) == me[2]; + r = r && fwrite(eo_rtable[m],b,me[3], ttf) == me[3]; + r = r && fwrite(cp_rtable[m],b,me[6], ttf) == me[6]; + r = r && fwrite(co_rtable[m],b,me[7], ttf) == me[7]; + r = r && fwrite(cpos_rtable[m],b,me[10],ttf) == me[10]; + r = r && fwrite(moves_rtable[m],b,me[11],ttf)== me[11]; + } + fclose(ttf); + return r; + } else { + return false; + } +} + +static bool +write_ttables_file() +{ + FILE *ttf; + int m, b = sizeof(uint16_t);; + bool r = true; + + if ((ttf = fopen("ttables", "wb")) != NULL) { + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_ttable[m],b,me[0],ttf) == me[0]; + r = r && fwrite(eposs_ttable[m],b,me[1],ttf) == me[1]; + r = r && fwrite(eposm_ttable[m],b,me[2],ttf) == me[2]; + r = r && fwrite(eofb_ttable[m],b,me[3],ttf) == me[3]; + r = r && fwrite(eorl_ttable[m],b,me[4],ttf) == me[4]; + r = r && fwrite(eoud_ttable[m],b,me[5],ttf) == me[5]; + r = r && fwrite(cp_ttable[m],b,me[6],ttf) == me[6]; + r = r && fwrite(coud_ttable[m],b,me[7],ttf) == me[7]; + r = r && fwrite(corl_ttable[m],b,me[8],ttf) == me[8]; + r = r && fwrite(cofb_ttable[m],b,me[9],ttf) == me[9]; + r = r && fwrite(cpos_ttable[m],b,me[10],ttf) == me[10]; + } + fclose(ttf); + return r; + } else { + return false; + } +} + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg alg, Cube cube) +{ + return apply_alg_filtered(alg, cube, pf_all); +} + +Cube +apply_move(Move m, Cube cube) +{ + Cube moved = {0}; + + moved.epose = epose_ttable[m][cube.epose]; + moved.eposs = eposs_ttable[m][cube.eposs]; + moved.eposm = eposm_ttable[m][cube.eposm]; + moved.eofb = eofb_ttable[m][cube.eofb]; + moved.eorl = eorl_ttable[m][cube.eorl]; + moved.eoud = eoud_ttable[m][cube.eoud]; + moved.coud = coud_ttable[m][cube.coud]; + moved.cofb = cofb_ttable[m][cube.cofb]; + moved.corl = corl_ttable[m][cube.corl]; + moved.cp = cp_ttable[m][cube.cp]; + moved.cpos = cpos_ttable[m][cube.cpos]; + + return moved; +} + +Cube +apply_trans(Trans t, Cube cube) +{ + Cube transformed = {0}; + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + transformed.epose = epose_rtable[t][aux_epos[epose_source[t]]]; + transformed.eposs = eposs_rtable[t][aux_epos[eposs_source[t]]]; + transformed.eposm = eposm_rtable[t][aux_epos[eposm_source[t]]]; + transformed.eofb = eo_rtable[t][aux_eo[eofb_source[t]]]; + transformed.eorl = eo_rtable[t][aux_eo[eorl_source[t]]]; + transformed.eoud = eo_rtable[t][aux_eo[eoud_source[t]]]; + transformed.coud = co_rtable[t][aux_co[coud_source[t]]]; + transformed.corl = co_rtable[t][aux_co[corl_source[t]]]; + transformed.cofb = co_rtable[t][aux_co[cofb_source[t]]]; + transformed.cp = cp_rtable[t][cube.cp]; + transformed.cpos = cpos_rtable[t][cube.cpos]; + + return transformed; +} + +/* TODO: this has to be changed using pre-computations */ +bool +block_solved(Cube cube, Block block) +{ + CubeArray arr; + int i; + bool ret = true; + + alloc_cubearray(&arr, pf_all); + cube_to_arrays(cube, &arr, pf_all); + + for (i = 0; i < 12; i++) + ret = ret && !(block.edge[i] && (arr.ep[i] != i || arr.eofb[i])); + for (i = 0; i < 8; i++) + ret = ret && !(block.corner[i] && (arr.cp[i] != i || arr.coud[i])); + for (i = 0; i < 6; i++) + ret = ret && !(block.center[i] && arr.cpos[i] != i); + + free_cubearray(&arr, pf_all); + + return ret; +} + +Center +center_at(Cube cube, Center c) +{ + int ret; + CubeArray arr; + + alloc_cubearray(&arr, pf_cpos); + cube_to_arrays(cube, &arr, pf_cpos); + ret = arr.cpos[c]; + free_cubearray(&arr, pf_cpos); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +Corner +corner_at(Cube cube, Corner c) +{ + int ret; + CubeArray arr; + + alloc_cubearray(&arr, pf_cp); + cube_to_arrays(cube, &arr, pf_cp); + ret = arr.cp[c]; + free_cubearray(&arr, pf_cp); + + return ret; +} + +Edge +edge_at(Cube cube, Edge e) +{ + int ret; + CubeArray arr; + + alloc_cubearray(&arr, pf_ep); + cube_to_arrays(cube, &arr, pf_ep); + ret = arr.ep[e]; + free_cubearray(&arr, pf_ep); + + return ret; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray arr, inv; + Cube ret; + int i; + + alloc_cubearray(&arr, pf_all); + alloc_cubearray(&inv, pf_all); + + cube_to_arrays(cube, &arr, pf_all); + + for (i = 0; i < 12; i++) { + inv.ep[arr.ep[i]] = i; + inv.eofb[arr.ep[i]] = arr.eofb[i]; + inv.eorl[arr.ep[i]] = arr.eorl[i]; + inv.eoud[arr.ep[i]] = arr.eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv.cp[arr.cp[i]] = i; + inv.coud[arr.cp[i]] = (3 - arr.coud[i]) % 3; + inv.corl[arr.cp[i]] = (3 - arr.corl[i]) % 3; + inv.cofb[arr.cp[i]] = (3 - arr.cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv.cpos[arr.cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(&arr, pf_all); + free_cubearray(&inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_solved(Cube cube, bool reorient) +{ + int i; + + if (reorient) + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube), false)) + return true; + + return equal(cube, (Cube){0}); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b; + uint16_t x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + CubeArray arr; + + alloc_cubearray(&arr, pf_all); + cube_to_arrays(cube, &arr, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr.ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr.eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr.cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr.coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr.cpos[i]]); + printf("\n"); + + free_cubearray(&arr, pf_all); +} + +void +concat(Alg src1, Alg src2, Alg dest) +{ + int n1 = len(src1), n2 = len(src2); + + if (!realloc_alg(dest, n1+n2)) + return; + + copy_alg(src1, dest); + copy_alg(src2, dest + n1); +} + +void +copy_alg(Alg src, Alg dest) +{ + int i, n = len(src); + + if (!realloc_alg(dest, n)) + return; + + for (i = 0; src[i].m != NULLMOVE; i++) + dest[i] = src[i]; + dest[i].m = NULLMOVE; +} + +void +invert_alg(Alg src, Alg dest) +{ + Move m; + int i, n = len(src); + + if (!realloc_alg(dest, n)) + return; + + for (i = 0; i < n; i++) { + m = inverse_move_aux[src[i].m]; + dest[n-i-1] = (NissMove) { .m = m, .inverse = src[i].inverse }; + } + dest[n].m = NULLMOVE; +} + +int +len(Alg alg) +{ + int i; + + for (i = 0; alg[i].m != NULLMOVE; i++); + + return i; +} + +Alg +new_alg(char *str) +{ + Alg alg = malloc(BASEALGLEN * sizeof(NissMove)); + int i; + bool niss = false; + Move j, m; + NissMove nm; + + alg[0] = (NissMove){ .m = NULLMOVE }; + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + nm = (NissMove){ .m = m, .inverse = niss }; + append_move(alg, nm); + break; + } + } + } + append_move(alg, (NissMove){ .m = NULLMOVE }); + + return alg; +} + +void +print_alg(Alg alg) +{ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; alg[i].m != NULLMOVE; i++) { + if (!niss && alg[i].inverse) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg[i].inverse) + strcpy(fill, ") "); + if (niss == alg[i].inverse) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg[i].m]); + niss = alg[i].inverse; + } + printf("%s\n", niss ? ")" : ""); +} + +void +remove_last_moves(Alg alg, int k) +{ + int n = len(alg); + int i = max(0, n-k); + + alg[i].m = NULLMOVE; +} + +AlgList * +solve(Cube cube, Step step, SolveOptions opts) +{ + AlgList *sols = new_alglist(); + AlgListNode *node; + Cube c; + DfsData dd; + int i; + + if (step.ready != NULL && !step.ready(cube)) + return sols; + + if (opts.sorted_moves == NULL) + opts.sorted_moves = moveset_to_movelist(opts.moveset, step); + + for (i = opts.min_moves; i <= opts.max_moves; i++) { + if (sols->len && opts.optimal_only) + break; + + c = apply_trans(opts.pre_trans, cube); + dd = (DfsData) { + .d = i, + .m = 0, + .niss = false, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + solve_dfs(c, step, opts, dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans_aux[opts.pre_trans], node->alg); + + return sols; +} + +void +transform_alg(Trans t, Alg alg) +{ + int i; + + for (i = 0; alg[i].m != NULLMOVE; i++) + alg[i].m = moves_rtable[t][alg[i].m]; +} + + +void +init() +{ + init_moves(); + init_auxtables(); + init_trans(); +} diff --git a/old/2021-06-02-cleanedup/cube.h b/old/2021-06-02-cleanedup/cube.h @@ -0,0 +1,179 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +/* Constants ****************************************************************/ + +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) + +/* Typedefs *****************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct nissmove * Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct cube Cube; +typedef struct nissmove NissMove; +typedef struct solveoptions SolveOptions; +typedef struct step Step; + +/* Type specifications *******************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, /* R|L */ +}; + +struct +alglist +{ + AlgListNode *first; + AlgListNode *last; + int len; +}; + +struct +alglistnode +{ + Alg alg; + AlgListNode *next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +cube +{ + uint16_t epose; + uint16_t eposs; + uint16_t eposm; + uint16_t eofb; + uint16_t eorl; + uint16_t eoud; + uint16_t cp; + uint16_t coud; + uint16_t cofb; + uint16_t corl; + uint16_t cpos; +}; + +struct +nissmove +{ + Move m; + bool inverse; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool *moveset; + Move *sorted_moves; + Trans pre_trans; +}; + +struct +step +{ + int (*f)(Cube); + bool (*ready)(Cube); +}; + +/* Public functions **********************************************************/ + +Cube apply_alg(Alg alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Center center_at(Cube cube, Center c); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Corner corner_at(Cube cube, Corner c); +Edge edge_at(Cube cube, Edge e); +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_solved(Cube cube, bool reorient); +int piece_orientation(Cube cube, int piece, char *orientation); +void print_cube(Cube cube); +AlgList * solve(Cube cube, Step step, SolveOptions opts); + +void concat(Alg src1, Alg src2, Alg dest); +void copy_alg(Alg src, Alg dest); +void invert_alg(Alg src, Alg dest); +int len(Alg alg); +Alg new_alg(char *str); +void print_alg(Alg alg); +void remove_last_moves(Alg alg, int k); +void transform_alg(Trans t, Alg alg); + +void init(); diff --git a/old/2021-06-02-cleanedup/main.c b/old/2021-06-02-cleanedup/main.c @@ -0,0 +1,35 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + + init(); + + algo = new_alg("R U R' F"); + cube = apply_alg(algo, (Cube){0}); + + print_cube(apply_trans(rd, cube)); + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 5, + .optimal_only = true, + .max_solutions = 3, + .can_niss = false, + .moveset = standard_moveset, + .pre_trans = fu + }; + sols = solve(cube, step_eofb, opts); + if (sols->len == 0) + fprintf(stderr, "No solution found\n"); + else + print_alg(sols->first->alg); + + + return 0; +} diff --git a/old/2021-06-02-cleanedup/steps.h b/old/2021-06-02-cleanedup/steps.h @@ -0,0 +1,22 @@ +/* Pre-conditions, i.e. "ready(cube)" functions ******************************/ + +bool always_ready(Cube cube) { return true; } + +/* Main functions, i.e. "f(cube)" ********************************************/ + +int f_eofb(Cube cube) { return (cube.eofb != 0) ? 1 : 0; } + +/* Steps *********************************************************************/ + +Step step_eofb = { .f = f_eofb, .ready = always_ready }; + +/* Movesets ******************************************************************/ + +bool standard_moveset[NMOVES] = { + [U] = true, [U2] = true, [U3] = true, + [D] = true, [D2] = true, [D3] = true, + [R] = true, [R2] = true, [R3] = true, + [L] = true, [L2] = true, [L3] = true, + [F] = true, [F2] = true, [F3] = true, + [B] = true, [B2] = true, [B3] = true, +}; diff --git a/old/2021-06-14-oldalg/cube.c b/old/2021-06-14-oldalg/cube.c @@ -0,0 +1,2849 @@ +#include "cube.h" + +/* Constants and macros *****************************************************/ + +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define BASEALGLEN 50 +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) +#define PTABLESIZE(n) ((n+1) / 2) +#define PTABLEVAL(tab,ind) (((ind)%2) ? (tab[(ind)/2] / 16) : \ + (tab[(ind)/2] % 16)) + + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static uint16_t array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static Move base_move(Move m); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent(int pos1, int pos2); +static uint16_t epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void generate_ptable(PruneData *pd); +static void generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void moveset_to_list(bool (*ms)(Move), int (*f)(Cube), Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static void ptable_set_reached(PruneData *pd, uint64_t ind); +static void ptable_update(PruneData *pd, uint64_t ind, int m); +static bool read_ptable_file(PruneData *pd); +static bool read_ttables_file(); +static bool read_mtables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static void solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_ptable_file(PruneData *pd); +static bool write_ttables_file(); +static bool write_mtables_file(); + +static void init_auxtables(); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_steps(); +static void init_strings(); +static void init_trans(); +static void init_trans_aux(); + +static bool moveset_HTM(Move m); +static bool moveset_URF(Move m); + +/* Steps and related functions and data **************************************/ + +Step eofb_HTM; +Step eorl_HTM; +Step eoud_HTM; +Step coud_HTM; +Step corl_HTM; +Step cofb_HTM; +Step coud_URF; +Step corl_URF; +Step cofb_URF; +Step corners_HTM; +Step corners_URF; +Step edges_HTM; +Step drud_HTM; +Step optimal_HTM; + +PruneData pd_eofb_HTM; +PruneData pd_coud_HTM; +PruneData pd_corners_HTM; +PruneData pd_ep_HTM; +PruneData pd_drud_HTM; + +static uint64_t index_eofb(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_ep(Cube cube); +static uint64_t index_drud(Cube cube); + +static int check_nothing(Cube cube); +static int check_eofb_HTM(Cube cube); +static int check_coud_HTM(Cube cube); +static int check_coud_URF(Cube cube); +static int check_corners_HTM(Cube cube); +static int check_corners_URF(Cube cube); +static int check_edges_HTM(Cube cube); +static int check_drud_HTM(Cube cube); +static int check_optimal_HTM(Cube cube); + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; + +static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eo_ttable[NTRANS][POW2TO11]; +static uint16_t cp_ttable[NTRANS][FACTORIAL8]; +static uint16_t co_ttable[NTRANS][POW3TO7]; +static uint16_t cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eofb_mtable[NMOVES][POW2TO11]; +static uint16_t eorl_mtable[NMOVES][POW2TO11]; +static uint16_t eoud_mtable[NMOVES][POW2TO11]; +static uint16_t cp_mtable[NMOVES][FACTORIAL8]; +static uint16_t coud_mtable[NMOVES][POW3TO7]; +static uint16_t cofb_mtable[NMOVES][POW3TO7]; +static uint16_t corl_mtable[NMOVES][POW3TO7]; +static uint16_t cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * trans_algs[NROTATIONS]; + + +/* Local functions implementation ********************************************/ + +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + AlgNode *i; + + node->alg = new_alg(""); + for (i = alg->first; i != NULL; i = i->next) + append_move(node->alg, i->m, i->inverse); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + AlgNode *node = malloc(sizeof(AlgNode)); + + node->m = m; + node->inverse = inverse; + node->next = NULL; + node->prev = alg->last; + + if (++alg->len == 1) + alg->first = node; + else + alg->last->next = node; + alg->last = node; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + AlgNode *i; + + for (i = alg->first; i != NULL; i = i->next) + if (i->inverse) + ret = a ? apply_move(i->m, ret) : + apply_move_cubearray(i->m, ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = alg->first; i != NULL; i = i->next) + if (!i->inverse) + ret = a ? apply_move(i->m, ret) : + apply_move_cubearray(i->m, ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static uint16_t +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 1) + ep8[j++] = (edge_slice(ep[i]) == 0) ? 1 : 0; + + return subset_to_index(ep8, 8, 4); +} + +static uint16_t +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(uint16_t epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + AlgNode *aux, *i = alg->first; + + while (i != NULL) { + aux = i->next; + free(i); + i = aux; + } + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +generate_ptable(PruneData *pd) +{ + uint64_t j; + DfsData dd; + + if (pd->generated) + return; + + pd->ptable = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + dd.m = 0; + dd.last1 = NULLMOVE; + dd.last2 = NULLMOVE; + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->size; j++) + ptable_update(pd, j, 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + + pd->reached = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t)); + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) { + memset(pd->reached, 0, PTABLESIZE(pd->size)*sizeof(uint8_t)); + generate_ptable_dfs((Cube){0}, pd, &dd); + fprintf(stderr, "Depth %d completed, generated %lu/%lu\n", + dd.d, pd->n, pd->size); + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; + free(pd->reached); +} + +static void +generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + uint64_t ind = pd->index(c); + int oldval = PTABLEVAL(pd->ptable, ind); + Move i, move, l1 = dd->last1, l2 = dd->last2; + + if (oldval < dd->m || PTABLEVAL(pd->reached, ind) || pd->n == pd->size) + return; + + ptable_set_reached(pd, ind); + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, ind, dd->m); + return; + } + + dd->m++; + dd->last2 = dd->last1; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (possible_next[l2][l1][move]) { + dd->last1 = move; + generate_ptable_dfs(apply_move(move, c), pd, dd); + } + } + + dd->m--; + dd->last1 = l1; + dd->last2 = l2; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +moveset_to_list(bool (*ms)(Move m), int (*f)(Cube), Move *r) +{ + Cube c; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + c = apply_move(i, (Cube){0}); + if (f != NULL && f(c)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; /* Truncate */ + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static void +ptable_set_reached(PruneData *pd, uint64_t ind) +{ + uint8_t oldval2 = pd->reached[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1; +} + +static void +ptable_update(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n; + pd->n++; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f); + fclose(f); + + return r == PTABLESIZE(pd->size); +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(uint16_t); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + Alg *inv; + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Cube ret; + + if (r != mirror) { + ret = apply_alg_generic(trans_algs[r], c, f, true); + inv = on_inverse(trans_algs[r]); + ret = apply_alg_generic(inv, ret, f, true); + free_alg(inv); + } else { + ret = move_via_arrays(&ma, (Cube){0}, f); + ret = compose_filtered(c, ret, f); + ret = move_via_arrays(&ma, ret, f); + } + + return ret; +} + +static void +solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move move, l1 = dd->last1, l2 = dd->last2; + int i, lower_bound = s.check(c); + bool found_many, prune, niss_make_sense, nissbackup = dd->niss; + + if (opts->can_niss && !dd->niss) + lower_bound = MIN(1, lower_bound); + + found_many = dd->sols->len >= opts->max_solutions; + prune = dd->current_alg->len + lower_bound > dd->d; + if (found_many || prune) + return; + + if (!lower_bound) { /* solved */ + if (dd->current_alg->len == dd->d) + append_alg(dd->sols, dd->current_alg); + return; + } + + dd->last2 = dd->last1; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (possible_next[l2][l1][move]) { + dd->last1 = move; + append_move(dd->current_alg, move, dd->niss); + solve_dfs(apply_move(move, c), s, opts, dd); + remove_last_move(dd->current_alg); + } + } + + niss_make_sense = !dd->current_alg->len || + (s.check(apply_move(l1,(Cube){0}))); + if (opts->can_niss && !dd->niss && niss_make_sense) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + solve_dfs(inverse_cube(c), s, opts, dd); + } + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = nissbackup; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f); + fclose(f); + + return written == PTABLESIZE(pd->size); +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(uint16_t); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + uint64_t ui, uj; + int i, j, k; + bool cij, p1, p2; + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj] = epos_dependent(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2); + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[mirror] = mirror; +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + uint16_t ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray){ + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_steps() +{ + /* PruneData */ + pd_eofb_HTM.filename = "ptable_eofb_HTM"; + pd_eofb_HTM.size = POW2TO11; + pd_eofb_HTM.index = index_eofb; + pd_eofb_HTM.moveset = moveset_HTM; + + pd_coud_HTM.filename = "ptable_coud_HTM"; + pd_coud_HTM.size = POW3TO7; + pd_coud_HTM.index = index_coud; + pd_coud_HTM.moveset = moveset_HTM; + + pd_corners_HTM.filename = "ptable_corners_HTM"; + pd_corners_HTM.size = POW3TO7 * FACTORIAL8; + pd_corners_HTM.index = index_corners; + pd_corners_HTM.moveset = moveset_HTM; + + pd_ep_HTM.filename = "ptable_ep_HTM"; + pd_ep_HTM.size = FACTORIAL12; + pd_ep_HTM.index = index_ep; + pd_ep_HTM.moveset = moveset_HTM; + + pd_drud_HTM.filename = "ptable_drud_HTM"; + pd_drud_HTM.size = POW2TO11 * POW3TO7 * BINOM12ON4; + pd_drud_HTM.index = index_drud; + pd_drud_HTM.moveset = moveset_HTM; + + + /* Actual steps */ + eofb_HTM.check = check_eofb_HTM; + eofb_HTM.ready = check_nothing; + eofb_HTM.pre_trans = uf; + eofb_HTM.moveset = moveset_HTM; + + eorl_HTM.check = check_eofb_HTM; + eorl_HTM.ready = check_nothing; + eorl_HTM.pre_trans = ur; + eorl_HTM.moveset = moveset_HTM; + + eoud_HTM.check = check_eofb_HTM; + eoud_HTM.ready = check_nothing; + eoud_HTM.pre_trans = bu; + eoud_HTM.moveset = moveset_HTM; + + + coud_HTM.check = check_coud_HTM; + coud_HTM.ready = check_nothing; + coud_HTM.pre_trans = uf; + coud_HTM.moveset = moveset_HTM; + + corl_HTM.check = check_coud_HTM; + corl_HTM.ready = check_nothing; + corl_HTM.pre_trans = rf; + corl_HTM.moveset = moveset_HTM; + + cofb_HTM.check = check_coud_HTM; + cofb_HTM.ready = check_nothing; + cofb_HTM.pre_trans = fd; + cofb_HTM.moveset = moveset_HTM; + + coud_URF.check = check_coud_URF; + coud_URF.ready = check_nothing; + coud_URF.pre_trans = uf; + coud_URF.moveset = moveset_URF; + + corl_URF.check = check_coud_URF; + corl_URF.ready = check_nothing; + corl_URF.pre_trans = rf; + corl_URF.moveset = moveset_URF; + + cofb_URF.check = check_coud_URF; + cofb_URF.ready = check_nothing; + cofb_URF.pre_trans = fd; + cofb_URF.moveset = moveset_URF; + + corners_HTM.check = check_corners_HTM; + corners_HTM.ready = check_nothing; + corners_HTM.pre_trans = uf; + corners_HTM.moveset = moveset_HTM; + + corners_URF.check = check_corners_URF; + corners_URF.ready = check_nothing; + corners_URF.pre_trans = uf; + corners_URF.moveset = moveset_URF; + + edges_HTM.check = check_edges_HTM; + edges_HTM.ready = check_nothing; + edges_HTM.pre_trans = uf; + edges_HTM.moveset = moveset_HTM; + + drud_HTM.check = check_drud_HTM; + drud_HTM.ready = check_nothing; + drud_HTM.pre_trans = uf; + drud_HTM.moveset = moveset_HTM; + + optimal_HTM.check = check_optimal_HTM; + optimal_HTM.ready = check_nothing; + optimal_HTM.pre_trans = uf; + optimal_HTM.moveset = moveset_HTM; +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_trans() { + Cube aux, cube, c[3]; + CubeArray epcp; + int eparr[12], eoarr[12]; + int cparr[8], coarr[8]; + int i; + bool b1, b2, b3; + uint16_t ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + if (i == mirror) + cube = (Cube){0}; + else + cube = apply_alg(trans_algs[i], (Cube){0}); + + epose_source[i] = edge_slice(edge_at(cube, FR)); + eposs_source[i] = edge_slice(edge_at(cube, UR)); + eposm_source[i] = edge_slice(edge_at(cube, UF)); + eofb_source[i] = center_at(cube, F_center)/2; + eorl_source[i] = center_at(cube, R_center)/2; + eoud_source[i] = center_at(cube, U_center)/2; + coud_source[i] = center_at(cube, U_center)/2; + cofb_source[i] = center_at(cube, F_center)/2; + corl_source[i] = center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + if (m == mirror) { + memcpy(eparr, ep_mirror, 12 * sizeof(int)); + memcpy(cparr, cp_mirror, 8 * sizeof(int)); + } else { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(trans_algs[m], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m == mirror) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + if (m == mirror) { + b1 = (mi >= U && mi <= Bw3); + b2 = (mi >= S && mi <= E3); + b3 = (mi >= x && mi <= z3); + if (b1 || b2 || b3) + moves_ttable[m][mi] = + inverse_move_aux[mi]; + else + moves_ttable[m][mi] = mi; + + if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3) + moves_ttable[m][mi] += 3; + if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3) + moves_ttable[m][mi] -= 3; + } else { + aux = apply_trans(m, apply_move(mi,(Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move( + inverse_move_aux[move], aux); + if (is_solved(cube, false)) + moves_ttable[m][mi] = move; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + trans_algs[uf] = new_alg(""); + trans_algs[ur] = new_alg("y"); + trans_algs[ub] = new_alg("y2"); + trans_algs[ul] = new_alg("y3"); + + trans_algs[df] = new_alg("z2"); + trans_algs[dr] = new_alg("y z2"); + trans_algs[db] = new_alg("x2"); + trans_algs[dl] = new_alg("y3 z2"); + + trans_algs[rf] = new_alg("z3"); + trans_algs[rd] = new_alg("z3 y"); + trans_algs[rb] = new_alg("z3 y2"); + trans_algs[ru] = new_alg("z3 y3"); + + trans_algs[lf] = new_alg("z"); + trans_algs[ld] = new_alg("z y3"); + trans_algs[lb] = new_alg("z y2"); + trans_algs[lu] = new_alg("z y"); + + trans_algs[fu] = new_alg("x y2"); + trans_algs[fr] = new_alg("x y"); + trans_algs[fd] = new_alg("x"); + trans_algs[fl] = new_alg("x y3"); + + trans_algs[bu] = new_alg("x3"); + trans_algs[br] = new_alg("x3 y"); + trans_algs[bd] = new_alg("x3 y2"); + trans_algs[bl] = new_alg("x3 y3"); +} + +/* Linearization functions implementation ************************************/ + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_ep(Cube cube) +{ + uint64_t a, b, c; + + /* TODO: remove this check */ + if (epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] ==BINOM8ON4) + fprintf(stderr, "Impossible\n"); + + a = cube.eposs; + b = (cube.epose % FACTORIAL4) + + epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] * + FACTORIAL4; + c = cube.eposm % FACTORIAL4; + + b *= FACTORIAL4 * BINOM12ON4; + c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4; + + return a + b + c; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +/* Step checking functions implementation ************************************/ + +static int +check_nothing(Cube cube) +{ + return true; +} + +static int +check_eofb_HTM(Cube cube) +{ + if (!pd_eofb_HTM.generated) + generate_ptable(&pd_eofb_HTM); + + return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb); +} + +static int +check_coud_HTM(Cube cube) +{ + if (!pd_coud_HTM.generated) + generate_ptable(&pd_coud_HTM); + + return PTABLEVAL(pd_coud_HTM.ptable, cube.coud); +} + +static int +check_coud_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + int ud = check_coud_HTM(cube); + int rl = check_coud_HTM(apply_move(z, cube)); + int fb = check_coud_HTM(apply_move(x, cube)); + + return MIN(ud, MIN(rl, fb)); +} + +static int +check_corners_HTM(Cube cube) +{ + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + + return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); +} + +static int +check_corners_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret = 15; + Cube c; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + c = apply_alg(trans_algs[i], cube); + ret = MIN(ret, check_corners_HTM(c)); + } + + return ret; +} + +static int +check_edges_HTM(Cube cube) +{ + int ret = 0; + + if (!pd_ep_HTM.generated) + generate_ptable(&pd_ep_HTM); + + ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube))); + ret = MAX(ret, check_eofb_HTM(cube)); + ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube))); + ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube))); + + return ret; +} + +static int +check_drud_HTM(Cube cube) +{ + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + + return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); +} + +static int +check_optimal_HTM(Cube cube) +{ + int dr1, dr2, dr3, drmax, cor; /*ep;*/ + + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + /* + *if (!pd_ep_HTM.generated) + * generate_ptable(&pd_ep_HTM); + */ + + dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); + dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube))); + dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube))); + + drmax = MAX(dr1, MAX(dr2, dr3)); + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + drmax++; + + cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); + /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */ + + /*return MAX(drmax, MAX(ep, cor));*/ + if (drmax == 0 && cor == 0) + return is_solved(cube, false) ? 0 : 1; + return MAX(drmax, cor); +} + + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + Cube moved = {0}; + + moved.epose = epose_mtable[m][cube.epose]; + moved.eposs = eposs_mtable[m][cube.eposs]; + moved.eposm = eposm_mtable[m][cube.eposm]; + moved.eofb = eofb_mtable[m][cube.eofb]; + moved.eorl = eorl_mtable[m][cube.eorl]; + moved.eoud = eoud_mtable[m][cube.eoud]; + moved.coud = coud_mtable[m][cube.coud]; + moved.cofb = cofb_mtable[m][cube.cofb]; + moved.corl = corl_mtable[m][cube.corl]; + moved.cp = cp_mtable[m][cube.cp]; + moved.cpos = cpos_mtable[m][cube.cpos]; + + return moved; +} + +Cube +apply_trans(Trans t, Cube cube) +{ + Cube transformed = {0}; + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + transformed.epose = epose_ttable[t][aux_epos[epose_source[t]]]; + transformed.eposs = eposs_ttable[t][aux_epos[eposs_source[t]]]; + transformed.eposm = eposm_ttable[t][aux_epos[eposm_source[t]]]; + transformed.eofb = eo_ttable[t][aux_eo[eofb_source[t]]]; + transformed.eorl = eo_ttable[t][aux_eo[eorl_source[t]]]; + transformed.eoud = eo_ttable[t][aux_eo[eoud_source[t]]]; + transformed.coud = co_ttable[t][aux_co[coud_source[t]]]; + transformed.corl = co_ttable[t][aux_co[corl_source[t]]]; + transformed.cofb = co_ttable[t][aux_co[cofb_source[t]]]; + transformed.cp = cp_ttable[t][cube.cp]; + transformed.cpos = cpos_ttable[t][cube.cpos]; + + return transformed; +} + +/* TODO: this has to be changed using pre-computations */ +bool +block_solved(Cube cube, Block block) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + int i; + bool ret = true; + + for (i = 0; i < 12; i++) + ret = ret && !(block.edge[i] && (arr->ep[i] != i || arr->eofb[i])); + for (i = 0; i < 8; i++) + ret = ret && !(block.corner[i] && (arr->cp[i] != i || arr->coud[i])); + for (i = 0; i < 6; i++) + ret = ret && !(block.center[i] && arr->cpos[i] != i); + + free_cubearray(arr, pf_all); + + return ret; +} + +Center +center_at(Cube cube, Center c) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_cpos); + + ret = arr->cpos[c]; + free_cubearray(arr, pf_cpos); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +Corner +corner_at(Cube cube, Corner c) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_cp); + + ret = arr->cp[c]; + free_cubearray(arr, pf_cp); + + return ret; +} + +Edge +edge_at(Cube cube, Edge e) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + free_cubearray(arr, pf_ep); + + return ret; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + Trans i; + + if (reorient) + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(trans_algs[i],cube), false)) + return true; + + return equal(cube, (Cube){0}); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b; + uint16_t x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c = apply_trans(step.pre_trans, cube); + DfsData dd = { + .m = 0, + .niss = false, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(c)) + return sols; + + moveset_to_list(step.moveset, step.check, dd.sorted_moves); + + for (dd.d = MAX(opts->min_moves, step.check(c)); + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + solve_dfs(c, step, opts, &dd); + if (opts->feedback) + fprintf(stderr, + "Depth %d completed, found %d solutions\n", + dd.d, sols->len); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans_aux[step.pre_trans], node->alg); + + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + AlgNode *i; + + for (i = alg->last; i != NULL; i = i->prev) + append_move(ret, i->m, i->inverse); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->first = NULL; + alg->last = NULL; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + AlgNode *i; + + for (i = alg->first; i != NULL; i = i->next) + append_move(ret, i->m, !i->inverse); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + char fill[4]; + AlgNode *i; + bool niss = false; + + for (i = alg->first; i != NULL; i = i->next) { + if (!niss && i->inverse) + strcpy(fill, i == alg->first ? "(" : " ("); + if (niss && !i->inverse) + strcpy(fill, ") "); + if (niss == i->inverse) + strcpy(fill, i == alg->first ? "" : " "); + + printf("%s%s", fill, move_string[i->m]); + niss = i->inverse; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + memset(a, 0, 16 * sizeof(uint64_t)); + + if (!pd->generated) + generate_ptable(pd); + + for (i = 0; i < pd->size; i++) + a[PTABLEVAL(pd->ptable, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +remove_last_move(Alg *alg) +{ + AlgNode *newlast = alg->last->prev; + free(alg->last); + if (newlast != NULL) + newlast->next = NULL; + alg->last = newlast; + alg->len--; +} + +void +transform_alg(Trans t, Alg *alg) +{ + AlgNode *i; + + for (i = alg->first; i != NULL; i = i->next) + i->m = moves_ttable[t][i->m]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_trans_aux(); + init_trans(); + init_steps(); +} + + diff --git a/old/2021-06-14-oldalg/cube.h b/old/2021-06-14-oldalg/cube.h @@ -0,0 +1,73 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include "cubetypes.h" + +/* Steps *********************************************************************/ + +extern Step eofb_HTM; +extern Step eorl_HTM; +extern Step eoud_HTM; +extern Step coud_HTM; +extern Step corl_HTM; +extern Step cofb_HTM; +extern Step coud_URF; +extern Step corl_URF; +extern Step cofb_URF; +extern Step corners_HTM; +extern Step corners_URF; +extern Step edges_HTM; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_ep_HTM; +extern PruneData pd_drud_HTM; + +/* Public functions **********************************************************/ + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Center center_at(Cube cube, Center c); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Corner corner_at(Cube cube, Corner c); +Edge edge_at(Cube cube, Edge e); +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +int piece_orientation(Cube cube, int piece, char *orientation); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); + +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +void remove_last_move(Alg *alg); +void transform_alg(Trans t, Alg *alg); + +void print_ptable(PruneData *pd); + +void init(); + +#endif + diff --git a/old/2021-06-14-oldalg/cubetypes.h b/old/2021-06-14-oldalg/cubetypes.h @@ -0,0 +1,210 @@ +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct algnode AlgNode; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, /* R|L */ +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + AlgNode * first; + AlgNode * last; + int len; +}; + +struct +algnode +{ + Move m; + bool inverse; + AlgNode * next; + AlgNode * prev; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +cube +{ + uint16_t epose; + uint16_t eposs; + uint16_t eposm; + uint16_t eofb; + uint16_t eorl; + uint16_t eoud; + uint16_t cp; + uint16_t coud; + uint16_t cofb; + uint16_t corl; + uint16_t cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +dfsdata +{ + int d; + int m; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[z3+1]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + uint8_t * reached; + bool generated; + uint64_t n; + uint64_t size; + uint64_t (*index)(Cube); + bool (*moveset)(Move); +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; +}; + +struct +step +{ + int (*check)(Cube); + int (*ready)(Cube); + bool (*moveset)(Move); + Trans pre_trans; +}; diff --git a/old/2021-06-14-oldalg/main.c b/old/2021-06-14-oldalg/main.c @@ -0,0 +1,56 @@ +#include <stdio.h> +#include "cube.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 2, nrand = 100000, sum; + + Step *stps[20] = {&drud_HTM, &optimal_HTM}; + char sss[30][30] = {"DR on U/D", "Optimal solution"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true + }; + + init(); + +/* + print_ptable(&pd_drud_HTM); + print_ptable(&pd_ep_HTM); + print_ptable(&pd_corners_HTM); +*/ + + srand(time(NULL)); + sum = 0; + for (i = 0; i < nrand; i++) + sum += optimal_HTM.check(random_cube()); + printf("Average pruning value: %lf\n", ((double)sum) / ((double) nrand)); + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + for (i = 0; i < ns; i++) { + if (stps[i]->check == NULL) + fprintf(stderr, "Check function for step %d is null\n", i); + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free(algo); + } + + return 0; +} diff --git a/old/2021-06-15-before-separating-steps/cube.c b/old/2021-06-15-before-separating-steps/cube.c @@ -0,0 +1,2836 @@ +#include "cube.h" + +/* Constants and macros *****************************************************/ + +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define BASEALGLEN 50 +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) +#define PTABLESIZE(n) ((n+1) / 2) +#define PTABLEVAL(tab,ind) (((ind)%2) ? (tab[(ind)/2] / 16) : \ + (tab[(ind)/2] % 16)) + + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static uint16_t array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static Move base_move(Move m); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent(int pos1, int pos2); +static uint16_t epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void generate_ptable(PruneData *pd); +static void generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void moveset_to_list(bool (*ms)(Move), int (*f)(Cube), Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static void ptable_set_reached(PruneData *pd, uint64_t ind); +static void ptable_update(PruneData *pd, uint64_t ind, int m); +static bool read_ptable_file(PruneData *pd); +static bool read_ttables_file(); +static bool read_mtables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static void solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_ptable_file(PruneData *pd); +static bool write_ttables_file(); +static bool write_mtables_file(); + +static void init_auxtables(); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_steps(); +static void init_strings(); +static void init_trans(); +static void init_trans_aux(); + +static bool moveset_HTM(Move m); +static bool moveset_URF(Move m); + +/* Steps and related functions and data **************************************/ + +Step eofb_HTM; +Step eorl_HTM; +Step eoud_HTM; +Step coud_HTM; +Step corl_HTM; +Step cofb_HTM; +Step coud_URF; +Step corl_URF; +Step cofb_URF; +Step corners_HTM; +Step corners_URF; +Step edges_HTM; +Step drud_HTM; +Step optimal_HTM; + +PruneData pd_eofb_HTM; +PruneData pd_coud_HTM; +PruneData pd_corners_HTM; +PruneData pd_ep_HTM; +PruneData pd_drud_HTM; + +static uint64_t index_eofb(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_ep(Cube cube); +static uint64_t index_drud(Cube cube); + +static int check_nothing(Cube cube); +static int check_eofb_HTM(Cube cube); +static int check_coud_HTM(Cube cube); +static int check_coud_URF(Cube cube); +static int check_corners_HTM(Cube cube); +static int check_corners_URF(Cube cube); +static int check_edges_HTM(Cube cube); +static int check_drud_HTM(Cube cube); +static int check_optimal_HTM(Cube cube); + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; + +static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eo_ttable[NTRANS][POW2TO11]; +static uint16_t cp_ttable[NTRANS][FACTORIAL8]; +static uint16_t co_ttable[NTRANS][POW3TO7]; +static uint16_t cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eofb_mtable[NMOVES][POW2TO11]; +static uint16_t eorl_mtable[NMOVES][POW2TO11]; +static uint16_t eoud_mtable[NMOVES][POW2TO11]; +static uint16_t cp_mtable[NMOVES][FACTORIAL8]; +static uint16_t coud_mtable[NMOVES][POW3TO7]; +static uint16_t cofb_mtable[NMOVES][POW3TO7]; +static uint16_t corl_mtable[NMOVES][POW3TO7]; +static uint16_t cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * trans_algs[NROTATIONS]; + + +/* Local functions implementation ********************************************/ + +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) { + if (alg->len > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might be wrong.\n"); + } + alg->move = realloc(alg->move, 2 * alg->len * sizeof(Move)); + alg->inv = realloc(alg->inv, 2 * alg->len * sizeof(bool)); + alg->allocated *= 2; + } + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static uint16_t +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 1) + ep8[j++] = (edge_slice(ep[i]) == 0) ? 1 : 0; + + return subset_to_index(ep8, 8, 4); +} + +static uint16_t +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(uint16_t epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +generate_ptable(PruneData *pd) +{ + uint64_t j; + DfsData dd; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + dd.m = 0; + dd.last1 = NULLMOVE; + dd.last2 = NULLMOVE; + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->size; j++) + ptable_update(pd, j, 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + + pd->reached = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t)); + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) { + memset(pd->reached, 0, PTABLESIZE(pd->size)*sizeof(uint8_t)); + generate_ptable_dfs((Cube){0}, pd, &dd); + fprintf(stderr, "Depth %d completed, generated %lu/%lu\n", + dd.d, pd->n, pd->size); + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; + free(pd->reached); +} + +static void +generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + uint64_t ind = pd->index(c); + int oldval = PTABLEVAL(pd->ptable, ind); + Move i, move, l1 = dd->last1, l2 = dd->last2; + + if (oldval < dd->m || PTABLEVAL(pd->reached, ind) || pd->n == pd->size) + return; + + ptable_set_reached(pd, ind); + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, ind, dd->m); + return; + } + + dd->m++; + dd->last2 = dd->last1; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (possible_next[l2][l1][move]) { + dd->last1 = move; + generate_ptable_dfs(apply_move(move, c), pd, dd); + } + } + + dd->m--; + dd->last1 = l1; + dd->last2 = l2; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +moveset_to_list(bool (*ms)(Move m), int (*f)(Cube), Move *r) +{ + Cube c; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + c = apply_move(i, (Cube){0}); + if (f != NULL && f(c)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; /* Truncate */ + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static void +ptable_set_reached(PruneData *pd, uint64_t ind) +{ + uint8_t oldval2 = pd->reached[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1; +} + +static void +ptable_update(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n; + pd->n++; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f); + fclose(f); + + return r == PTABLESIZE(pd->size); +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(uint16_t); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + Alg *inv; + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Cube ret; + + if (r != mirror) { + ret = apply_alg_generic(trans_algs[r], c, f, true); + inv = on_inverse(trans_algs[r]); + ret = apply_alg_generic(inv, ret, f, true); + free_alg(inv); + } else { + ret = move_via_arrays(&ma, (Cube){0}, f); + ret = compose_filtered(c, ret, f); + ret = move_via_arrays(&ma, ret, f); + } + + return ret; +} + +static void +solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move move, l1 = dd->last1, l2 = dd->last2; + int i, lower_bound = s.check(c); + bool found_many, prune, niss_make_sense, nissbackup = dd->niss; + + if (opts->can_niss && !dd->niss) + lower_bound = MIN(1, lower_bound); + + found_many = dd->sols->len >= opts->max_solutions; + prune = dd->current_alg->len + lower_bound > dd->d; + if (found_many || prune) + return; + + if (lower_bound == 0) { + if (dd->current_alg->len == dd->d) + append_alg(dd->sols, dd->current_alg); + return; + } + + dd->last2 = dd->last1; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (possible_next[l2][l1][move]) { + dd->last1 = move; + append_move(dd->current_alg, move, dd->niss); + solve_dfs(apply_move(move, c), s, opts, dd); + dd->current_alg->len--; + } + } + + niss_make_sense = !dd->current_alg->len || + (s.check(apply_move(l1,(Cube){0}))); + if (opts->can_niss && !dd->niss && niss_make_sense) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + solve_dfs(inverse_cube(c), s, opts, dd); + } + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = nissbackup; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f); + fclose(f); + + return written == PTABLESIZE(pd->size); +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(uint16_t); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + uint64_t ui, uj; + int i, j, k; + bool cij, p1, p2; + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj] = epos_dependent(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2); + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[mirror] = mirror; +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + uint16_t ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray){ + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_steps() +{ + /* PruneData */ + pd_eofb_HTM.filename = "ptable_eofb_HTM"; + pd_eofb_HTM.size = POW2TO11; + pd_eofb_HTM.index = index_eofb; + pd_eofb_HTM.moveset = moveset_HTM; + + pd_coud_HTM.filename = "ptable_coud_HTM"; + pd_coud_HTM.size = POW3TO7; + pd_coud_HTM.index = index_coud; + pd_coud_HTM.moveset = moveset_HTM; + + pd_corners_HTM.filename = "ptable_corners_HTM"; + pd_corners_HTM.size = POW3TO7 * FACTORIAL8; + pd_corners_HTM.index = index_corners; + pd_corners_HTM.moveset = moveset_HTM; + + pd_ep_HTM.filename = "ptable_ep_HTM"; + pd_ep_HTM.size = FACTORIAL12; + pd_ep_HTM.index = index_ep; + pd_ep_HTM.moveset = moveset_HTM; + + pd_drud_HTM.filename = "ptable_drud_HTM"; + pd_drud_HTM.size = POW2TO11 * POW3TO7 * BINOM12ON4; + pd_drud_HTM.index = index_drud; + pd_drud_HTM.moveset = moveset_HTM; + + + /* Actual steps */ + eofb_HTM.check = check_eofb_HTM; + eofb_HTM.ready = check_nothing; + eofb_HTM.pre_trans = uf; + eofb_HTM.moveset = moveset_HTM; + + eorl_HTM.check = check_eofb_HTM; + eorl_HTM.ready = check_nothing; + eorl_HTM.pre_trans = ur; + eorl_HTM.moveset = moveset_HTM; + + eoud_HTM.check = check_eofb_HTM; + eoud_HTM.ready = check_nothing; + eoud_HTM.pre_trans = bu; + eoud_HTM.moveset = moveset_HTM; + + + coud_HTM.check = check_coud_HTM; + coud_HTM.ready = check_nothing; + coud_HTM.pre_trans = uf; + coud_HTM.moveset = moveset_HTM; + + corl_HTM.check = check_coud_HTM; + corl_HTM.ready = check_nothing; + corl_HTM.pre_trans = rf; + corl_HTM.moveset = moveset_HTM; + + cofb_HTM.check = check_coud_HTM; + cofb_HTM.ready = check_nothing; + cofb_HTM.pre_trans = fd; + cofb_HTM.moveset = moveset_HTM; + + coud_URF.check = check_coud_URF; + coud_URF.ready = check_nothing; + coud_URF.pre_trans = uf; + coud_URF.moveset = moveset_URF; + + corl_URF.check = check_coud_URF; + corl_URF.ready = check_nothing; + corl_URF.pre_trans = rf; + corl_URF.moveset = moveset_URF; + + cofb_URF.check = check_coud_URF; + cofb_URF.ready = check_nothing; + cofb_URF.pre_trans = fd; + cofb_URF.moveset = moveset_URF; + + corners_HTM.check = check_corners_HTM; + corners_HTM.ready = check_nothing; + corners_HTM.pre_trans = uf; + corners_HTM.moveset = moveset_HTM; + + corners_URF.check = check_corners_URF; + corners_URF.ready = check_nothing; + corners_URF.pre_trans = uf; + corners_URF.moveset = moveset_URF; + + edges_HTM.check = check_edges_HTM; + edges_HTM.ready = check_nothing; + edges_HTM.pre_trans = uf; + edges_HTM.moveset = moveset_HTM; + + drud_HTM.check = check_drud_HTM; + drud_HTM.ready = check_nothing; + drud_HTM.pre_trans = uf; + drud_HTM.moveset = moveset_HTM; + + optimal_HTM.check = check_optimal_HTM; + optimal_HTM.ready = check_nothing; + optimal_HTM.pre_trans = uf; + optimal_HTM.moveset = moveset_HTM; +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_trans() { + Cube aux, cube, c[3]; + CubeArray epcp; + int eparr[12], eoarr[12]; + int cparr[8], coarr[8]; + int i; + bool b1, b2, b3; + uint16_t ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + if (i == mirror) + cube = (Cube){0}; + else + cube = apply_alg(trans_algs[i], (Cube){0}); + + epose_source[i] = edge_slice(edge_at(cube, FR)); + eposs_source[i] = edge_slice(edge_at(cube, UR)); + eposm_source[i] = edge_slice(edge_at(cube, UF)); + eofb_source[i] = center_at(cube, F_center)/2; + eorl_source[i] = center_at(cube, R_center)/2; + eoud_source[i] = center_at(cube, U_center)/2; + coud_source[i] = center_at(cube, U_center)/2; + cofb_source[i] = center_at(cube, F_center)/2; + corl_source[i] = center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + if (m == mirror) { + memcpy(eparr, ep_mirror, 12 * sizeof(int)); + memcpy(cparr, cp_mirror, 8 * sizeof(int)); + } else { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(trans_algs[m], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m == mirror) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + if (m == mirror) { + b1 = (mi >= U && mi <= Bw3); + b2 = (mi >= S && mi <= E3); + b3 = (mi >= x && mi <= z3); + if (b1 || b2 || b3) + moves_ttable[m][mi] = + inverse_move_aux[mi]; + else + moves_ttable[m][mi] = mi; + + if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3) + moves_ttable[m][mi] += 3; + if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3) + moves_ttable[m][mi] -= 3; + } else { + aux = apply_trans(m, apply_move(mi,(Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move( + inverse_move_aux[move], aux); + if (is_solved(cube, false)) + moves_ttable[m][mi] = move; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + trans_algs[uf] = new_alg(""); + trans_algs[ur] = new_alg("y"); + trans_algs[ub] = new_alg("y2"); + trans_algs[ul] = new_alg("y3"); + + trans_algs[df] = new_alg("z2"); + trans_algs[dr] = new_alg("y z2"); + trans_algs[db] = new_alg("x2"); + trans_algs[dl] = new_alg("y3 z2"); + + trans_algs[rf] = new_alg("z3"); + trans_algs[rd] = new_alg("z3 y"); + trans_algs[rb] = new_alg("z3 y2"); + trans_algs[ru] = new_alg("z3 y3"); + + trans_algs[lf] = new_alg("z"); + trans_algs[ld] = new_alg("z y3"); + trans_algs[lb] = new_alg("z y2"); + trans_algs[lu] = new_alg("z y"); + + trans_algs[fu] = new_alg("x y2"); + trans_algs[fr] = new_alg("x y"); + trans_algs[fd] = new_alg("x"); + trans_algs[fl] = new_alg("x y3"); + + trans_algs[bu] = new_alg("x3"); + trans_algs[br] = new_alg("x3 y"); + trans_algs[bd] = new_alg("x3 y2"); + trans_algs[bl] = new_alg("x3 y3"); +} + +/* Linearization functions implementation ************************************/ + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_ep(Cube cube) +{ + uint64_t a, b, c; + + /* TODO: remove this check */ + if (epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] ==BINOM8ON4) + fprintf(stderr, "Impossible\n"); + + a = cube.eposs; + b = (cube.epose % FACTORIAL4) + + epos_dependent_aux[cube.eposs/FACTORIAL4][cube.epose/FACTORIAL4] * + FACTORIAL4; + c = cube.eposm % FACTORIAL4; + + b *= FACTORIAL4 * BINOM12ON4; + c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4; + + return a + b + c; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +/* Step checking functions implementation ************************************/ + +static int +check_nothing(Cube cube) +{ + return true; +} + +static int +check_eofb_HTM(Cube cube) +{ + if (!pd_eofb_HTM.generated) + generate_ptable(&pd_eofb_HTM); + + return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb); +} + +static int +check_coud_HTM(Cube cube) +{ + if (!pd_coud_HTM.generated) + generate_ptable(&pd_coud_HTM); + + return PTABLEVAL(pd_coud_HTM.ptable, cube.coud); +} + +static int +check_coud_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + int ud = check_coud_HTM(cube); + int rl = check_coud_HTM(apply_move(z, cube)); + int fb = check_coud_HTM(apply_move(x, cube)); + + return MIN(ud, MIN(rl, fb)); +} + +static int +check_corners_HTM(Cube cube) +{ + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + + return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); +} + +static int +check_corners_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret = 15; + Cube c; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + c = apply_alg(trans_algs[i], cube); + ret = MIN(ret, check_corners_HTM(c)); + } + + return ret; +} + +static int +check_edges_HTM(Cube cube) +{ + int ret = 0; + + if (!pd_ep_HTM.generated) + generate_ptable(&pd_ep_HTM); + + ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube))); + ret = MAX(ret, check_eofb_HTM(cube)); + ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube))); + ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube))); + + return ret; +} + +static int +check_drud_HTM(Cube cube) +{ + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + + return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); +} + +static int +check_optimal_HTM(Cube cube) +{ + int dr1, dr2, dr3, drmax, cor; /*ep;*/ + + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + /* + *if (!pd_ep_HTM.generated) + * generate_ptable(&pd_ep_HTM); + */ + + dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); + dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube))); + dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube))); + + drmax = MAX(dr1, MAX(dr2, dr3)); + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + drmax++; + + cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); + /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */ + + /*return MAX(drmax, MAX(ep, cor));*/ + if (drmax == 0 && cor == 0) + return is_solved(cube, false) ? 0 : 1; + return MAX(drmax, cor); +} + + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + Cube moved = {0}; + + moved.epose = epose_mtable[m][cube.epose]; + moved.eposs = eposs_mtable[m][cube.eposs]; + moved.eposm = eposm_mtable[m][cube.eposm]; + moved.eofb = eofb_mtable[m][cube.eofb]; + moved.eorl = eorl_mtable[m][cube.eorl]; + moved.eoud = eoud_mtable[m][cube.eoud]; + moved.coud = coud_mtable[m][cube.coud]; + moved.cofb = cofb_mtable[m][cube.cofb]; + moved.corl = corl_mtable[m][cube.corl]; + moved.cp = cp_mtable[m][cube.cp]; + moved.cpos = cpos_mtable[m][cube.cpos]; + + return moved; +} + +Cube +apply_trans(Trans t, Cube cube) +{ + Cube transformed = {0}; + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + transformed.epose = epose_ttable[t][aux_epos[epose_source[t]]]; + transformed.eposs = eposs_ttable[t][aux_epos[eposs_source[t]]]; + transformed.eposm = eposm_ttable[t][aux_epos[eposm_source[t]]]; + transformed.eofb = eo_ttable[t][aux_eo[eofb_source[t]]]; + transformed.eorl = eo_ttable[t][aux_eo[eorl_source[t]]]; + transformed.eoud = eo_ttable[t][aux_eo[eoud_source[t]]]; + transformed.coud = co_ttable[t][aux_co[coud_source[t]]]; + transformed.corl = co_ttable[t][aux_co[corl_source[t]]]; + transformed.cofb = co_ttable[t][aux_co[cofb_source[t]]]; + transformed.cp = cp_ttable[t][cube.cp]; + transformed.cpos = cpos_ttable[t][cube.cpos]; + + return transformed; +} + +/* TODO: this has to be changed using pre-computations */ +bool +block_solved(Cube cube, Block block) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + int i; + bool ret = true; + + for (i = 0; i < 12; i++) + ret = ret && !(block.edge[i] && (arr->ep[i] != i || arr->eofb[i])); + for (i = 0; i < 8; i++) + ret = ret && !(block.corner[i] && (arr->cp[i] != i || arr->coud[i])); + for (i = 0; i < 6; i++) + ret = ret && !(block.center[i] && arr->cpos[i] != i); + + free_cubearray(arr, pf_all); + + return ret; +} + +Center +center_at(Cube cube, Center c) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_cpos); + + ret = arr->cpos[c]; + free_cubearray(arr, pf_cpos); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +Corner +corner_at(Cube cube, Corner c) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_cp); + + ret = arr->cp[c]; + free_cubearray(arr, pf_cp); + + return ret; +} + +Edge +edge_at(Cube cube, Edge e) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + free_cubearray(arr, pf_ep); + + return ret; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + Trans i; + + if (reorient) + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(trans_algs[i],cube), false)) + return true; + + return equal(cube, (Cube){0}); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b; + uint16_t x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c = apply_trans(step.pre_trans, cube); + DfsData dd = { + .m = 0, + .niss = false, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(c)) + return sols; + + moveset_to_list(step.moveset, step.check, dd.sorted_moves); + + for (dd.d = MAX(opts->min_moves, step.check(c)); + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + solve_dfs(c, step, opts, &dd); + if (opts->feedback) + fprintf(stderr, + "Depth %d completed, found %d solutions\n", + dd.d, sols->len); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans_aux[step.pre_trans], node->alg); + + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, alg->move[i], alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + memset(a, 0, 16 * sizeof(uint64_t)); + + if (!pd->generated) + generate_ptable(pd); + + for (i = 0; i < pd->size; i++) + a[PTABLEVAL(pd->ptable, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_trans_aux(); + init_trans(); + init_steps(); +} + + diff --git a/old/2021-06-15-before-separating-steps/cube.h b/old/2021-06-15-before-separating-steps/cube.h @@ -0,0 +1,72 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include "cubetypes.h" + +/* Steps *********************************************************************/ + +extern Step eofb_HTM; +extern Step eorl_HTM; +extern Step eoud_HTM; +extern Step coud_HTM; +extern Step corl_HTM; +extern Step cofb_HTM; +extern Step coud_URF; +extern Step corl_URF; +extern Step cofb_URF; +extern Step corners_HTM; +extern Step corners_URF; +extern Step edges_HTM; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_ep_HTM; +extern PruneData pd_drud_HTM; + +/* Public functions **********************************************************/ + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Center center_at(Cube cube, Center c); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Corner corner_at(Cube cube, Corner c); +Edge edge_at(Cube cube, Edge e); +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +int piece_orientation(Cube cube, int piece, char *orientation); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); + +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +void transform_alg(Trans t, Alg *alg); + +void print_ptable(PruneData *pd); + +void init(); + +#endif + diff --git a/old/2021-06-15-before-separating-steps/cubetypes.h b/old/2021-06-15-before-separating-steps/cubetypes.h @@ -0,0 +1,201 @@ +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, /* R|L */ +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +cube +{ + uint16_t epose; + uint16_t eposs; + uint16_t eposm; + uint16_t eofb; + uint16_t eorl; + uint16_t eoud; + uint16_t cp; + uint16_t coud; + uint16_t cofb; + uint16_t corl; + uint16_t cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +dfsdata +{ + int d; + int m; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[z3+1]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + uint8_t * reached; + bool generated; + uint64_t n; + uint64_t size; + uint64_t (*index)(Cube); + bool (*moveset)(Move); +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; +}; + +struct +step +{ + int (*check)(Cube); + int (*ready)(Cube); + bool (*moveset)(Move); + Trans pre_trans; +}; diff --git a/old/2021-06-15-before-separating-steps/main.c b/old/2021-06-15-before-separating-steps/main.c @@ -0,0 +1,60 @@ +#include <stdio.h> +#include "cube.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 2, nrand = 100000, sum1, sum2; + + Step *stps[20] = {&drud_HTM, &optimal_HTM}; + char sss[30][30] = {"DR on U/D", "Optimal solution"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true + }; + + init(); + +/* + print_ptable(&pd_drud_HTM); + print_ptable(&pd_ep_HTM); + print_ptable(&pd_corners_HTM); +*/ + + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + for (i = 0; i < nrand; i++) { + sum1 += optimal_HTM.check(random_cube()); + sum2 += corners_HTM.check(random_cube()); + } + printf("Average optimal pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners pruning: %lf\n", ((double)sum2) / ((double) nrand)); + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + for (i = 0; i < ns; i++) { + if (stps[i]->check == NULL) + fprintf(stderr, "Check function for step %d is null\n", i); + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free(algo); + } + + return 0; +} diff --git a/old/2021-06-15-before-separating-steps/steps.h b/old/2021-06-15-before-separating-steps/steps.h @@ -0,0 +1,20 @@ +extern Step eofb_HTM; +extern Step eorl_HTM; +extern Step eoud_HTM; +extern Step coud_HTM; +extern Step corl_HTM; +extern Step cofb_HTM; +extern Step coud_URF; +extern Step corl_URF; +extern Step cofb_URF; +extern Step corners_HTM; +extern Step corners_URF; +extern Step edges_HTM; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_ep_HTM; +extern PruneData pd_drud_HTM; diff --git a/old/2021-06-17-cachedata/cube.c b/old/2021-06-17-cachedata/cube.c @@ -0,0 +1,2738 @@ +#include "cube.h" + +/* Constants and macros *****************************************************/ + +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) +#define PTABLESIZE(n) ((n+1) / 2) + + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static bool allowed_next_move(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static uint16_t array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent(int pos1, int pos2); +static uint16_t epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void generate_ctable_dfs(Cube c, CacheData *cd, DfsData *dd); +static void generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Checker f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +/*static int powint(int a, int b);*/ +static void ptable_set_reached(PruneData *pd, uint64_t ind); +static void ptable_update(PruneData *pd, uint64_t ind, int m); +static void push_cachenode(CacheData *cd, Cube c, Alg *alg); +static void realloc_alg(Alg *alg, int n); +static bool read_ctable_file(CacheData *cd); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static void solve_cd(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_ctable_file(CacheData *cd); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_ttables_file(); + +static void init_auxtables(); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_trans(); +static void init_trans_aux(); + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; + +static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eo_ttable[NTRANS][POW2TO11]; +static uint16_t cp_ttable[NTRANS][FACTORIAL8]; +static uint16_t co_ttable[NTRANS][POW3TO7]; +static uint16_t cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eofb_mtable[NMOVES][POW2TO11]; +static uint16_t eorl_mtable[NMOVES][POW2TO11]; +static uint16_t eoud_mtable[NMOVES][POW2TO11]; +static uint16_t cp_mtable[NMOVES][FACTORIAL8]; +static uint16_t coud_mtable[NMOVES][POW3TO7]; +static uint16_t cofb_mtable[NMOVES][POW3TO7]; +static uint16_t corl_mtable[NMOVES][POW3TO7]; +static uint16_t cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * trans_algs[NROTATIONS]; + + +/* Local functions implementation ********************************************/ + +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static bool +allowed_next_move(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static uint16_t +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 1) + ep8[j++] = (edge_slice(ep[i]) == 0) ? 1 : 0; + + return subset_to_index(ep8, 8, 4); +} + +static uint16_t +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(uint16_t epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +generate_ctable_dfs(Cube c, CacheData *cd, DfsData *dd) +{ + int i; + bool flag; + Move move, l1 = dd->last1, l2 = dd->last2; + + if (dd->current_alg->len > 0) { + for (i = 0, flag = true; i < cd->nind; i++) { + if (cd->index[i](c) != cd->index[i]((Cube){0})) { + flag = false; + break; + } + } + if (flag) + return; + } + + if (dd->current_alg->len == dd->d) { + push_cachenode(cd, c, dd->current_alg); + return; + } + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next_move(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + append_move(dd->current_alg, move, false); + generate_ctable_dfs(apply_move(move, c), cd, dd); + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static void +generate_ptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + uint64_t ind = pd->index(c); + int oldval = PTABLEVAL(pd->ptable, ind); + Move i, move, l1 = dd->last1, l2 = dd->last2; + + if (oldval < dd->m || PTABLEVAL(pd->reached, ind) || pd->n == pd->size) + return; + + ptable_set_reached(pd, ind); + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, ind, dd->m); + return; + } + + dd->m++; + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next_move(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + generate_ptable_dfs(apply_move(move, c), pd, dd); + dd->last2 = l2; + dd->last1 = l1; + } + } + + dd->m--; + dd->last1 = l1; + dd->last2 = l2; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(bool (*ms)(Move m), int (*f)(Cube), Move *r) +{ + Cube c; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + c = apply_move(i, (Cube){0}); + if (f != NULL && f(c)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +/* +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} +*/ + +static void +ptable_set_reached(PruneData *pd, uint64_t ind) +{ + uint8_t oldval2 = pd->reached[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1; +} + +static void +ptable_update(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n; + pd->n++; +} + +static void +push_cachenode(CacheData *cd, Cube c, Alg *alg) +{ + CacheNode *node = malloc(sizeof(CacheNode)); + uint64_t ind0 = cd->index[0](c); + int i; + + node->indexval = malloc((cd->nind - 1) * sizeof(uint64_t)); + for (i = 1; i < cd->nind; i++) + node->indexval[i-1] = cd->index[i](c); + + node->sol = inverse_alg(alg); + realloc_alg(node->sol, cd->len); + node->next = cd->ctable[ind0]; + cd->ctable[ind0] = node; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n); + alg->inv = realloc(alg->inv, n); + alg->allocated = n; +} + +static bool +read_ctable_file(CacheData *cd) +{ + return false; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f); + fclose(f); + + return r == PTABLESIZE(pd->size); +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(uint16_t); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + Alg *inv; + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Cube ret; + + if (r != mirror) { + ret = apply_alg_generic(trans_algs[r], c, f, true); + inv = on_inverse(trans_algs[r]); + ret = apply_alg_generic(inv, ret, f, true); + free_alg(inv); + } else { + ret = move_via_arrays(&ma, (Cube){0}, f); + ret = compose_filtered(c, ret, f); + ret = move_via_arrays(&ma, ret, f); + } + + return ret; +} + +static void +solve_cd(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + int j, k; + bool flag; + uint64_t ind0; + CacheNode *i; + + if (!s.cd->generated) + generate_ctable(s.cd); + + ind0 = s.cd->index[0](c); + for (i = s.cd->ctable[ind0]; + i != NULL && dd->sols->len < opts->max_solutions; + i = i->next) { + for (j = 1, flag = true; j < s.cd->nind; j++) { + if (s.cd->index[j](c) != i->indexval[j-1]) { + flag = false; + break; + } + } + if (flag) { + for (k = 0; k < s.cd->len; k++) + append_move(dd->current_alg, + i->sol->move[k], dd->niss); + append_alg(dd->sols, dd->current_alg); + if (opts->feedback) { + printf("[cached] "); + print_alg(dd->current_alg, false); + } + } + } +} + +static void +solve_dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move move, l1 = dd->last1, l2 = dd->last2; + int i, lower_bound; + bool nissbackup = dd->niss; + + if (dd->sols->len >= opts->max_solutions) + return; + + lower_bound = s.check(c); + if (opts->can_niss && !dd->niss) + lower_bound = MIN(1, lower_bound); + + if (dd->current_alg->len + lower_bound > dd->d) + return; + + if (lower_bound == 0) { + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + if (opts->feedback) + print_alg(dd->current_alg, false); + } + return; + } + + if (s.cd != NULL && s.cd->len == dd->d - dd->current_alg->len) { + solve_cd(c, s, opts, dd); + } else { + for (i = 0; + dd->sorted_moves[i] != NULLMOVE && + dd->sols->len < opts->max_solutions; + i++) { + move = dd->sorted_moves[i]; + if (allowed_next_move(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + append_move(dd->current_alg, move, dd->niss); + solve_dfs(apply_move(move, c), s, opts, dd); + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } + } + + if (opts->can_niss && !dd->niss) { + if (dd->current_alg->len == 0 || + (s.check(apply_move(inverse_move(l1), (Cube){0})))) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + solve_dfs(inverse_cube(c), s, opts, dd); + } + } + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = nissbackup; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_ctable_file(CacheData *cd) +{ + return false; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), PTABLESIZE(pd->size), f); + fclose(f); + + return written == PTABLESIZE(pd->size); +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(uint16_t); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + uint64_t ui, uj; + int i, j, k; + bool cij, p1, p2; + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj] = epos_dependent(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[mirror] = mirror; +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + uint16_t ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_trans() { + Cube aux, cube, c[3]; + CubeArray epcp; + int eparr[12], eoarr[12]; + int cparr[8], coarr[8]; + int i; + bool b1, b2, b3; + uint16_t ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + if (i == mirror) + cube = (Cube){0}; + else + cube = apply_alg(trans_algs[i], (Cube){0}); + + epose_source[i] = edge_slice(edge_at(cube, FR)); + eposs_source[i] = edge_slice(edge_at(cube, UR)); + eposm_source[i] = edge_slice(edge_at(cube, UF)); + eofb_source[i] = center_at(cube, F_center)/2; + eorl_source[i] = center_at(cube, R_center)/2; + eoud_source[i] = center_at(cube, U_center)/2; + coud_source[i] = center_at(cube, U_center)/2; + cofb_source[i] = center_at(cube, F_center)/2; + corl_source[i] = center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + if (m == mirror) { + memcpy(eparr, ep_mirror, 12 * sizeof(int)); + memcpy(cparr, cp_mirror, 8 * sizeof(int)); + } else { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(trans_algs[m], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m == mirror) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + if (m == mirror) { + b1 = (mi >= U && mi <= Bw3); + b2 = (mi >= S && mi <= E3); + b3 = (mi >= x && mi <= z3); + if (b1 || b2 || b3) + moves_ttable[m][mi] = + inverse_move_aux[mi]; + else + moves_ttable[m][mi] = mi; + + if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3) + moves_ttable[m][mi] += 3; + if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3) + moves_ttable[m][mi] -= 3; + } else { + aux = apply_trans(m, apply_move(mi,(Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move( + inverse_move_aux[move], aux); + if (is_solved(cube, false)) + moves_ttable[m][mi] = move; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + trans_algs[uf] = new_alg(""); + trans_algs[ur] = new_alg("y"); + trans_algs[ub] = new_alg("y2"); + trans_algs[ul] = new_alg("y3"); + + trans_algs[df] = new_alg("z2"); + trans_algs[dr] = new_alg("y z2"); + trans_algs[db] = new_alg("x2"); + trans_algs[dl] = new_alg("y3 z2"); + + trans_algs[rf] = new_alg("z3"); + trans_algs[rd] = new_alg("z3 y"); + trans_algs[rb] = new_alg("z3 y2"); + trans_algs[ru] = new_alg("z3 y3"); + + trans_algs[lf] = new_alg("z"); + trans_algs[ld] = new_alg("z y3"); + trans_algs[lb] = new_alg("z y2"); + trans_algs[lu] = new_alg("z y"); + + trans_algs[fu] = new_alg("x y2"); + trans_algs[fr] = new_alg("x y"); + trans_algs[fd] = new_alg("x"); + trans_algs[fl] = new_alg("x y3"); + + trans_algs[bu] = new_alg("x3"); + trans_algs[br] = new_alg("x3 y"); + trans_algs[bd] = new_alg("x3 y2"); + trans_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + +Cube +apply_trans(Trans t, Cube cube) +{ + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +/* TODO: this has to be changed using pre-computations */ +bool +block_solved(Cube cube, Block block) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + int i; + bool ret = true; + + for (i = 0; i < 12; i++) + ret = ret && !(block.edge[i] && (arr->ep[i] != i || arr->eofb[i])); + for (i = 0; i < 8; i++) + ret = ret && !(block.corner[i] && (arr->cp[i] != i || arr->coud[i])); + for (i = 0; i < 6; i++) + ret = ret && !(block.center[i] && arr->cpos[i] != i); + + free_cubearray(arr, pf_all); + + return ret; +} + +Center +center_at(Cube cube, Center c) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_cpos); + + ret = arr->cpos[c]; + free_cubearray(arr, pf_cpos); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +Corner +corner_at(Cube cube, Corner c) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_cp); + + ret = arr->cp[c]; + free_cubearray(arr, pf_cp); + + return ret; +} + +Edge +edge_at(Cube cube, Edge e) +{ + int ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + free_cubearray(arr, pf_ep); + + return ret; +} + +uint64_t +epos_dependent_cube(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +generate_ctable(CacheData *cd) +{ + uint64_t i; + DfsData dd = { + .d = cd->len, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .current_alg = new_alg("") + }; + + if (cd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + cd->ctable = malloc(cd->maxind0 * sizeof(CacheNode *)); + for (i = 0; i < cd->maxind0; i++) + cd->ctable[i] = NULL; + + if (read_ctable_file(cd)) { + cd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", cd->filename); + + moveset_to_list(cd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + generate_ctable_dfs((Cube){0}, cd, &dd); + + if (!write_ctable_file(cd)) + fprintf(stderr, "Error writing ctable file\n"); + + cd->generated = true; +} + +void +generate_ptable(PruneData *pd) +{ + uint64_t j; + DfsData dd = { + .m = 0, + .last1 = NULLMOVE, + .last2 = NULLMOVE + }; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->size; j++) + ptable_update(pd, j, 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + pd->reached = malloc(PTABLESIZE(pd->size) * sizeof(uint8_t)); + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) { + memset(pd->reached, 0, PTABLESIZE(pd->size)*sizeof(uint8_t)); + generate_ptable_dfs((Cube){0}, pd, &dd); + fprintf(stderr, "Depth %d completed, generated %lu/%lu\n", + dd.d, pd->n, pd->size); + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; + free(pd->reached); +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + Trans i; + + if (reorient) + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(trans_algs[i],cube), false)) + return true; + + return equal(cube, (Cube){0}); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b; + uint16_t x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c = apply_trans(opts->pre_trans, cube); + DfsData dd = { + .m = 0, + .niss = false, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(c)) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.check, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = MAX(opts->min_moves, step.check(c)); + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + solve_dfs(c, step, opts, &dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans_aux[opts->pre_trans], node->alg); + + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, alg->move[i], alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_cachedata(CacheData *cd, bool printalgs) +{ + CacheNode *j; + uint64_t i, s, maxa = 0, maxs = 0, n = 0, empty = 0; + + if (!cd->generated) + generate_ctable(cd); + + for (i = 0; i < cd->maxind0; i++) { + if (cd->ctable[i] == NULL) { + empty++; + continue; + } + + for (j = cd->ctable[i], s = 0; + j != NULL; j = j->next, n++, s++) + if (printalgs) + print_alg(j->sol, false); + + if (s > maxs) { + maxs = s; + maxa = i; + } + } + + printf("Cache data %s\n", cd->filename); + printf("Length: %d\n", cd->len); + printf("Main table cells: %lu (of which %lu empty)\n", i, empty); + printf("%lu solutions cached ", n); + printf("(most crowded cell: %lu with %lu)\n", maxa, maxs); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + generate_ptable(pd); + + for (i = 0; i < pd->size; i++) + a[PTABLEVAL(pd->ptable, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + + +Alg * +trans_alg(Trans i) +{ + return trans_algs[i]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_trans_aux(); + init_trans(); +} + diff --git a/old/2021-06-17-cachedata/cube.h b/old/2021-06-17-cachedata/cube.h @@ -0,0 +1,79 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +/* Constants and macros *****************************************************/ + +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) +#define PTABLESIZE(n) ((n+1) / 2) +#define PTABLEVAL(t,i) (((i)%2) ? (t[(i)/2] / 16) : (t[(i)/2] % 16)) + +/* Type definitions **********************************************************/ + +#include "cubetypes.h" + +/* Public functions **********************************************************/ + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Center center_at(Cube cube, Center c); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Corner corner_at(Cube cube, Corner c); +Edge edge_at(Cube cube, Edge e); +uint64_t epos_dependent_cube(Cube cube); +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +int piece_orientation(Cube cube, int piece, char *orientation); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * trans_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +void generate_ctable(CacheData *cd); +void generate_ptable(PruneData *pd); +void print_cachedata(CacheData *cd, bool printalgs); +void print_ptable(PruneData *pd); + +void init(); + +#endif + diff --git a/old/2021-06-17-cachedata/cubetypes.h b/old/2021-06-17-cachedata/cubetypes.h @@ -0,0 +1,232 @@ +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct cachedata CacheData; +typedef struct cachenode CacheNode; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; + +typedef int (*Checker)(Cube); +typedef uint64_t (*Indexer)(Cube); +typedef bool (*Moveset)(Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, /* R|L */ +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +cachedata +{ + char * filename; + CacheNode ** ctable; + bool generated; + uint64_t maxind0; + int len; + int nind; + Indexer index[20]; /* Should be enough */ + Checker check; + Moveset moveset; +}; + +struct +cachenode +{ + uint64_t * indexval; + Alg * sol; + CacheNode * next; +}; + +struct +cube +{ + uint16_t epose; + uint16_t eposs; + uint16_t eposm; + uint16_t eofb; + uint16_t eorl; + uint16_t eoud; + uint16_t cp; + uint16_t coud; + uint16_t cofb; + uint16_t corl; + uint16_t cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +dfsdata +{ + int d; + int m; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + uint8_t * reached; + bool generated; + uint64_t n; + uint64_t size; + Indexer index; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; + Trans pre_trans; +}; + +struct +step +{ + Checker check; + Checker ready; + Moveset moveset; + CacheData * cd; +}; diff --git a/old/2021-06-17-cachedata/main.c b/old/2021-06-17-cachedata/main.c @@ -0,0 +1,63 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 3; /*, nrand = 100000, sum1, sum2;*/ + + Step *stps[20] = {&eofb_HTM, &corners_URF, &optimal_HTM}; + char sss[30][30] = {"EO on F/B", "Corners URF", "Optimal solution"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true, + .pre_trans = uf + }; + + init(); + +/* + print_ptable(&pd_corners_HTM); + + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + for (i = 0; i < nrand; i++) { + sum1 += optimal_HTM.check(random_cube()); + sum2 += corners_HTM.check(random_cube()); + } + printf("Average optimal pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners pruning: %lf\n", ((double)sum2) / ((double) nrand)); +*/ + + print_cachedata(&cd_optimal_HTM_6, false); + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + for (i = 0; i < ns; i++) { + if (stps[i]->check == NULL) + fprintf(stderr, "Check function for step %d is null\n", i); + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free(algo); + } + + return 0; +} + diff --git a/old/2021-06-17-cachedata/steps.c b/old/2021-06-17-cachedata/steps.c @@ -0,0 +1,327 @@ +#include "steps.h" + +/* Check functions ***********************************************************/ + +static int check_nothing(Cube cube); +static int check_eofb_HTM(Cube cube); +static int check_coud_HTM(Cube cube); +static int check_coud_URF(Cube cube); +static int check_corners_HTM(Cube cube); +static int check_corners_URF(Cube cube); +static int check_edges_HTM(Cube cube); +static int check_drud_HTM(Cube cube); +static int check_optimal_HTM(Cube cube); + + +/* Index functions ***********************************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_ep(Cube cube); +static uint64_t index_drud(Cube cube); + + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .check = check_eofb_HTM, + .ready = check_nothing, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .check = check_coud_HTM, + .ready = check_nothing, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .check = check_coud_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .check = check_corners_HTM, + .ready = check_nothing, + .moveset = moveset_HTM +}; + +Step +corners_URF = { + .check = check_corners_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +edges_HTM = { + .check = check_edges_HTM, + .ready = check_nothing, + .moveset = moveset_HTM +}; + +Step +drud_HTM = { + .check = check_drud_HTM, + .ready = check_nothing, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .check = check_optimal_HTM, + .ready = check_nothing, + .moveset = moveset_HTM, + .cd = &cd_optimal_HTM_6 +}; + + +/* Cache solutions ***********************************************************/ + +CacheData +cd_optimal_HTM_6 = { + .filename = "cd_optimal_HTM_6", + .maxind0 = POW3TO7 * FACTORIAL8, + .len = 6, + .nind = 3, + .index = { index_corners, index_eofb, index_ep }, + .moveset = moveset_HTM +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .size = POW2TO11, + .index = index_eofb, + .moveset = moveset_HTM +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .size = POW3TO7, + .index = index_coud, + .moveset = moveset_HTM +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .size = POW3TO7 * FACTORIAL8, + .index = index_corners, + .moveset = moveset_HTM +}; + +PruneData +pd_ep_HTM = { + .filename = "ptable_ep_HTM", + .size = FACTORIAL12, + .index = index_ep, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .size = POW2TO11 * POW3TO7 * BINOM12ON4, + .index = index_drud, + .moveset = moveset_HTM +}; + + +/* Check functions implementation ********************************************/ + +static int +check_nothing(Cube cube) +{ + /* At least we check that it is admissible */ + return is_admissible(cube); +} + +static int +check_eofb_HTM(Cube cube) +{ + if (!pd_eofb_HTM.generated) + generate_ptable(&pd_eofb_HTM); + + return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb); +} + +static int +check_coud_HTM(Cube cube) +{ + if (!pd_coud_HTM.generated) + generate_ptable(&pd_coud_HTM); + + return PTABLEVAL(pd_coud_HTM.ptable, cube.coud); +} + +static int +check_coud_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + int ud = check_coud_HTM(cube); + int rl = check_coud_HTM(apply_move(z, cube)); + int fb = check_coud_HTM(apply_move(x, cube)); + + return MIN(ud, MIN(rl, fb)); +} + +static int +check_corners_HTM(Cube cube) +{ + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + + return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); +} + +static int +check_corners_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) + ret = MIN(ret,check_corners_HTM(apply_alg(trans_alg(i),cube))); + + return ret; +} + +static int +check_edges_HTM(Cube cube) +{ + int ret = 0; + + if (!pd_ep_HTM.generated) + generate_ptable(&pd_ep_HTM); + + ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube))); + ret = MAX(ret, check_eofb_HTM(cube)); + ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube))); + ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube))); + + return ret; +} + +static int +check_drud_HTM(Cube cube) +{ + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + + return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); +} + +static int +check_optimal_HTM(Cube cube) +{ + int dr1, dr2, dr3, drmax, cor; /*ep;*/ + + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + /* + *if (!pd_ep_HTM.generated) + * generate_ptable(&pd_ep_HTM); + */ + + dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); + dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube))); + dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube))); + + drmax = MAX(dr1, MAX(dr2, dr3)); + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + drmax++; + + cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); + /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */ + + /*return MAX(drmax, MAX(ep, cor));*/ + if (drmax == 0 && cor == 0) + return is_solved(cube, false) ? 0 : 1; + return MAX(drmax, cor); +} + + +/* Index functions implementation ********************************************/ + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_ep(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eposs; + b = (cube.epose % FACTORIAL4) + epos_dependent_cube(cube)*FACTORIAL4; + c = cube.eposm % FACTORIAL4; + + b *= FACTORIAL4 * BINOM12ON4; + c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4; + + return a + b + c; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + diff --git a/old/2021-06-17-cachedata/steps.h b/old/2021-06-17-cachedata/steps.h @@ -0,0 +1,34 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +/* Steps *********************************************************************/ + +extern Step eofb_HTM; +extern Step coud_HTM; +extern Step coud_URF; +extern Step corners_HTM; +extern Step corners_URF; +extern Step edges_HTM; +extern Step drud_HTM; +extern Step optimal_HTM; + +/* Cache solutions ***********************************************************/ + +extern CacheData cd_optimal_HTM_6; + +/* Pruning tables ************************************************************/ + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_ep_HTM; +extern PruneData pd_drud_HTM; + +/* Movesets ******************************************************************/ + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); + +#endif diff --git a/old/2021-06-23-realizing-bad-tables/HERE b/old/2021-06-23-realizing-bad-tables/HERE @@ -0,0 +1 @@ +Here you can also find some tables data that I have removed, like ep and tripleeo diff --git a/old/2021-06-23-realizing-bad-tables/cube.c b/old/2021-06-23-realizing-bad-tables/cube.c @@ -0,0 +1,2844 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static int array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent_pos(int pos1, int pos2); +static int epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(int epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Checker f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static bool ptable_has_reached(PruneData *pd, uint64_t ind); +static void ptable_set_reached(PruneData *pd, uint64_t ind); +static void ptable_update(PruneData *pd, uint64_t ind, int m); +static void realloc_alg(Alg *alg, int n); +static bool read_algset_file(AlgSet *as); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_algset_file(AlgSet *as); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_ttables_file(); + +static void init_auxtables(); +static void init_cphtr_cosets(); +static void init_cphtr_cosets_bfs(int i, int c); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_trans(); +static void init_trans_aux(); + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static int cphtr_cosets[FACTORIAL8]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * trans_algs[NROTATIONS]; + + +/* Local functions implementation ********************************************/ + +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +/* +static int +cphtr_cp(int cp) +{ + int i, a[8]; + + index_to_perm(cp, 8, a); + + for (i = 0; i < 8; i++) + if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR) + a[i] = 0; + else + a[i] = 1; + + swap(&a[1], &a[5]); + swap(&a[3], &a[7]); + + return subset_to_index(a, 8, 4); +} +*/ + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; (m=moves[i]) != NULLMOVE && dd->sols->len < maxnsol; i++) { + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + + if (dd->current_alg->len == 0 || + (s.check(apply_move(inverse_move(l1), (Cube){0}), 1))) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.check(c, dd->d - dd->current_alg->len); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent_pos(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + uint64_t ind = pd->index(c); + int oldval = ptableval(pd, ind); + + if (oldval < dd->m || ptable_has_reached(pd, ind) || pd->n == pd->size) + return; + + ptable_set_reached(pd, ind); + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, ind, dd->m); + return; + } + + genptable_dfs_branch(c, pd, dd); +} + +static void +genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd) +{ + Move i, move, l1 = dd->last1, l2 = dd->last2; + + dd->m++; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + + genptable_dfs(apply_move(move, c), pd, dd); + + dd->last2 = l2; + dd->last1 = l1; + } + } + + dd->m--; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Checker f, Move *r) +{ + Cube c; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + c = apply_move(i, (Cube){0}); + if (f != NULL && f(c, 1)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static bool +ptable_has_reached(PruneData *pd, uint64_t ind) +{ + return (ind % 2) ? pd->reached[ind/2] / 16 : pd->reached[ind/2] % 16; +} + +static void +ptable_set_reached(PruneData *pd, uint64_t ind) +{ + uint8_t oldval2 = pd->reached[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->reached[ind/2] = (ind % 2) ? 16 + other : 16*other + 1; +} + +static void +ptable_update(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_algset_file(AlgSet *as) +{ + return false; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + Alg *inv; + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Cube ret; + + if (r != mirror) { + ret = apply_alg_generic(trans_algs[r], c, f, true); + inv = on_inverse(trans_algs[r]); + ret = apply_alg_generic(inv, ret, f, true); + free_alg(inv); + } else { + ret = move_via_arrays(&ma, (Cube){0}, f); + ret = compose_filtered(c, ret, f); + ret = move_via_arrays(&ma, ret, f); + } + + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_algset_file(AlgSet *as) +{ + return false; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[mirror] = mirror; +} + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the cosets for cphtr. + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0; + + for (i = 0; i < FACTORIAL8; i++) + cphtr_cosets[i] = -1; + + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_cosets[i] == -1) + init_cphtr_cosets_bfs(i, c++); +} + +static void +init_cphtr_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + jj = cp_mtable[moves[k]][next[j]]; + if (cphtr_cosets[jj] == -1) { + cphtr_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_trans() { + Cube aux, cube, c[3]; + CubeArray epcp; + int eparr[12], eoarr[12]; + int cparr[8], coarr[8]; + int i; + unsigned int ui; + bool b1, b2, b3; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + if (i == mirror) + cube = (Cube){0}; + else + cube = apply_alg(trans_algs[i], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + if (m == mirror) { + memcpy(eparr, ep_mirror, 12 * sizeof(int)); + memcpy(cparr, cp_mirror, 8 * sizeof(int)); + } else { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(trans_algs[m], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m == mirror) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + if (m == mirror) { + b1 = (mi >= U && mi <= Bw3); + b2 = (mi >= S && mi <= E3); + b3 = (mi >= x && mi <= z3); + if (b1 || b2 || b3) + moves_ttable[m][mi] = + inverse_move_aux[mi]; + else + moves_ttable[m][mi] = mi; + + if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3) + moves_ttable[m][mi] += 3; + if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3) + moves_ttable[m][mi] -= 3; + } else { + aux = apply_trans(m, apply_move(mi,(Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move( + inverse_move_aux[move], aux); + if (is_solved(cube, false)) + moves_ttable[m][mi] = move; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + trans_algs[uf] = new_alg(""); + trans_algs[ur] = new_alg("y"); + trans_algs[ub] = new_alg("y2"); + trans_algs[ul] = new_alg("y3"); + + trans_algs[df] = new_alg("z2"); + trans_algs[dr] = new_alg("y z2"); + trans_algs[db] = new_alg("x2"); + trans_algs[dl] = new_alg("y3 z2"); + + trans_algs[rf] = new_alg("z3"); + trans_algs[rd] = new_alg("z3 y"); + trans_algs[rb] = new_alg("z3 y2"); + trans_algs[ru] = new_alg("z3 y3"); + + trans_algs[lf] = new_alg("z"); + trans_algs[ld] = new_alg("z y3"); + trans_algs[lb] = new_alg("z y2"); + trans_algs[lu] = new_alg("z y"); + + trans_algs[fu] = new_alg("x y2"); + trans_algs[fr] = new_alg("x y"); + trans_algs[fd] = new_alg("x"); + trans_algs[fl] = new_alg("x y3"); + + trans_algs[bu] = new_alg("x3"); + trans_algs[br] = new_alg("x3 y"); + trans_algs[bd] = new_alg("x3 y2"); + trans_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + +Cube +apply_trans(Trans t, Cube cube) +{ + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +uint64_t +cphtr(Cube cube) +{ + return cphtr_cosets[cube.cp]; +} + +uint64_t +epos_dependent(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genalgset(AlgSet *as) +{ + uint64_t i; + + if (as->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + as->table = malloc(as->size * sizeof(AlgList *)); + + if (read_algset_file(as)) { + as->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", as->filename); + + for (i = 0; i < as->size; i++) { + as->table[i] = solve(as->antindex(i), as->step, &as->opts); + fprintf(stderr, "Generated %lu / %lu cases\n", i, as->size); + } + + if (!write_algset_file(as)) + fprintf(stderr, "Error writing algset file\n"); + + as->generated = true; +} + +void +genptable(PruneData *pd) +{ + uint64_t j; + DfsData dd = { + .m = 0, + .last1 = NULLMOVE, + .last2 = NULLMOVE + }; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->size; j++) + ptable_update(pd, j, 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + pd->reached = malloc(ptablesize(pd) * sizeof(uint8_t)); + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) { + memset(pd->reached, 0, ptablesize(pd)*sizeof(uint8_t)); + genptable_dfs((Cube){0}, pd, &dd); + fprintf(stderr, "Depth %d completed, generated %lu/%lu\n", + dd.d, pd->n, pd->size); + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; + free(pd->reached); +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + Trans i; + + if (reorient) + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(trans_algs[i],cube), false)) + return true; + + return equal(cube, (Cube){0}); +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c = apply_trans(opts->pre_trans, cube); + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && step.ready(c, 1) != 0) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.check, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(c, step, opts, &dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans_aux[opts->pre_trans], node->alg); + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, alg->move[i], alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->size; i++) + a[ptableval(pd, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->size + 1) / 2; +} + +int +ptableval(PruneData *pd, uint64_t ind) +{ + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + + +Alg * +trans_alg(Trans i) +{ + return trans_algs[i]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_cphtr_cosets(); + init_trans_aux(); + init_trans(); +} + diff --git a/old/2021-06-23-realizing-bad-tables/cube.h b/old/2021-06-23-realizing-bad-tables/cube.h @@ -0,0 +1,88 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +/* Constants and macros *****************************************************/ + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) + +/* Type definitions **********************************************************/ + +#include "cubetypes.h" + +/* Public functions **********************************************************/ + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +uint64_t cphtr(Cube cube); +uint64_t epos_dependent(Cube cube); +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * trans_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +void genalgset(AlgSet *as); +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, uint64_t ind); + +void init(); + +#endif + diff --git a/old/2021-06-23-realizing-bad-tables/cubetypes.h b/old/2021-06-23-realizing-bad-tables/cubetypes.h @@ -0,0 +1,224 @@ +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct algset AlgSet; +typedef struct block Block; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef int (*Checker) (Cube, int); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, /* R|L */ +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + uint8_t * reached; + bool generated; + uint64_t n; + uint64_t size; + Indexer index; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; + Trans pre_trans; +}; + +struct +step +{ + Checker check; + Checker ready; + Moveset moveset; +}; + +/* TODO: I put it here because it needs the definition of Step, sort it out */ +struct +algset +{ + char * filename; + AlgList ** table; + bool generated; + uint64_t size; + Indexer index; + AntiIndexer antindex; + Step step; + SolveOptions opts; +}; diff --git a/old/2021-06-23-realizing-bad-tables/main.c b/old/2021-06-23-realizing-bad-tables/main.c @@ -0,0 +1,70 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 3; +/* int nrand = 10000, sum1, sum2, sum3;*/ + + Step *stps[20] = {&corners_HTM, &eofb_HTM, &optimal_HTM}; + char sss[30][30] = {"Corners", "EO on F/B", "Optimal solution"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true, + .pre_trans = uf + }; + + init(); + + print_ptable(&pd_corners_HTM); + +/* + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + sum3 = 0; + for (i = 0; i < nrand; i++) { + cube = random_cube(); + sum1 += corners_HTM.check(cube, 20); + sum2 += cornershtr_HTM.check(cube, 20); + sum3 += optimal_HTM.check(cube, 20); + } + printf("Average corners pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand)); + printf("Average optimal pruning: %lf\n", ((double)sum3) / ((double) nrand)); +*/ + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + print_cube(cube); + printf("Index: %lu\n", pd_cornershtr_HTM.index(cube)); + + for (i = 0; i < ns; i++) { + if (stps[i]->check == NULL) + fprintf(stderr, "Check function for step %d is null\n", i); + printf("Check: %d\n", stps[i]->check(cube, 20)); + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free_alg(algo); + } + + return 0; +} + diff --git a/old/2021-06-23-realizing-bad-tables/steps.c b/old/2021-06-23-realizing-bad-tables/steps.c @@ -0,0 +1,486 @@ +#include "steps.h" + +/* Check functions ***********************************************************/ + +static int check_nothing(Cube cube, int target); +static int check_centers(Cube cube, int target); +static int check_eofb_HTM(Cube cube, int target); +static int check_coud_HTM(Cube cube, int target); +static int check_coud_URF(Cube cube, int target); +static int check_corners_HTM(Cube cube, int target); +static int check_cornershtr_HTM(Cube cube, int target); +static int check_corners_URF(Cube cube, int target); +static int check_cornershtr_URF(Cube cube, int target); +static int check_ep_HTM(Cube cube, int target); +static int check_edges_HTM(Cube cube, int target); +static int check_drud_HTM(Cube cube, int target); +static int check_optimal_HTM(Cube cube, int target); +static int check_corners6eo_HTM(Cube cube, int target); +static int check_tripleeo_HTM(Cube cube, int target); + + +/* Index functions ***********************************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_ep(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_corners6eo(Cube cube); +static uint64_t index_tripleeo(Cube cube); + + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .check = check_eofb_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .check = check_coud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .check = check_coud_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .check = check_corners_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_HTM = { + .check = check_cornershtr_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_URF = { + .check = check_cornershtr_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_URF = { + .check = check_corners_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +edges_HTM = { + .check = check_edges_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +drud_HTM = { + .check = check_drud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .check = check_optimal_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +tripleeo_HTM = { + .check = check_tripleeo_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + + +/* Blocks ********************************************************************/ + +Block +block_223dl = { + .edge = { [DF] = 1, [DL] = 1, [DB] = 1, [FL] = 1, [BL] = 1 }, + .corner = { [DFL] = 1, [DBL] = 1 }, + .center = { [D_center] = 1, [L_center] = 1 } +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .size = POW2TO11, + .index = index_eofb, + .moveset = moveset_HTM +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .size = POW3TO7, + .index = index_coud, + .moveset = moveset_HTM +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "ptable_cornershtr_HTM", + .size = POW3TO7 * BINOM8ON4 * 6, /*TODO: check */ + .index = index_cornershtr, + .moveset = moveset_HTM +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .size = POW3TO7 * FACTORIAL8, + .index = index_corners, + .moveset = moveset_HTM +}; + +PruneData +pd_ep_HTM = { + .filename = "ptable_ep_HTM", + .size = FACTORIAL12, + .index = index_ep, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .size = POW2TO11 * POW3TO7 * BINOM12ON4, + .index = index_drud, + .moveset = moveset_HTM +}; + +PruneData +pd_corners6eo_HTM = { + .filename = "ptable_corners6eo_HTM", + .size = POW2TO6 * POW3TO7 * FACTORIAL8, + .index = index_corners6eo, + .moveset = moveset_HTM +}; + +PruneData +pd_tripleeo_HTM = { + .filename = "ptable_tripleeo_HTM", + .size = BINOM12ON4 * BINOM8ON4 * POW2TO11, + .index = index_tripleeo, + .moveset = moveset_HTM +}; + + +/* Alg sets ******************************************************************/ + +/* TODO: remove this (I am only keeping it as an example for future algesets) +AlgSet +as_ephtr_HTM = { + .filename = "algset_ephtr_HTM", + .size = FACTORIAL4 * FACTORIAL4 * FACTORIAL4, + .index = index_ephtr, + .antindex = index_to_cube_ephtr, + .step = (Step) { + .check = check_optimal_HTM, + .ready = check_htr_solved_corners_HTM, + .moveset = moveset_HTM + }, + .opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .max_solutions = 1, + .optimal_only = true, + .can_niss = false, + .feedback = true, + .pre_trans = uf + } +}; +*/ + + +/* Check functions implementation ********************************************/ + +static int +check_nothing(Cube cube, int target) +{ + return 0; +} + +static int +check_centers(Cube cube, int target) +{ + return (cube.cpos == 0) ? 0 : 1; +} + +static int +check_eofb_HTM(Cube cube, int target) +{ + if (!pd_eofb_HTM.generated) + genptable(&pd_eofb_HTM); + + return ptableval(&pd_eofb_HTM, cube.eofb); +} + +static int +check_coud_HTM(Cube cube, int target) +{ + if (!pd_coud_HTM.generated) + genptable(&pd_coud_HTM); + + return ptableval(&pd_coud_HTM, cube.coud); +} + +static int +check_coud_URF(Cube cube, int target) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + int ud = check_coud_HTM(cube, target); + int rl = check_coud_HTM(apply_move(z, cube), target); + int fb = check_coud_HTM(apply_move(x, cube), target); + + return MIN(ud, MIN(rl, fb)); +} + +static int +check_corners_HTM(Cube cube, int target) +{ + if (!pd_corners_HTM.generated) + genptable(&pd_corners_HTM); + + return ptableval(&pd_corners_HTM, index_corners(cube)); +} + +static int +check_cornershtr_HTM(Cube cube, int target) +{ + if (!pd_cornershtr_HTM.generated) + genptable(&pd_cornershtr_HTM); + + return ptableval(&pd_cornershtr_HTM, index_cornershtr(cube)); +} + +static int +check_cornershtr_URF(Cube cube, int target) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + c = check_cornershtr_HTM(apply_alg(trans_alg(i),cube), target); + ret = MIN(ret, c); + } + + return ret; +} + +static int +check_corners_URF(Cube cube, int target) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) + ret = MIN(ret, check_corners_HTM( + apply_alg(trans_alg(i), cube), target)); + + return ret; +} + +static int +check_ep_HTM(Cube cube, int target) +{ + if (!pd_ep_HTM.generated) + genptable(&pd_ep_HTM); + + return ptableval(&pd_ep_HTM, index_ep(cube)); +} + +static int +check_edges_HTM(Cube cube, int target) +{ + return MAX(check_ep_HTM(cube, target), + check_tripleeo_HTM(cube, target)); +} + +static int +check_drud_HTM(Cube cube, int target) +{ + if (!pd_drud_HTM.generated) + genptable(&pd_drud_HTM); + + return ptableval(&pd_drud_HTM, index_drud(cube)); +} + +static int +check_optimal_HTM(Cube cube, int target) +{ + int dr1, dr2, dr3, cor, ret; + Cube cube2, cube3; + + dr1 = check_drud_HTM(cube, target); + /*cor = check_corners6eo_HTM(cube, target);*/ + cor = check_corners_HTM(cube, target); + ret = MAX(dr1, cor); + + if (ret > target) + return ret; + + cube2 = apply_trans(rf, cube); + dr2 = check_drud_HTM(cube2, target); + /*cor = check_corners6eo_HTM(cube2, target); + ret = MAX(ret, MAX(dr2, cor));*/ + ret = MAX(ret, dr2); + + if (ret > target) + return ret; + + cube3 = apply_trans(bd, cube); + dr3 = check_drud_HTM(cube3, target); + /*cor = check_corners6eo_HTM(cube3, target);*/ + + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + /*ret = MAX(ret, MAX(dr3, cor));*/ + ret = MAX(ret, dr3); + + if (ret == 0) + return (!cube.epose && !cube.eposs && !cube.eposm) ? 0 : 6; + + return ret; +} + +static int +check_corners6eo_HTM(Cube cube, int target) +{ + if (!pd_corners6eo_HTM.generated) + genptable(&pd_corners6eo_HTM); + + return ptableval(&pd_corners6eo_HTM, index_corners6eo(cube)); +} + +static int +check_tripleeo_HTM(Cube cube, int target) +{ + if (!pd_tripleeo_HTM.generated) + genptable(&pd_tripleeo_HTM); + + return ptableval(&pd_tripleeo_HTM, index_tripleeo(cube)); +} + + +/* Index functions implementation ********************************************/ + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_ep(Cube cube) +{ + uint64_t a, b, c; + + a = cube.epose; + b = (cube.eposs % FACTORIAL4) + epos_dependent(cube)*FACTORIAL4; + c = cube.eposm % FACTORIAL4; + + b *= FACTORIAL4 * BINOM12ON4; + c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4; + + return a + b + c; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_corners6eo(Cube cube) +{ + return (cube.coud*FACTORIAL8 + cube.cp)*POW2TO6 + (cube.eofb%POW2TO6); +} + +static uint64_t +index_tripleeo(Cube cube) +{ + uint64_t ee, es; + + ee = cube.epose / FACTORIAL4; + es = epos_dependent(cube); + + return (ee * BINOM8ON4 + es) * POW2TO11 + cube.eofb; +} + + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + diff --git a/old/2021-06-23-realizing-bad-tables/steps.h b/old/2021-06-23-realizing-bad-tables/steps.h @@ -0,0 +1,42 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +/* Steps *********************************************************************/ + +extern Step eofb_HTM; +extern Step coud_HTM; +extern Step coud_URF; +extern Step corners_HTM; +extern Step cornershtr_HTM; +extern Step corners_URF; +extern Step cornershtr_URF; +extern Step edges_HTM; +extern Step drud_HTM; +extern Step optimal_HTM; +extern Step tripleeo_HTM; + +/* Blocks ********************************************************************/ + +extern Block block_223dl; + +/* Pruning tables ************************************************************/ + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_ep_HTM; +extern PruneData pd_drud_HTM; +extern PruneData pd_tripleeo_HTM; + +/* Alg sets ******************************************************************/ + + +/* Movesets ******************************************************************/ + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); + +#endif diff --git a/old/2021-06-23-uint16t/cube.c b/old/2021-06-23-uint16t/cube.c @@ -0,0 +1,2762 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static uint16_t array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent(int pos1, int pos2); +static uint16_t epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(uint16_t epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Checker f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static bool ptable_has_reached(PruneData *pd, uint64_t ind); +static void ptable_set_reached(PruneData *pd, uint64_t ind); +static void ptable_update(PruneData *pd, uint64_t ind, int m); +static void realloc_alg(Alg *alg, int n); +static bool read_algset_file(AlgSet *as); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_algset_file(AlgSet *as); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_ttables_file(); + +static void init_auxtables(); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_trans(); +static void init_trans_aux(); + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static uint16_t epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static uint16_t eo_ttable[NTRANS][POW2TO11]; +static uint16_t cp_ttable[NTRANS][FACTORIAL8]; +static uint16_t co_ttable[NTRANS][POW3TO7]; +static uint16_t cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static uint16_t epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static uint16_t eofb_mtable[NMOVES][POW2TO11]; +static uint16_t eorl_mtable[NMOVES][POW2TO11]; +static uint16_t eoud_mtable[NMOVES][POW2TO11]; +static uint16_t cp_mtable[NMOVES][FACTORIAL8]; +static uint16_t coud_mtable[NMOVES][POW3TO7]; +static uint16_t cofb_mtable[NMOVES][POW3TO7]; +static uint16_t corl_mtable[NMOVES][POW3TO7]; +static uint16_t cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * trans_algs[NROTATIONS]; + + +/* Local functions implementation ********************************************/ + +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static uint16_t +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; (m=moves[i]) != NULLMOVE && dd->sols->len < maxnsol; i++) { + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + + if (dd->current_alg->len == 0 || + (s.check(apply_move(inverse_move(l1), (Cube){0}), 1))) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.check(c, dd->d - dd->current_alg->len); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static uint16_t +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(uint16_t epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + uint64_t ind = pd->index(c); + int oldval = ptableval(pd, ind); + + if (oldval < dd->m || ptable_has_reached(pd, ind) || pd->n == pd->size) + return; + + ptable_set_reached(pd, ind); + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, ind, dd->m); + return; + } + + genptable_dfs_branch(c, pd, dd); +} + +static void +genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd) +{ + Move i, move, l1 = dd->last1, l2 = dd->last2; + + dd->m++; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + + genptable_dfs(apply_move(move, c), pd, dd); + + dd->last2 = l2; + dd->last1 = l1; + } + } + + dd->m--; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Checker f, Move *r) +{ + Cube c; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + c = apply_move(i, (Cube){0}); + if (f != NULL && f(c, 1)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static bool +ptable_has_reached(PruneData *pd, uint64_t ind) +{ + return ind % 2 ? pd->reached[ind/2] / 16 : pd->reached[ind/2] % 16; +} + +static void +ptable_set_reached(PruneData *pd, uint64_t ind) +{ + uint8_t oldval2 = pd->reached[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->reached[ind/2] = ind % 2 ? 16 + other : 16*other + 1; +} + +static void +ptable_update(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = ind % 2 ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = ind % 2 ? 16*n + other : 16*other + n; + pd->n++; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_algset_file(AlgSet *as) +{ + return false; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(uint16_t); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + Alg *inv; + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Cube ret; + + if (r != mirror) { + ret = apply_alg_generic(trans_algs[r], c, f, true); + inv = on_inverse(trans_algs[r]); + ret = apply_alg_generic(inv, ret, f, true); + free_alg(inv); + } else { + ret = move_via_arrays(&ma, (Cube){0}, f); + ret = compose_filtered(c, ret, f); + ret = move_via_arrays(&ma, ret, f); + } + + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_algset_file(AlgSet *as) +{ + return false; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(uint16_t); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(uint16_t); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj] = epos_dependent(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[mirror] = mirror; +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + uint16_t ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_trans() { + Cube aux, cube, c[3]; + CubeArray epcp; + int eparr[12], eoarr[12]; + int cparr[8], coarr[8]; + int i; + bool b1, b2, b3; + uint16_t ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + if (i == mirror) + cube = (Cube){0}; + else + cube = apply_alg(trans_algs[i], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + if (m == mirror) { + memcpy(eparr, ep_mirror, 12 * sizeof(int)); + memcpy(cparr, cp_mirror, 8 * sizeof(int)); + } else { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(trans_algs[m], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m == mirror) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + if (m == mirror) { + b1 = (mi >= U && mi <= Bw3); + b2 = (mi >= S && mi <= E3); + b3 = (mi >= x && mi <= z3); + if (b1 || b2 || b3) + moves_ttable[m][mi] = + inverse_move_aux[mi]; + else + moves_ttable[m][mi] = mi; + + if ((mi-1)/3==(R-1)/3 || (mi-1)/3==(Rw-1)/3) + moves_ttable[m][mi] += 3; + if ((mi-1)/3==(L-1)/3 || (mi-1)/3==(L2-1)/3) + moves_ttable[m][mi] -= 3; + } else { + aux = apply_trans(m, apply_move(mi,(Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move( + inverse_move_aux[move], aux); + if (is_solved(cube, false)) + moves_ttable[m][mi] = move; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + trans_algs[uf] = new_alg(""); + trans_algs[ur] = new_alg("y"); + trans_algs[ub] = new_alg("y2"); + trans_algs[ul] = new_alg("y3"); + + trans_algs[df] = new_alg("z2"); + trans_algs[dr] = new_alg("y z2"); + trans_algs[db] = new_alg("x2"); + trans_algs[dl] = new_alg("y3 z2"); + + trans_algs[rf] = new_alg("z3"); + trans_algs[rd] = new_alg("z3 y"); + trans_algs[rb] = new_alg("z3 y2"); + trans_algs[ru] = new_alg("z3 y3"); + + trans_algs[lf] = new_alg("z"); + trans_algs[ld] = new_alg("z y3"); + trans_algs[lb] = new_alg("z y2"); + trans_algs[lu] = new_alg("z y"); + + trans_algs[fu] = new_alg("x y2"); + trans_algs[fr] = new_alg("x y"); + trans_algs[fd] = new_alg("x"); + trans_algs[fl] = new_alg("x y3"); + + trans_algs[bu] = new_alg("x3"); + trans_algs[br] = new_alg("x3 y"); + trans_algs[bd] = new_alg("x3 y2"); + trans_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + +Cube +apply_trans(Trans t, Cube cube) +{ + uint16_t aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + uint16_t aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + uint16_t aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +uint64_t +epos_dependent_cube(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genalgset(AlgSet *as) +{ + uint64_t i; + + if (as->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + as->table = malloc(as->size * sizeof(AlgList *)); + + if (read_algset_file(as)) { + as->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", as->filename); + + for (i = 0; i < as->size; i++) { + as->table[i] = solve(as->antindex(i), as->step, &as->opts); + fprintf(stderr, "Generated %lu / %lu cases\n", i, as->size); + } + + if (!write_algset_file(as)) + fprintf(stderr, "Error writing algset file\n"); + + as->generated = true; +} + +void +genptable(PruneData *pd) +{ + uint64_t j; + DfsData dd = { + .m = 0, + .last1 = NULLMOVE, + .last2 = NULLMOVE + }; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->size; j++) + ptable_update(pd, j, 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + pd->reached = malloc(ptablesize(pd) * sizeof(uint8_t)); + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->size; dd.d++) { + memset(pd->reached, 0, ptablesize(pd)*sizeof(uint8_t)); + genptable_dfs((Cube){0}, pd, &dd); + fprintf(stderr, "Depth %d completed, generated %lu/%lu\n", + dd.d, pd->n, pd->size); + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; + free(pd->reached); +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + Trans i; + + if (reorient) + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(trans_algs[i],cube), false)) + return true; + + return equal(cube, (Cube){0}); +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b; + uint16_t x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c = apply_trans(opts->pre_trans, cube); + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && step.ready(c, 1) != 0) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.check, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(c, step, opts, &dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans_aux[opts->pre_trans], node->alg); + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, alg->move[i], alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->size; i++) + a[ptableval(pd, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->size + 1) / 2; +} + +int +ptableval(PruneData *pd, uint64_t ind) +{ + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + + +Alg * +trans_alg(Trans i) +{ + return trans_algs[i]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_trans_aux(); + init_trans(); +} + diff --git a/old/2021-06-23-uint16t/cube.h b/old/2021-06-23-uint16t/cube.h @@ -0,0 +1,87 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +/* Constants and macros *****************************************************/ + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS (mirror+1) +#define NROTATIONS (NTRANS-1) + +/* Type definitions **********************************************************/ + +#include "cubetypes.h" + +/* Public functions **********************************************************/ + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +uint64_t epos_dependent_cube(Cube cube); +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * trans_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +void genalgset(AlgSet *as); +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, uint64_t ind); + +void init(); + +#endif + diff --git a/old/2021-06-23-uint16t/cubetypes.h b/old/2021-06-23-uint16t/cubetypes.h @@ -0,0 +1,224 @@ +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct algset AlgSet; +typedef struct block Block; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef int (*Checker) (Cube, int); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + mirror, /* R|L */ +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +cube +{ + uint16_t epose; + uint16_t eposs; + uint16_t eposm; + uint16_t eofb; + uint16_t eorl; + uint16_t eoud; + uint16_t cp; + uint16_t coud; + uint16_t cofb; + uint16_t corl; + uint16_t cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + uint8_t * reached; + bool generated; + uint64_t n; + uint64_t size; + Indexer index; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; + Trans pre_trans; +}; + +struct +step +{ + Checker check; + Checker ready; + Moveset moveset; +}; + +/* TODO: I put it here because it needs the definition of Step, sort it out */ +struct +algset +{ + char * filename; + AlgList ** table; + bool generated; + uint64_t size; + Indexer index; + AntiIndexer antindex; + Step step; + SolveOptions opts; +}; diff --git a/old/2021-06-30-noreached/cube.c b/old/2021-06-30-noreached/cube.c @@ -0,0 +1,3394 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static Cube admissible_eos_from_eofbepos(Cube cube); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static int array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent_pos(int pos1, int pos2); +static int epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(int epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd); +static void gensym(SymData *sd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void init_auxtables(); +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_symdata(); +static void init_trans(); +static void init_trans_aux(); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Estimator f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void realloc_alg(Alg *alg, int n); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_symdata_file(SymData *sd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_symdata_file(SymData *sd); +static bool write_ttables_file(); + + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static Cube admissible_ee_aux[POW2TO11*BINOM12ON4]; +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * rotation_algs[NROTATIONS]; + + +/* Symmetry data for some coordinates ****************************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int n_all_symdata = 2; +static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 }; + +/* Coordinates and their implementation **************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11 +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4 +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7 +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8 +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6 +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4 +}; + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430 +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430 +}; + + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[index_coud(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[index_eofbepos(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + + +/* TODO: rename */ +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + return admissible_ee_aux[ind]; +} + +/* TODO: rename */ +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = anti_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +/* TODO: admissible eos and cos */ +static Cube +antindex_drud(uint64_t ind) +{ + Cube c = {0}; + + c.eofb = ind % POW2TO11; + c.coud = (ind / POW2TO11) % POW3TO7; + c.epose = (ind % (POW2TO11 * POW3TO7)) * FACTORIAL4; + + return c; +} + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/POW3TO7]; + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)]; + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + + +/* Checkers ******************************************************************/ + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/ +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Local functions implementation ********************************************/ + +/* TODO: this should be an anti index (maybe?) */ +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static Cube +admissible_eos_from_eofbepos(Cube cube) +{ + Edge e; + Cube ret; + CubeArray *arr = new_cubearray(cube, pf_all); + + memcpy(arr->eorl, arr->eofb, 12 * sizeof(int)); + memcpy(arr->eoud, arr->eofb, 12 * sizeof(int)); + + for (e = 0; e < 12; e++) { + if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) || + (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0)) + arr->eorl[e] = 1 - arr->eorl[e]; + if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) || + (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2)) + arr->eoud[e] = 1 - arr->eoud[e]; + } + + ret = arrays_to_cube(arr, pf_all); + free_cubearray(arr, pf_all); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +/* +static int +cphtr_cp(int cp) +{ + int i, a[8]; + + index_to_perm(cp, 8, a); + + for (i = 0; i < 8; i++) + if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR) + a[i] = 0; + else + a[i] = 1; + + swap(&a[1], &a[5]); + swap(&a[3], &a[7]); + + return subset_to_index(a, 8, 4); +} +*/ + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s.estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent_pos(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + int oldval = ptableval(pd, c); + + if (oldval < dd->m || pd->n == pd->coord->max || + (dd->m != 0 && pd->coord->check(c)) ) + return; + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, c, dd->m); + return; + } + + genptable_dfs_branch(c, pd, dd); +} + +static void +genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd) +{ + Move i, move, l1 = dd->last1, l2 = dd->last2; + + dd->m++; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + + genptable_dfs(apply_move(move, c), pd, dd); + + dd->last2 = l2; + dd->last1 = l1; + } + } + + dd->m--; +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Estimator f, Move *r) +{ + CubeTarget ct = { .target = 1 }; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + ct.cube = apply_move(i, (Cube){0}); + if (f != NULL && f(ct)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) { + k = (ui / POW2TO11) * 24; + c1 = admissible_ep((Cube){ .epose = k }, pf_e); + c1.eofb = ui % POW2TO11; + c1 = admissible_eos_from_eofbepos(c1); + admissible_ee_aux[ui] = c1; + } + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[uf_mirror] = uf_mirror; + inverse_trans_aux[ur_mirror] = ur_mirror; + inverse_trans_aux[ul_mirror] = ul_mirror; + inverse_trans_aux[ub_mirror] = ub_mirror; + + inverse_trans_aux[df_mirror] = df_mirror; + inverse_trans_aux[dr_mirror] = dl_mirror; + inverse_trans_aux[dl_mirror] = dr_mirror; + inverse_trans_aux[db_mirror] = db_mirror; + + inverse_trans_aux[rf_mirror] = rf_mirror; + inverse_trans_aux[rd_mirror] = br_mirror; + inverse_trans_aux[rb_mirror] = lb_mirror; + inverse_trans_aux[ru_mirror] = fl_mirror; + + inverse_trans_aux[lf_mirror] = lf_mirror; + inverse_trans_aux[ld_mirror] = bl_mirror; + inverse_trans_aux[lb_mirror] = rb_mirror; + inverse_trans_aux[lu_mirror] = fr_mirror; + + inverse_trans_aux[fu_mirror] = fu_mirror; + inverse_trans_aux[fr_mirror] = lu_mirror; + inverse_trans_aux[fd_mirror] = bu_mirror; + inverse_trans_aux[fl_mirror] = ru_mirror; + + inverse_trans_aux[bu_mirror] = fd_mirror; + inverse_trans_aux[br_mirror] = rd_mirror; + inverse_trans_aux[bd_mirror] = bd_mirror; + inverse_trans_aux[bl_mirror] = ld_mirror; +} + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + jj = cp_mtable[moves[k]][next[j]]; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_symdata() +{ + int i; + + for (i = 0; i < n_all_symdata; i++) + gensym(all_sd[i]); +} + +static void +init_trans() { + Cube aux, cube, mirr, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move_aux[move], aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube, false) || + is_solved(mirr, false)) + moves_ttable[m][mi] = move; + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + rotation_algs[uf] = new_alg(""); + rotation_algs[ur] = new_alg("y"); + rotation_algs[ub] = new_alg("y2"); + rotation_algs[ul] = new_alg("y3"); + + rotation_algs[df] = new_alg("z2"); + rotation_algs[dr] = new_alg("y z2"); + rotation_algs[db] = new_alg("x2"); + rotation_algs[dl] = new_alg("y3 z2"); + + rotation_algs[rf] = new_alg("z3"); + rotation_algs[rd] = new_alg("z3 y"); + rotation_algs[rb] = new_alg("z3 y2"); + rotation_algs[ru] = new_alg("z3 y3"); + + rotation_algs[lf] = new_alg("z"); + rotation_algs[ld] = new_alg("z y3"); + rotation_algs[lb] = new_alg("z y2"); + rotation_algs[lu] = new_alg("z y"); + + rotation_algs[fu] = new_alg("x y2"); + rotation_algs[fr] = new_alg("x y"); + rotation_algs[fd] = new_alg("x"); + rotation_algs[fl] = new_alg("x y3"); + + rotation_algs[bu] = new_alg("x3"); + rotation_algs[br] = new_alg("x3 y"); + rotation_algs[bd] = new_alg("x3 y2"); + rotation_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + + +Cube +apply_trans(Trans t, Cube cube) +{ + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +/*TODO maybe move the next two */ +uint64_t +cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +Cube +anti_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +uint64_t +epos_dependent(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genptable(PruneData *pd) +{ + Cube cube; + uint64_t j, oldn = 0; + DfsData dd = { + .m = 0, + .last1 = NULLMOVE, + .last2 = NULLMOVE + }; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update(pd, pd->coord->cube(j), 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->coord->max; dd.d++) { + for (j = 0; j < pd->coord->max; j++) { + cube = pd->coord->cube(j); + if (pd->coord->check(cube)) + genptable_dfs(cube, pd, &dd); + } + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + dd.d, pd->n-oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + int i; + + if (reorient) { + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube),false)) + return true; + return false; + } else { + return equal(cube, (Cube){0}); + } +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +/* TODO: clean pre_trans or put it back */ +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + /*AlgListNode *node;*/ + AlgList *sols = new_alglist(); + /*Cube c = apply_trans(opts->pre_trans, cube);*/ + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(cube)) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.estimate, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(cube, step, opts, &dd); + } + +/* + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(opts->pre_trans), node->alg); +*/ + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval(pd, pd->coord->cube(i))]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + uint64_t ind = pd->coord->index(cube); + + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + + +Alg * +rotation_alg(Trans i) +{ + return rotation_algs[i % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_cphtr_cosets(); + init_trans_aux(); + init_trans(); + init_symdata(); +} + diff --git a/old/2021-06-30-noreached/cube.h b/old/2021-06-30-noreached/cube.h @@ -0,0 +1,90 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "macros.h" +#include "cubetypes.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_drud; +extern Coordinate coord_coud_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_khuge; + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */ +Cube anti_cphtr(uint64_t ind); /*TODO also this */ +uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +bool check_centers(Cube cube); +bool check_corners(Cube cube); +bool check_cornershtr(Cube cube); +bool check_coud(Cube cube); +bool check_drud(Cube cube); +bool check_eofb(Cube cube); +bool check_eofbepos(Cube cube); +bool check_epose(Cube cube); +bool check_ep(Cube cube); +bool check_khuge(Cube cube); +bool check_nothing(Cube cube); + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * rotation_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +void init(); + +#endif + diff --git a/old/2021-06-30-noreached/cubetypes.h b/old/2021-06-30-noreached/cubetypes.h @@ -0,0 +1,250 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + Checker check; + uint64_t max; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; +}; + +struct +step +{ + Estimator estimate; + Checker ready; + Moveset moveset; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/old/2021-06-30-noreached/macros.h b/old/2021-06-30-noreached/macros.h @@ -0,0 +1,23 @@ +#ifndef MACROS_H +#define MACROS_H + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS 48 +#define NROTATIONS 24 + +#endif diff --git a/old/2021-06-30-noreached/main.c b/old/2021-06-30-noreached/main.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 2; +/* Move m;*/ +/* int nrand = 10000, sum1, sum2, sum3;*/ + + Step *stps[20] = {&drud_HTM, &optimal_HTM}; + char sss[30][30] = {"DR on UD", "Optimal solve"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true, + }; + + init(); + +/* + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + sum3 = 0; + for (i = 0; i < nrand; i++) { + cube = random_cube(); + sum1 += drud_HTM.check(cube, 20); + sum2 += optimal_HTM.check(cube, 20); + sum3 += cornershtreofb_HTM.check(cube, 20); + } + printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand)); + printf("Average corners htr + eofb pruning: %lf\n", ((double)sum3) / ((double) nrand)); +*/ + +/* + for (m = U; m <= B3; m++) { + printf("Class eofbepos after %d: ", m); + printf("%lu\n", coord_khuge.index(apply_move(m,(Cube){0}))); + } +*/ + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + print_alg(inverse_alg(algo), false); +/* + printf("After rb_mirror:\n"); + cube = apply_trans(rb_mirror, cube); + print_cube(cube); + printf("Going back:\n"); + cube = apply_trans(inverse_trans(rb_mirror), cube); +*/ + + print_cube(cube); + + for (i = 0; i < ns; i++) { + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free_alg(algo); + } + + return 0; +} + diff --git a/old/2021-06-30-noreached/steps.c b/old/2021-06-30-noreached/steps.c @@ -0,0 +1,307 @@ +#include "steps.h" + +/* Standard checkers (return lower bound) ************************************/ + +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .estimate = estimate_coud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .estimate = estimate_coud_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .estimate = estimate_corners_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_HTM = { + .estimate = estimate_cornershtr_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_URF = { + .estimate = estimate_cornershtr_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_URF = { + .estimate = estimate_corners_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +drud_HTM = { + .estimate = estimate_drud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "ptable_cornershtr_withcosets_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .coord = &coord_drud, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "ptable_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, +}; + +PruneData +pd_khuge_HTM = { + .filename = "ptable_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM +}; + + +/* Standard checkers (return lower bound) ************************************/ + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + if (!pd_eofb_HTM.generated) + genptable(&pd_eofb_HTM); + + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + if (!pd_coud_HTM.generated) + genptable(&pd_coud_HTM); + + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + if (!pd_corners_HTM.generated) + genptable(&pd_corners_HTM); + + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + if (!pd_cornershtr_HTM.generated) + genptable(&pd_cornershtr_HTM); + + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ +/* + if (!pd_drud_HTM.generated) + genptable(&pd_drud_HTM); + + return ptableval(&pd_drud_HTM, ct.cube); +*/ + + if (!pd_drud_sym16_HTM.generated) + genptable(&pd_drud_sym16_HTM); + + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +/* TODO: if lucky, remove this */ +/* +static int +estimate_optimal_HTM(CubeTarget ct) +{ + static Trans t1 = { .rot = rf, .mirror = false }; + static Trans t2 = { .rot = bd, .mirror = false }; + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + dr1 = estimate_drud_HTM(ct); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + ct.cube = apply_trans(t1, cube); + dr2 = estimate_drud_HTM(ct); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + ct.cube = apply_trans(t2, cube); + dr3 = estimate_drud_HTM(ct); + + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + ret = MAX(ret, dr3); + + if (ret == 0) + return check_ep(cube) ? 0 : 6; + + return ret; +} +*/ + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + static Trans t2 = rf, t3 = bd; + + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + if (!pd_khuge_HTM.generated) + genptable(&pd_khuge_HTM); + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(t2, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(t3, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + return MAX(ret, dr3); +} diff --git a/old/2021-06-30-noreached/steps.h b/old/2021-06-30-noreached/steps.h @@ -0,0 +1,24 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +extern Step eofb_HTM; +extern Step coud_HTM; +extern Step coud_URF; +extern Step corners_HTM; +extern Step cornershtr_HTM; +extern Step corners_URF; +extern Step cornershtr_URF; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_cornershtreofb_HTM; +extern PruneData pd_drud_HTM; +extern PruneData pd_khuge_HTM; + +#endif diff --git a/old/2021-06-30-reached/cube.c b/old/2021-06-30-reached/cube.c @@ -0,0 +1,3419 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static Cube admissible_eos_from_eofbepos(Cube cube); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static int array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent_pos(int pos1, int pos2); +static int epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(int epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd); +static void gensym(SymData *sd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void init_auxtables(); +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_symdata(); +static void init_trans(); +static void init_trans_aux(); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Estimator f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static bool ptable_has_reached(PruneData *pd, Cube cube); +static void ptable_set_reached(PruneData *pd, Cube cube); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void realloc_alg(Alg *alg, int n); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_symdata_file(SymData *sd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_symdata_file(SymData *sd); +static bool write_ttables_file(); + + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static Cube admissible_ee_aux[POW2TO11*BINOM12ON4]; +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * rotation_algs[NROTATIONS]; + + +/* Symmetry data for some coordinates ****************************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int n_all_symdata = 2; +static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 }; + +/* Coordinates and their implementation **************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11 +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4 +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7 +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8 +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6 +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4 +}; + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430 +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430 +}; + + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[index_coud(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[index_eofbepos(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + + +/* TODO: rename */ +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + return admissible_ee_aux[ind]; +} + +/* TODO: rename */ +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = anti_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +/* TODO: admissible eos and cos */ +static Cube +antindex_drud(uint64_t ind) +{ + Cube c = {0}; + + c.eofb = ind % POW2TO11; + c.coud = (ind / POW2TO11) % POW3TO7; + c.epose = (ind % (POW2TO11 * POW3TO7)) * FACTORIAL4; + + return c; +} + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/POW3TO7]; + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)]; + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + + +/* Checkers ******************************************************************/ + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/ +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Local functions implementation ********************************************/ + +/* TODO: this should be an anti index (maybe?) */ +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static Cube +admissible_eos_from_eofbepos(Cube cube) +{ + Edge e; + Cube ret; + CubeArray *arr = new_cubearray(cube, pf_all); + + memcpy(arr->eorl, arr->eofb, 12 * sizeof(int)); + memcpy(arr->eoud, arr->eofb, 12 * sizeof(int)); + + for (e = 0; e < 12; e++) { + if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) || + (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0)) + arr->eorl[e] = 1 - arr->eorl[e]; + if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) || + (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2)) + arr->eoud[e] = 1 - arr->eoud[e]; + } + + ret = arrays_to_cube(arr, pf_all); + free_cubearray(arr, pf_all); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +/* +static int +cphtr_cp(int cp) +{ + int i, a[8]; + + index_to_perm(cp, 8, a); + + for (i = 0; i < 8; i++) + if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR) + a[i] = 0; + else + a[i] = 1; + + swap(&a[1], &a[5]); + swap(&a[3], &a[7]); + + return subset_to_index(a, 8, 4); +} +*/ + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s.estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent_pos(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + int oldval = ptableval(pd, c); + + if (oldval < dd->m || ptable_has_reached(pd, c) || + pd->n == pd->coord->max || (dd->m != 0 && pd->coord->check(c)) ) + return; + + /*ptable_set_reached(pd, c);*/ + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, c, dd->m); + return; + } + + genptable_dfs_branch(c, pd, dd); +} + +static void +genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd) +{ + Move i, move, l1 = dd->last1, l2 = dd->last2; + + dd->m++; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + + genptable_dfs(apply_move(move, c), pd, dd); + + dd->last2 = l2; + dd->last1 = l1; + } + } + + dd->m--; +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Estimator f, Move *r) +{ + CubeTarget ct = { .target = 1 }; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + ct.cube = apply_move(i, (Cube){0}); + if (f != NULL && f(ct)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static bool +ptable_has_reached(PruneData *pd, Cube cube) +{ + uint64_t ind = pd->coord->index(cube); + + return (ind % 2) ? pd->reached[ind/2] / 16 : pd->reached[ind/2] % 16; +} + +static void +ptable_set_reached(PruneData *pd, Cube cube) +{ + uint64_t ind = pd->coord->index(cube); + uint8_t oldval2 = pd->reached[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->reached[ind/2] = (ind % 2) ? 16 + other : 16*other + 1; +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) { + k = (ui / POW2TO11) * 24; + c1 = admissible_ep((Cube){ .epose = k }, pf_e); + c1.eofb = ui % POW2TO11; + c1 = admissible_eos_from_eofbepos(c1); + admissible_ee_aux[ui] = c1; + } + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[uf_mirror] = uf_mirror; + inverse_trans_aux[ur_mirror] = ur_mirror; + inverse_trans_aux[ul_mirror] = ul_mirror; + inverse_trans_aux[ub_mirror] = ub_mirror; + + inverse_trans_aux[df_mirror] = df_mirror; + inverse_trans_aux[dr_mirror] = dl_mirror; + inverse_trans_aux[dl_mirror] = dr_mirror; + inverse_trans_aux[db_mirror] = db_mirror; + + inverse_trans_aux[rf_mirror] = rf_mirror; + inverse_trans_aux[rd_mirror] = br_mirror; + inverse_trans_aux[rb_mirror] = lb_mirror; + inverse_trans_aux[ru_mirror] = fl_mirror; + + inverse_trans_aux[lf_mirror] = lf_mirror; + inverse_trans_aux[ld_mirror] = bl_mirror; + inverse_trans_aux[lb_mirror] = rb_mirror; + inverse_trans_aux[lu_mirror] = fr_mirror; + + inverse_trans_aux[fu_mirror] = fu_mirror; + inverse_trans_aux[fr_mirror] = lu_mirror; + inverse_trans_aux[fd_mirror] = bu_mirror; + inverse_trans_aux[fl_mirror] = ru_mirror; + + inverse_trans_aux[bu_mirror] = fd_mirror; + inverse_trans_aux[br_mirror] = rd_mirror; + inverse_trans_aux[bd_mirror] = bd_mirror; + inverse_trans_aux[bl_mirror] = ld_mirror; +} + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + jj = cp_mtable[moves[k]][next[j]]; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_symdata() +{ + int i; + + for (i = 0; i < n_all_symdata; i++) + gensym(all_sd[i]); +} + +static void +init_trans() { + Cube aux, cube, mirr, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move_aux[move], aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube, false) || + is_solved(mirr, false)) + moves_ttable[m][mi] = move; + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + rotation_algs[uf] = new_alg(""); + rotation_algs[ur] = new_alg("y"); + rotation_algs[ub] = new_alg("y2"); + rotation_algs[ul] = new_alg("y3"); + + rotation_algs[df] = new_alg("z2"); + rotation_algs[dr] = new_alg("y z2"); + rotation_algs[db] = new_alg("x2"); + rotation_algs[dl] = new_alg("y3 z2"); + + rotation_algs[rf] = new_alg("z3"); + rotation_algs[rd] = new_alg("z3 y"); + rotation_algs[rb] = new_alg("z3 y2"); + rotation_algs[ru] = new_alg("z3 y3"); + + rotation_algs[lf] = new_alg("z"); + rotation_algs[ld] = new_alg("z y3"); + rotation_algs[lb] = new_alg("z y2"); + rotation_algs[lu] = new_alg("z y"); + + rotation_algs[fu] = new_alg("x y2"); + rotation_algs[fr] = new_alg("x y"); + rotation_algs[fd] = new_alg("x"); + rotation_algs[fl] = new_alg("x y3"); + + rotation_algs[bu] = new_alg("x3"); + rotation_algs[br] = new_alg("x3 y"); + rotation_algs[bd] = new_alg("x3 y2"); + rotation_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + + +Cube +apply_trans(Trans t, Cube cube) +{ + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +/*TODO maybe move the next two */ +uint64_t +cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +Cube +anti_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +uint64_t +epos_dependent(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genptable(PruneData *pd) +{ + Cube cube; + uint64_t j, oldn = 0; + DfsData dd = { + .m = 0, + .last1 = NULLMOVE, + .last2 = NULLMOVE + }; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update(pd, pd->coord->cube(j), 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + pd->reached = malloc(ptablesize(pd) * sizeof(uint8_t)); + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->coord->max; dd.d++) { + memset(pd->reached, 0, ptablesize(pd)*sizeof(uint8_t)); + for (j = 0; j < pd->coord->max; j++) { + cube = pd->coord->cube(j); + if (pd->coord->check(cube)) + genptable_dfs(cube, pd, &dd); + } + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + dd.d, pd->n-oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; + free(pd->reached); +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + int i; + + if (reorient) { + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube),false)) + return true; + return false; + } else { + return equal(cube, (Cube){0}); + } +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +/* TODO: clean pre_trans or put it back */ +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + /*AlgListNode *node;*/ + AlgList *sols = new_alglist(); + /*Cube c = apply_trans(opts->pre_trans, cube);*/ + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(cube)) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.estimate, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(cube, step, opts, &dd); + } + +/* + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(opts->pre_trans), node->alg); +*/ + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval(pd, pd->coord->cube(i))]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + uint64_t ind = pd->coord->index(cube); + + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + + +Alg * +rotation_alg(Trans i) +{ + return rotation_algs[i % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_cphtr_cosets(); + init_trans_aux(); + init_trans(); + init_symdata(); +} + diff --git a/old/2021-06-30-reached/cube.h b/old/2021-06-30-reached/cube.h @@ -0,0 +1,90 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "macros.h" +#include "cubetypes.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_drud; +extern Coordinate coord_coud_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_khuge; + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */ +Cube anti_cphtr(uint64_t ind); /*TODO also this */ +uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +bool check_centers(Cube cube); +bool check_corners(Cube cube); +bool check_cornershtr(Cube cube); +bool check_coud(Cube cube); +bool check_drud(Cube cube); +bool check_eofb(Cube cube); +bool check_eofbepos(Cube cube); +bool check_epose(Cube cube); +bool check_ep(Cube cube); +bool check_khuge(Cube cube); +bool check_nothing(Cube cube); + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * rotation_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +void init(); + +#endif + diff --git a/old/2021-06-30-reached/cubetypes.h b/old/2021-06-30-reached/cubetypes.h @@ -0,0 +1,251 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + Checker check; + uint64_t max; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + uint8_t * reached; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; +}; + +struct +step +{ + Estimator estimate; + Checker ready; + Moveset moveset; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/old/2021-06-30-reached/macros.h b/old/2021-06-30-reached/macros.h @@ -0,0 +1,23 @@ +#ifndef MACROS_H +#define MACROS_H + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS 48 +#define NROTATIONS 24 + +#endif diff --git a/old/2021-06-30-reached/main.c b/old/2021-06-30-reached/main.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 2; +/* Move m;*/ +/* int nrand = 10000, sum1, sum2, sum3;*/ + + Step *stps[20] = {&drud_HTM, &optimal_HTM}; + char sss[30][30] = {"DR on UD", "Optimal solve"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true, + }; + + init(); + +/* + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + sum3 = 0; + for (i = 0; i < nrand; i++) { + cube = random_cube(); + sum1 += drud_HTM.check(cube, 20); + sum2 += optimal_HTM.check(cube, 20); + sum3 += cornershtreofb_HTM.check(cube, 20); + } + printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand)); + printf("Average corners htr + eofb pruning: %lf\n", ((double)sum3) / ((double) nrand)); +*/ + +/* + for (m = U; m <= B3; m++) { + printf("Class eofbepos after %d: ", m); + printf("%lu\n", coord_khuge.index(apply_move(m,(Cube){0}))); + } +*/ + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + print_alg(inverse_alg(algo), false); +/* + printf("After rb_mirror:\n"); + cube = apply_trans(rb_mirror, cube); + print_cube(cube); + printf("Going back:\n"); + cube = apply_trans(inverse_trans(rb_mirror), cube); +*/ + + print_cube(cube); + + for (i = 0; i < ns; i++) { + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free_alg(algo); + } + + return 0; +} + diff --git a/old/2021-06-30-reached/steps.c b/old/2021-06-30-reached/steps.c @@ -0,0 +1,307 @@ +#include "steps.h" + +/* Standard checkers (return lower bound) ************************************/ + +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .estimate = estimate_coud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .estimate = estimate_coud_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .estimate = estimate_corners_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_HTM = { + .estimate = estimate_cornershtr_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_URF = { + .estimate = estimate_cornershtr_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_URF = { + .estimate = estimate_corners_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +drud_HTM = { + .estimate = estimate_drud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "ptable_cornershtr_withcosets_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .coord = &coord_drud, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "pd_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, +}; + +PruneData +pd_khuge_HTM = { + .filename = "pd_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM +}; + + +/* Standard checkers (return lower bound) ************************************/ + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + if (!pd_eofb_HTM.generated) + genptable(&pd_eofb_HTM); + + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + if (!pd_coud_HTM.generated) + genptable(&pd_coud_HTM); + + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + if (!pd_corners_HTM.generated) + genptable(&pd_corners_HTM); + + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + if (!pd_cornershtr_HTM.generated) + genptable(&pd_cornershtr_HTM); + + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ +/* + if (!pd_drud_HTM.generated) + genptable(&pd_drud_HTM); + + return ptableval(&pd_drud_HTM, ct.cube); +*/ + + if (!pd_drud_sym16_HTM.generated) + genptable(&pd_drud_sym16_HTM); + + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +/* TODO: if lucky, remove this */ +/* +static int +estimate_optimal_HTM(CubeTarget ct) +{ + static Trans t1 = { .rot = rf, .mirror = false }; + static Trans t2 = { .rot = bd, .mirror = false }; + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + dr1 = estimate_drud_HTM(ct); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + ct.cube = apply_trans(t1, cube); + dr2 = estimate_drud_HTM(ct); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + ct.cube = apply_trans(t2, cube); + dr3 = estimate_drud_HTM(ct); + + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + ret = MAX(ret, dr3); + + if (ret == 0) + return check_ep(cube) ? 0 : 6; + + return ret; +} +*/ + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + static Trans t2 = rf, t3 = bd; + + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + if (!pd_khuge_HTM.generated) + genptable(&pd_khuge_HTM); + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(t2, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(t3, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + return MAX(ret, dr3); +} diff --git a/old/2021-06-30-reached/steps.h b/old/2021-06-30-reached/steps.h @@ -0,0 +1,24 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +extern Step eofb_HTM; +extern Step coud_HTM; +extern Step coud_URF; +extern Step corners_HTM; +extern Step cornershtr_HTM; +extern Step corners_URF; +extern Step cornershtr_URF; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_cornershtreofb_HTM; +extern PruneData pd_drud_HTM; +extern PruneData pd_khuge_HTM; + +#endif diff --git a/old/2021-07-02-genptable-dfs/cube.c b/old/2021-07-02-genptable-dfs/cube.c @@ -0,0 +1,3394 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static Cube admissible_eos_from_eofbepos(Cube cube); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static int array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent_pos(int pos1, int pos2); +static int epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(int epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_dfs(Cube c, PruneData *pd, DfsData *dd); +static void genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd); +static void gensym(SymData *sd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void init_auxtables(); +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_symdata(); +static void init_trans(); +static void init_trans_aux(); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Estimator f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void realloc_alg(Alg *alg, int n); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_symdata_file(SymData *sd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_symdata_file(SymData *sd); +static bool write_ttables_file(); + + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static Cube admissible_ee_aux[POW2TO11*BINOM12ON4]; +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * rotation_algs[NROTATIONS]; + + +/* Symmetry data for some coordinates ****************************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int n_all_symdata = 2; +static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 }; + +/* Coordinates and their implementation **************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11 +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4 +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7 +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8 +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6 +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4 +}; + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430 +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430 +}; + + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[index_coud(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[index_eofbepos(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + + +/* TODO: rename */ +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + return admissible_ee_aux[ind]; +} + +/* TODO: rename */ +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = anti_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +/* TODO: admissible eos and cos */ +static Cube +antindex_drud(uint64_t ind) +{ + Cube c = {0}; + + c.eofb = ind % POW2TO11; + c.coud = (ind / POW2TO11) % POW3TO7; + c.epose = (ind % (POW2TO11 * POW3TO7)) * FACTORIAL4; + + return c; +} + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/POW3TO7]; + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)]; + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + + +/* Checkers ******************************************************************/ + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/ +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Local functions implementation ********************************************/ + +/* TODO: this should be an anti index (maybe?) */ +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static Cube +admissible_eos_from_eofbepos(Cube cube) +{ + Edge e; + Cube ret; + CubeArray *arr = new_cubearray(cube, pf_all); + + memcpy(arr->eorl, arr->eofb, 12 * sizeof(int)); + memcpy(arr->eoud, arr->eofb, 12 * sizeof(int)); + + for (e = 0; e < 12; e++) { + if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) || + (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0)) + arr->eorl[e] = 1 - arr->eorl[e]; + if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) || + (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2)) + arr->eoud[e] = 1 - arr->eoud[e]; + } + + ret = arrays_to_cube(arr, pf_all); + free_cubearray(arr, pf_all); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +/* +static int +cphtr_cp(int cp) +{ + int i, a[8]; + + index_to_perm(cp, 8, a); + + for (i = 0; i < 8; i++) + if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR) + a[i] = 0; + else + a[i] = 1; + + swap(&a[1], &a[5]); + swap(&a[3], &a[7]); + + return subset_to_index(a, 8, 4); +} +*/ + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s.estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent_pos(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_dfs(Cube c, PruneData *pd, DfsData *dd) +{ + int oldval = ptableval(pd, c); + + if (oldval < dd->m || pd->n == pd->coord->max || + (dd->m != 0 && pd->coord->check(c)) ) + return; + + if (dd->m == dd->d) { + if (dd->m < oldval) + ptable_update(pd, c, dd->m); + return; + } + + genptable_dfs_branch(c, pd, dd); +} + +static void +genptable_dfs_branch(Cube c, PruneData *pd, DfsData *dd) +{ + Move i, move, l1 = dd->last1, l2 = dd->last2; + + dd->m++; + + for (i = 0; dd->sorted_moves[i] != NULLMOVE; i++) { + move = dd->sorted_moves[i]; + if (allowed_next(move, dd)) { + dd->last2 = dd->last1; + dd->last1 = move; + + genptable_dfs(apply_move(move, c), pd, dd); + + dd->last2 = l2; + dd->last1 = l1; + } + } + + dd->m--; +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Estimator f, Move *r) +{ + CubeTarget ct = { .target = 1 }; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + ct.cube = apply_move(i, (Cube){0}); + if (f != NULL && f(ct)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) { + k = (ui / POW2TO11) * 24; + c1 = admissible_ep((Cube){ .epose = k }, pf_e); + c1.eofb = ui % POW2TO11; + c1 = admissible_eos_from_eofbepos(c1); + admissible_ee_aux[ui] = c1; + } + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[uf_mirror] = uf_mirror; + inverse_trans_aux[ur_mirror] = ur_mirror; + inverse_trans_aux[ul_mirror] = ul_mirror; + inverse_trans_aux[ub_mirror] = ub_mirror; + + inverse_trans_aux[df_mirror] = df_mirror; + inverse_trans_aux[dr_mirror] = dl_mirror; + inverse_trans_aux[dl_mirror] = dr_mirror; + inverse_trans_aux[db_mirror] = db_mirror; + + inverse_trans_aux[rf_mirror] = rf_mirror; + inverse_trans_aux[rd_mirror] = br_mirror; + inverse_trans_aux[rb_mirror] = lb_mirror; + inverse_trans_aux[ru_mirror] = fl_mirror; + + inverse_trans_aux[lf_mirror] = lf_mirror; + inverse_trans_aux[ld_mirror] = bl_mirror; + inverse_trans_aux[lb_mirror] = rb_mirror; + inverse_trans_aux[lu_mirror] = fr_mirror; + + inverse_trans_aux[fu_mirror] = fu_mirror; + inverse_trans_aux[fr_mirror] = lu_mirror; + inverse_trans_aux[fd_mirror] = bu_mirror; + inverse_trans_aux[fl_mirror] = ru_mirror; + + inverse_trans_aux[bu_mirror] = fd_mirror; + inverse_trans_aux[br_mirror] = rd_mirror; + inverse_trans_aux[bd_mirror] = bd_mirror; + inverse_trans_aux[bl_mirror] = ld_mirror; +} + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + jj = cp_mtable[moves[k]][next[j]]; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_symdata() +{ + int i; + + for (i = 0; i < n_all_symdata; i++) + gensym(all_sd[i]); +} + +static void +init_trans() { + Cube aux, cube, mirr, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move_aux[move], aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube, false) || + is_solved(mirr, false)) + moves_ttable[m][mi] = move; + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + rotation_algs[uf] = new_alg(""); + rotation_algs[ur] = new_alg("y"); + rotation_algs[ub] = new_alg("y2"); + rotation_algs[ul] = new_alg("y3"); + + rotation_algs[df] = new_alg("z2"); + rotation_algs[dr] = new_alg("y z2"); + rotation_algs[db] = new_alg("x2"); + rotation_algs[dl] = new_alg("y3 z2"); + + rotation_algs[rf] = new_alg("z3"); + rotation_algs[rd] = new_alg("z3 y"); + rotation_algs[rb] = new_alg("z3 y2"); + rotation_algs[ru] = new_alg("z3 y3"); + + rotation_algs[lf] = new_alg("z"); + rotation_algs[ld] = new_alg("z y3"); + rotation_algs[lb] = new_alg("z y2"); + rotation_algs[lu] = new_alg("z y"); + + rotation_algs[fu] = new_alg("x y2"); + rotation_algs[fr] = new_alg("x y"); + rotation_algs[fd] = new_alg("x"); + rotation_algs[fl] = new_alg("x y3"); + + rotation_algs[bu] = new_alg("x3"); + rotation_algs[br] = new_alg("x3 y"); + rotation_algs[bd] = new_alg("x3 y2"); + rotation_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + + +Cube +apply_trans(Trans t, Cube cube) +{ + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +/*TODO maybe move the next two */ +uint64_t +cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +Cube +anti_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +uint64_t +epos_dependent(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genptable(PruneData *pd) +{ + Cube cube; + uint64_t j, oldn = 0; + DfsData dd = { + .m = 0, + .last1 = NULLMOVE, + .last2 = NULLMOVE + }; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update(pd, pd->coord->cube(j), 15); + + moveset_to_list(pd->moveset, NULL, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = 0, pd->n = 0; dd.d < 15 && pd->n < pd->coord->max; dd.d++) { + for (j = 0; j < pd->coord->max; j++) { + cube = pd->coord->cube(j); + if (pd->coord->check(cube)) + genptable_dfs(cube, pd, &dd); + } + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + dd.d, pd->n-oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + int i; + + if (reorient) { + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube),false)) + return true; + return false; + } else { + return equal(cube, (Cube){0}); + } +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +/* TODO: clean pre_trans or put it back */ +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + /*AlgListNode *node;*/ + AlgList *sols = new_alglist(); + /*Cube c = apply_trans(opts->pre_trans, cube);*/ + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(cube)) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.estimate, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(cube, step, opts, &dd); + } + +/* + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(opts->pre_trans), node->alg); +*/ + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval(pd, pd->coord->cube(i))]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + uint64_t ind = pd->coord->index(cube); + + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + + +Alg * +rotation_alg(Trans i) +{ + return rotation_algs[i % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_cphtr_cosets(); + init_trans_aux(); + init_trans(); + init_symdata(); +} + diff --git a/old/2021-07-02-genptable-dfs/cube.h b/old/2021-07-02-genptable-dfs/cube.h @@ -0,0 +1,90 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "macros.h" +#include "cubetypes.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_drud; +extern Coordinate coord_coud_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_khuge; + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */ +Cube anti_cphtr(uint64_t ind); /*TODO also this */ +uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +bool check_centers(Cube cube); +bool check_corners(Cube cube); +bool check_cornershtr(Cube cube); +bool check_coud(Cube cube); +bool check_drud(Cube cube); +bool check_eofb(Cube cube); +bool check_eofbepos(Cube cube); +bool check_epose(Cube cube); +bool check_ep(Cube cube); +bool check_khuge(Cube cube); +bool check_nothing(Cube cube); + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * rotation_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +void init(); + +#endif + diff --git a/old/2021-07-02-genptable-dfs/cubetypes.h b/old/2021-07-02-genptable-dfs/cubetypes.h @@ -0,0 +1,250 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + Checker check; + uint64_t max; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; +}; + +struct +step +{ + Estimator estimate; + Checker ready; + Moveset moveset; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/old/2021-07-02-genptable-dfs/macros.h b/old/2021-07-02-genptable-dfs/macros.h @@ -0,0 +1,23 @@ +#ifndef MACROS_H +#define MACROS_H + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS 48 +#define NROTATIONS 24 + +#endif diff --git a/old/2021-07-02-genptable-dfs/main.c b/old/2021-07-02-genptable-dfs/main.c @@ -0,0 +1,80 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 2; +/* Move m;*/ +/* int nrand = 10000, sum1, sum2, sum3;*/ + + Step *stps[20] = {&drud_HTM, &optimal_HTM}; + char sss[30][30] = {"DR on UD", "Optimal solve"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true, + }; + + init(); + +/* + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + sum3 = 0; + for (i = 0; i < nrand; i++) { + cube = random_cube(); + sum1 += drud_HTM.check(cube, 20); + sum2 += optimal_HTM.check(cube, 20); + sum3 += cornershtreofb_HTM.check(cube, 20); + } + printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand)); + printf("Average corners htr + eofb pruning: %lf\n", ((double)sum3) / ((double) nrand)); +*/ + +/* + for (m = U; m <= B3; m++) { + printf("Class eofbepos after %d: ", m); + printf("%lu\n", coord_khuge.index(apply_move(m,(Cube){0}))); + } +*/ + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + print_alg(inverse_alg(algo), false); +/* + printf("After rb_mirror:\n"); + cube = apply_trans(rb_mirror, cube); + print_cube(cube); + printf("Going back:\n"); + cube = apply_trans(inverse_trans(rb_mirror), cube); +*/ + + print_cube(cube); + + for (i = 0; i < ns; i++) { + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free_alg(algo); + } + + return 0; +} + diff --git a/old/2021-07-02-genptable-dfs/steps.c b/old/2021-07-02-genptable-dfs/steps.c @@ -0,0 +1,307 @@ +#include "steps.h" + +/* Standard checkers (return lower bound) ************************************/ + +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .estimate = estimate_coud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .estimate = estimate_coud_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .estimate = estimate_corners_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_HTM = { + .estimate = estimate_cornershtr_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_URF = { + .estimate = estimate_cornershtr_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_URF = { + .estimate = estimate_corners_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +drud_HTM = { + .estimate = estimate_drud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "ptable_cornershtr_withcosets_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .coord = &coord_drud, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "ptable_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, +}; + +PruneData +pd_khuge_HTM = { + .filename = "ptable_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM +}; + + +/* Standard checkers (return lower bound) ************************************/ + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + if (!pd_eofb_HTM.generated) + genptable(&pd_eofb_HTM); + + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + if (!pd_coud_HTM.generated) + genptable(&pd_coud_HTM); + + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + if (!pd_corners_HTM.generated) + genptable(&pd_corners_HTM); + + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + if (!pd_cornershtr_HTM.generated) + genptable(&pd_cornershtr_HTM); + + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ +/* + if (!pd_drud_HTM.generated) + genptable(&pd_drud_HTM); + + return ptableval(&pd_drud_HTM, ct.cube); +*/ + + if (!pd_drud_sym16_HTM.generated) + genptable(&pd_drud_sym16_HTM); + + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +/* TODO: if lucky, remove this */ +/* +static int +estimate_optimal_HTM(CubeTarget ct) +{ + static Trans t1 = { .rot = rf, .mirror = false }; + static Trans t2 = { .rot = bd, .mirror = false }; + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + dr1 = estimate_drud_HTM(ct); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + ct.cube = apply_trans(t1, cube); + dr2 = estimate_drud_HTM(ct); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + ct.cube = apply_trans(t2, cube); + dr3 = estimate_drud_HTM(ct); + + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + ret = MAX(ret, dr3); + + if (ret == 0) + return check_ep(cube) ? 0 : 6; + + return ret; +} +*/ + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + static Trans t2 = rf, t3 = bd; + + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + if (!pd_khuge_HTM.generated) + genptable(&pd_khuge_HTM); + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(t2, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(t3, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + return MAX(ret, dr3); +} diff --git a/old/2021-07-02-genptable-dfs/steps.h b/old/2021-07-02-genptable-dfs/steps.h @@ -0,0 +1,24 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +extern Step eofb_HTM; +extern Step coud_HTM; +extern Step coud_URF; +extern Step corners_HTM; +extern Step cornershtr_HTM; +extern Step corners_URF; +extern Step cornershtr_URF; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_cornershtreofb_HTM; +extern PruneData pd_drud_HTM; +extern PruneData pd_khuge_HTM; + +#endif diff --git a/old/2021-07-15-almostbeforerefactor/README b/old/2021-07-15-almostbeforerefactor/README @@ -0,0 +1,3 @@ +I started refactorig (splitting code into more files) before realizing I had no backup of +the fast version that works. +Take some stuff from alg.h back into cube.h, remove alg.* ad it should be ok. diff --git a/old/2021-07-15-almostbeforerefactor/alg.c b/old/2021-07-15-almostbeforerefactor/alg.c @@ -0,0 +1,3388 @@ +#include "alg.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static Cube admissible_eos_from_eofbepos(Cube cube); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static int array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent_pos(int pos1, int pos2); +static int epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(int epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_bfs(PruneData *pd, int d, Move *ms); +static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m); +static void gensym(SymData *sd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void init_auxtables(); +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_symdata(); +static void init_trans(); +static void init_trans_aux(); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Estimator f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static void ptable_update(PruneData *pd, Cube cube, int m); +static int ptableval_index(PruneData *pd, uint64_t ind); +static void realloc_alg(Alg *alg, int n); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_symdata_file(SymData *sd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_symdata_file(SymData *sd); +static bool write_ttables_file(); + + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static Cube admissible_ee_aux[POW2TO11*BINOM12ON4]; +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * rotation_algs[NROTATIONS]; + + +/* Symmetry data for some coordinates ****************************************/ + +Trans +trans_group_trivial[1] = { uf }; + +Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int n_all_symdata = 2; +static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 }; + +/* Coordinates and their implementation **************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11 +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4 +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7 +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8 +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6 +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4 +}; + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430 +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430 +}; + + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[index_coud(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[index_eofbepos(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + + +/* TODO: rename */ +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + return admissible_ee_aux[ind]; +} + +/* TODO: rename */ +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = anti_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +/* TODO: admissible eos and cos */ +/* DONE: temporary fix, make it better */ +static Cube +antindex_drud(uint64_t ind) +{ + uint64_t epos, eofb; + Cube c; + + eofb = ind % POW2TO11; + epos = ind / (POW2TO11 * POW3TO7); + c = admissible_ee_aux[eofb + POW2TO11 * epos]; + + c.coud = (ind / POW2TO11) % POW3TO7; + c.corl = c.coud; + c.cofb = c.coud; + + return c; +} + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/POW3TO7]; + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)]; + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + + +/* Checkers ******************************************************************/ + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/ +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Local functions implementation ********************************************/ + +/* TODO: this should be an anti index (maybe?) */ +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static Cube +admissible_eos_from_eofbepos(Cube cube) +{ + Edge e; + Cube ret; + CubeArray *arr = new_cubearray(cube, pf_all); + + memcpy(arr->eorl, arr->eofb, 12 * sizeof(int)); + memcpy(arr->eoud, arr->eofb, 12 * sizeof(int)); + + for (e = 0; e < 12; e++) { + if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) || + (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0)) + arr->eorl[e] = 1 - arr->eorl[e]; + if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) || + (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2)) + arr->eoud[e] = 1 - arr->eoud[e]; + } + + ret = arrays_to_cube(arr, pf_all); + free_cubearray(arr, pf_all); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +/* +static int +cphtr_cp(int cp) +{ + int i, a[8]; + + index_to_perm(cp, 8, a); + + for (i = 0; i < 8; i++) + if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR) + a[i] = 0; + else + a[i] = 1; + + swap(&a[1], &a[5]); + swap(&a[3], &a[7]); + + return subset_to_index(a, 8, 4); +} +*/ + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s.estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent_pos(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_bfs(PruneData *pd, int d, Move *ms) +{ + uint64_t i; + + for (i = 0; i < pd->coord->max; i++) + if (ptableval_index(pd, i) == d) + genptable_branch(pd, i, d, ms); +} + +static void +genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms) +{ + int i, j; + Cube cc, c; + + + for (i = 0; i < pd->ntrans; i++) { + c = apply_trans(pd->trans[i], pd->coord->cube(ind)); + for (j = 0; ms[j] != NULLMOVE; j++) { + cc = apply_move(ms[j], c); + if (ptableval(pd, cc) > d+1) + ptable_update(pd, cc, d+1); + } + } +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Estimator f, Move *r) +{ + CubeTarget ct = { .target = 1 }; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + ct.cube = apply_move(i, (Cube){0}); + if (f != NULL && f(ct)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +static int +ptableval_index(PruneData *pd, uint64_t ind) +{ + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) { + k = (ui / POW2TO11) * 24; + c1 = admissible_ep((Cube){ .epose = k }, pf_e); + c1.eofb = ui % POW2TO11; + c1 = admissible_eos_from_eofbepos(c1); + admissible_ee_aux[ui] = c1; + } + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[uf_mirror] = uf_mirror; + inverse_trans_aux[ur_mirror] = ur_mirror; + inverse_trans_aux[ul_mirror] = ul_mirror; + inverse_trans_aux[ub_mirror] = ub_mirror; + + inverse_trans_aux[df_mirror] = df_mirror; + inverse_trans_aux[dr_mirror] = dl_mirror; + inverse_trans_aux[dl_mirror] = dr_mirror; + inverse_trans_aux[db_mirror] = db_mirror; + + inverse_trans_aux[rf_mirror] = rf_mirror; + inverse_trans_aux[rd_mirror] = br_mirror; + inverse_trans_aux[rb_mirror] = lb_mirror; + inverse_trans_aux[ru_mirror] = fl_mirror; + + inverse_trans_aux[lf_mirror] = lf_mirror; + inverse_trans_aux[ld_mirror] = bl_mirror; + inverse_trans_aux[lb_mirror] = rb_mirror; + inverse_trans_aux[lu_mirror] = fr_mirror; + + inverse_trans_aux[fu_mirror] = fu_mirror; + inverse_trans_aux[fr_mirror] = lu_mirror; + inverse_trans_aux[fd_mirror] = bu_mirror; + inverse_trans_aux[fl_mirror] = ru_mirror; + + inverse_trans_aux[bu_mirror] = fd_mirror; + inverse_trans_aux[br_mirror] = rd_mirror; + inverse_trans_aux[bd_mirror] = bd_mirror; + inverse_trans_aux[bl_mirror] = ld_mirror; +} + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + jj = cp_mtable[moves[k]][next[j]]; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_symdata() +{ + int i; + + for (i = 0; i < n_all_symdata; i++) + gensym(all_sd[i]); +} + +static void +init_trans() { + Cube aux, cube, mirr, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move_aux[move], aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube, false) || + is_solved(mirr, false)) + moves_ttable[m][mi] = move; + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + rotation_algs[uf] = new_alg(""); + rotation_algs[ur] = new_alg("y"); + rotation_algs[ub] = new_alg("y2"); + rotation_algs[ul] = new_alg("y3"); + + rotation_algs[df] = new_alg("z2"); + rotation_algs[dr] = new_alg("y z2"); + rotation_algs[db] = new_alg("x2"); + rotation_algs[dl] = new_alg("y3 z2"); + + rotation_algs[rf] = new_alg("z3"); + rotation_algs[rd] = new_alg("z3 y"); + rotation_algs[rb] = new_alg("z3 y2"); + rotation_algs[ru] = new_alg("z3 y3"); + + rotation_algs[lf] = new_alg("z"); + rotation_algs[ld] = new_alg("z y3"); + rotation_algs[lb] = new_alg("z y2"); + rotation_algs[lu] = new_alg("z y"); + + rotation_algs[fu] = new_alg("x y2"); + rotation_algs[fr] = new_alg("x y"); + rotation_algs[fd] = new_alg("x"); + rotation_algs[fl] = new_alg("x y3"); + + rotation_algs[bu] = new_alg("x3"); + rotation_algs[br] = new_alg("x3 y"); + rotation_algs[bd] = new_alg("x3 y2"); + rotation_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + + +Cube +apply_trans(Trans t, Cube cube) +{ + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +/*TODO maybe move the next two */ +uint64_t +cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +Cube +anti_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +uint64_t +epos_dependent(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genptable(PruneData *pd) +{ + Move ms[NMOVES]; + int d; + uint64_t j, oldn = 0; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + moveset_to_list(pd->moveset, NULL, ms); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update(pd, pd->coord->cube(j), 15); + + /*TODO: change, set to 0 for every solved state (might be more than 1)*/ + ptable_update(pd, (Cube){0}, 0); + pd->n = 1; + + for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { + genptable_bfs(pd, d, ms); + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + d, pd->n - oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + int i; + + if (reorient) { + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube),false)) + return true; + return false; + } else { + return equal(cube, (Cube){0}); + } +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +/* TODO: clean pre_trans or put it back */ +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + /*AlgListNode *node;*/ + AlgList *sols = new_alglist(); + /*Cube c = apply_trans(opts->pre_trans, cube);*/ + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(cube)) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.estimate, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(cube, step, opts, &dd); + } + +/* + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(opts->pre_trans), node->alg); +*/ + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval(pd, pd->coord->cube(i))]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + return ptableval_index(pd, pd->coord->index(cube)); +} + +Alg * +rotation_alg(Trans i) +{ + return rotation_algs[i % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_cphtr_cosets(); + init_trans_aux(); + init_trans(); + init_symdata(); +} + diff --git a/old/2021-07-15-almostbeforerefactor/alg.h b/old/2021-07-15-almostbeforerefactor/alg.h @@ -0,0 +1,27 @@ +#ifndef ALG_H +#define ALG_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "macros.h" +#include "cubetypes.h" + +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Move inverse_move(Move m); +bool moveset_HTM(Move m); +bool moveset_URF(Move m); +Alg * new_alg(char *str); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); +Alg * rotation_alg(Trans i); +void transform_alg(Trans t, Alg *alg); + +#endif + diff --git a/old/2021-07-15-almostbeforerefactor/cube.c b/old/2021-07-15-almostbeforerefactor/cube.c @@ -0,0 +1,3391 @@ +#include "cube.h" + +/* TODO: remove! */ +#include "steps.h" + +/* Local functions **********************************************************/ + +static Cube admissible_ep(Cube cube, PieceFilter f); +static Cube admissible_eos_from_eofbepos(Cube cube); +static bool allowed_next(Move move, DfsData *dd); +static void append_alg(AlgList *l, Alg *alg); +static void append_move(Alg *alg, Move m, bool inverse); +static Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +static void apply_permutation(int *perm, int *set, int n); +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static int array_ep_to_epos(int *ep, int *eps_solved); +static Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +static int binomial(int n, int k); +static Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +static void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +static void dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_check_solved(SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd); +static int digit_array_to_int(int *a, int n, int b); +static int edge_slice(Edge e); /* E=0, S=1, M=2 */ +static int epos_dependent_pos(int pos1, int pos2); +static int epos_from_arrays(int *epos, int *ep); +static void epos_to_partial_ep(int epos, int *ep, int *ss); +static int factorial(int n); +static void free_alglistnode(AlgListNode *aln); +static void free_cubearray(CubeArray *arr, PieceFilter f); +static void genptable_bfs(PruneData *pd, int d, Move *ms); +static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m); +static void gensym(SymData *sd); +static void index_to_perm(int p, int n, int *r); +static void index_to_subset(int s, int n, int k, int *r); +static void init_auxtables(); +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_environment(); +static void init_moves(); +static void init_moves_aux(); +static void init_strings(); +static void init_symdata(); +static void init_trans(); +static void init_trans_aux(); +static void int_to_digit_array(int a, int b, int n, int *r); +static void int_to_sum_zero_array(int x, int b, int n, int *a); +static int invert_digits(int a, int b, int n); +static bool is_perm(int *a, int n); +static bool is_subset(int *a, int n, int k); +static Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +static void movelist_to_position(Move *movelist, int *position); +static void moveset_to_list(Moveset ms, Estimator f, Move *r); +static AlgList * new_alglist(); +static CubeArray * new_cubearray(Cube cube, PieceFilter f); +static int perm_sign(int *a, int n); +static int perm_to_index(int *a, int n); +static int powint(int a, int b); +static void ptable_update(PruneData *pd, Cube cube, int m); +static int ptableval_index(PruneData *pd, uint64_t ind); +static void realloc_alg(Alg *alg, int n); +static bool read_mtables_file(); +static bool read_ptable_file(PruneData *pd); +static bool read_symdata_file(SymData *sd); +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static int subset_to_index(int *a, int n, int k); +static void sum_arrays_mod(int *src, int *dst, int n, int m); +static void swap(int *a, int *b); +static bool write_mtables_file(); +static bool write_ptable_file(PruneData *pd); +static bool write_symdata_file(SymData *sd); +static bool write_ttables_file(); + + +/* All sorts of useful costants and tables **********************************/ + +static char * tabledir; + +static PieceFilter pf_all; +static PieceFilter pf_4val; +static PieceFilter pf_epcp; +static PieceFilter pf_cpos; +static PieceFilter pf_cp; +static PieceFilter pf_ep; +static PieceFilter pf_e; +static PieceFilter pf_s; +static PieceFilter pf_m; +static PieceFilter pf_eo; +static PieceFilter pf_co; + +static int epe_solved[4]; +static int eps_solved[4]; +static int epm_solved[4]; + +static char move_string[NMOVES][7]; +static char edge_string[12][7]; +static char corner_string[8][7]; +static char center_string[6][7]; + +static Cube admissible_ee_aux[POW2TO11*BINOM12ON4]; +static bool commute[NMOVES][NMOVES]; +static bool possible_next[NMOVES][NMOVES][NMOVES]; +static Move inverse_move_aux[NMOVES]; +static Trans inverse_trans_aux[NTRANS]; +static int epos_dependent_aux[BINOM12ON4][BINOM12ON4]; +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static Center what_center_at_aux[FACTORIAL6][6]; +static Corner what_corner_at_aux[FACTORIAL8][8]; +static int what_orientation_last_corner_aux[POW3TO7]; +static int what_orientation_last_edge_aux[POW2TO11]; +static Center where_is_center_aux[FACTORIAL6][6]; +static Corner where_is_corner_aux[FACTORIAL8][8]; +static Edge where_is_edge_aux[3][FACTORIAL12/FACTORIAL8][12]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + +static uint64_t me[12]; + +static int edge_cycle[NMOVES][12]; +static int corner_cycle[NMOVES][8]; +static int center_cycle[NMOVES][6]; +static int eofb_flipped[NMOVES][12]; +static int eorl_flipped[NMOVES][12]; +static int eoud_flipped[NMOVES][12]; +static int coud_flipped[NMOVES][8]; +static int corl_flipped[NMOVES][8]; +static int cofb_flipped[NMOVES][8]; +static Alg * equiv_alg[NMOVES]; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; +static int ep_mirror[12]; +static int cp_mirror[8]; +static int cpos_mirror[6]; +static Alg * rotation_algs[NROTATIONS]; + + +/* Symmetry data for some coordinates ****************************************/ + +Trans +trans_group_trivial[1] = { uf }; + +Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int n_all_symdata = 2; +static SymData * all_sd[2] = { &sd_coud_16, &sd_eofbepos_16 }; + +/* Coordinates and their implementation **************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11 +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4 +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7 +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8 +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6 +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4 +}; + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430 +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430 +}; + + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[index_coud(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[index_eofbepos(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + + +/* TODO: rename */ +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + return admissible_ee_aux[ind]; +} + +/* TODO: rename */ +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +/* TODO: admissible co for other orientations */ +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = anti_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +/* TODO: admissible eos and cos */ +/* DONE: temporary fix, make it better */ +static Cube +antindex_drud(uint64_t ind) +{ + uint64_t epos, eofb; + Cube c; + + eofb = ind % POW2TO11; + epos = ind / (POW2TO11 * POW3TO7); + c = admissible_ee_aux[eofb + POW2TO11 * epos]; + + c.coud = (ind / POW2TO11) % POW3TO7; + c.corl = c.coud; + c.cofb = c.coud; + + return c; +} + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/POW3TO7]; + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)]; + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + + +/* Checkers ******************************************************************/ + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/ +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + + +/* Local functions implementation ********************************************/ + +/* TODO: this should be an anti index (maybe?) */ +static Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +static Cube +admissible_eos_from_eofbepos(Cube cube) +{ + Edge e; + Cube ret; + CubeArray *arr = new_cubearray(cube, pf_all); + + memcpy(arr->eorl, arr->eofb, 12 * sizeof(int)); + memcpy(arr->eoud, arr->eofb, 12 * sizeof(int)); + + for (e = 0; e < 12; e++) { + if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) || + (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0)) + arr->eorl[e] = 1 - arr->eorl[e]; + if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) || + (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2)) + arr->eoud[e] = 1 - arr->eoud[e]; + } + + ret = arrays_to_cube(arr, pf_all); + free_cubearray(arr, pf_all); + + return ret; +} + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +static void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +static Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +static void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +static int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +static Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +static void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +/* +static int +cphtr_cp(int cp) +{ + int i, a[8]; + + index_to_perm(cp, 8, a); + + for (i = 0; i < 8; i++) + if (a[i] == UFR || a[i] == UBL || a[i] == DFL || a[i] == DBR) + a[i] = 0; + else + a[i] = 1; + + swap(&a[1], &a[5]); + swap(&a[3], &a[7]); + + return subset_to_index(a, 8, 4); +} +*/ + +static void +dfs(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s.estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s.estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +static int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +static int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +static int +epos_dependent_pos(int poss, int pose) +{ + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +static void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +static int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +static void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +static void +genptable_bfs(PruneData *pd, int d, Move *ms) +{ + uint64_t i; + + for (i = 0; i < pd->coord->max; i++) + if (ptableval_index(pd, i) == d) + genptable_branch(pd, i, d, ms); +} + +static void +genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms) +{ + int i, j; + Cube cc, c; + + + for (i = 0; i < pd->ntrans; i++) { + c = apply_trans(pd->trans[i], pd->coord->cube(ind)); + for (j = 0; ms[j] != NULLMOVE; j++) { + cc = apply_move(ms[j], c); + if (ptableval(pd, cc) > d+1) + ptable_update(pd, cc, d+1); + } + } +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +static void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +static void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +static void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +static int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +static bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +static bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +static Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +static void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +static void +moveset_to_list(Moveset ms, Estimator f, Move *r) +{ + CubeTarget ct = { .target = 1 }; + int b[NMOVES]; + int na = 0, nb = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) { + if (ms(i)) { + ct.cube = apply_move(i, (Cube){0}); + if (f != NULL && f(ct)) + r[na++] = i; + else + b[nb++] = i; + } + } + + memcpy(r + na, b, nb * sizeof(Move)); + r[na+nb] = NULLMOVE; +} + +static AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +static CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +static int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +static int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +static int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +static int +ptableval_index(PruneData *pd, uint64_t ind) +{ + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + +static bool +read_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +read_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +read_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +read_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fread(co_ttable[m], b, me[7], f) == me[7]; + r = r && fread(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fread(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_algs[r % NROTATIONS]); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_algs[r % NROTATIONS], ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +static void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +static void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +static bool +write_mtables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_ptable_file(PruneData *pd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + +static bool +write_symdata_file(SymData *sd) +{ + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_ttables_file() +{ + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(co_ttable[m], b, me[7], f) == me[7]; + r = r && fwrite(cpos_ttable[m], b, me[10], f) == me[10]; + r = r && fwrite(moves_ttable[m], b, me[11], f) == me[11]; + } + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +static void +init_auxtables() +{ + Cube c1, c2; + CubeArray *arr; + uint64_t ui, uj; + int i, j, k, auxarr[12]; + bool cij, p1, p2; + + for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) { + k = (ui / POW2TO11) * 24; + c1 = admissible_ep((Cube){ .epose = k }, pf_e); + c1.eofb = ui % POW2TO11; + c1 = admissible_eos_from_eofbepos(c1); + admissible_ee_aux[ui] = c1; + } + + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) { + what_center_at_aux[ui][i] = arr->cpos[i]; + where_is_center_aux[ui][arr->cpos[i]] = i; + } + free_cubearray(arr, pf_cpos); + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) { + what_corner_at_aux[ui][i] = arr->cp[i]; + where_is_corner_aux[ui][arr->cp[i]] = i; + } + free_cubearray(arr, pf_cp); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + where_is_edge_aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + where_is_edge_aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + where_is_edge_aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + what_orientation_last_corner_aux[ui] = auxarr[7]; + } + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + what_orientation_last_edge_aux[ui] = auxarr[11]; + } + + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + epos_dependent_aux[ui][uj]=epos_dependent_pos(ui, uj); + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + } + } + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + possible_next[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + for (i = 0; i < NMOVES; i++) + inverse_move_aux[i] = i ? i + 2 - 2*((i-1)%3) : NULLMOVE; + + /* Is there a more elegant way? */ + inverse_trans_aux[uf] = uf; + inverse_trans_aux[ur] = ul; + inverse_trans_aux[ul] = ur; + inverse_trans_aux[ub] = ub; + + inverse_trans_aux[df] = df; + inverse_trans_aux[dr] = dr; + inverse_trans_aux[dl] = dl; + inverse_trans_aux[db] = db; + + inverse_trans_aux[rf] = lf; + inverse_trans_aux[rd] = bl; + inverse_trans_aux[rb] = rb; + inverse_trans_aux[ru] = fr; + + inverse_trans_aux[lf] = rf; + inverse_trans_aux[ld] = br; + inverse_trans_aux[lb] = lb; + inverse_trans_aux[lu] = fl; + + inverse_trans_aux[fu] = fu; + inverse_trans_aux[fr] = ru; + inverse_trans_aux[fd] = bu; + inverse_trans_aux[fl] = lu; + + inverse_trans_aux[bu] = fd; + inverse_trans_aux[br] = ld; + inverse_trans_aux[bd] = bd; + inverse_trans_aux[bl] = rd; + + inverse_trans_aux[uf_mirror] = uf_mirror; + inverse_trans_aux[ur_mirror] = ur_mirror; + inverse_trans_aux[ul_mirror] = ul_mirror; + inverse_trans_aux[ub_mirror] = ub_mirror; + + inverse_trans_aux[df_mirror] = df_mirror; + inverse_trans_aux[dr_mirror] = dl_mirror; + inverse_trans_aux[dl_mirror] = dr_mirror; + inverse_trans_aux[db_mirror] = db_mirror; + + inverse_trans_aux[rf_mirror] = rf_mirror; + inverse_trans_aux[rd_mirror] = br_mirror; + inverse_trans_aux[rb_mirror] = lb_mirror; + inverse_trans_aux[ru_mirror] = fl_mirror; + + inverse_trans_aux[lf_mirror] = lf_mirror; + inverse_trans_aux[ld_mirror] = bl_mirror; + inverse_trans_aux[lb_mirror] = rb_mirror; + inverse_trans_aux[lu_mirror] = fr_mirror; + + inverse_trans_aux[fu_mirror] = fu_mirror; + inverse_trans_aux[fr_mirror] = lu_mirror; + inverse_trans_aux[fd_mirror] = bu_mirror; + inverse_trans_aux[fl_mirror] = ru_mirror; + + inverse_trans_aux[bu_mirror] = fd_mirror; + inverse_trans_aux[br_mirror] = rd_mirror; + inverse_trans_aux[bd_mirror] = bd_mirror; + inverse_trans_aux[bl_mirror] = ld_mirror; +} + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + jj = cp_mtable[moves[k]][next[j]]; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_environment() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } +} + +static void +init_moves() { + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); +} + +static void +init_moves_aux() +{ + /* Some standard PieceFilters */ + pf_all.epose = true; + pf_all.eposs = true; + pf_all.eposm = true; + pf_all.eofb = true; + pf_all.eorl = true; + pf_all.eoud = true; + pf_all.cp = true; + pf_all.cofb = true; + pf_all.corl = true; + pf_all.coud = true; + pf_all.cpos = true; + + pf_4val.epose = true; + pf_4val.eposs = true; + pf_4val.eposm = true; + pf_4val.eofb = true; + pf_4val.coud = true; + pf_4val.cp = true; + + pf_epcp.epose = true; + pf_epcp.eposs = true; + pf_epcp.eposm = true; + pf_epcp.cp = true; + + pf_cpos.cpos = true; + + pf_cp.cp = true; + + pf_ep.epose = true; + pf_ep.eposs = true; + pf_ep.eposm = true; + + pf_e.epose = true; + pf_s.eposs = true; + pf_m.eposm = true; + + pf_eo.eofb = true; + pf_eo.eorl = true; + pf_eo.eoud = true; + + pf_co.cofb = true; + pf_co.corl = true; + pf_co.coud = true; + + /* Used to convert to and from CubeArray */ + epe_solved[0] = FR; + epe_solved[1] = FL; + epe_solved[2] = BL; + epe_solved[3] = BR; + + eps_solved[0] = UL; + eps_solved[1] = UR; + eps_solved[2] = DL; + eps_solved[3] = DR; + + epm_solved[0] = UF; + epm_solved[1] = UB; + epm_solved[2] = DF; + epm_solved[3] = DB; + + /* Table sizes, used for reading and writing files */ + me[0] = FACTORIAL12/FACTORIAL8; + me[1] = FACTORIAL12/FACTORIAL8; + me[2] = FACTORIAL12/FACTORIAL8; + me[3] = POW2TO11; + me[4] = POW2TO11; + me[5] = POW2TO11; + me[6] = FACTORIAL8; + me[7] = POW3TO7; + me[8] = POW3TO7; + me[9] = POW3TO7; + me[10] = FACTORIAL6; + me[11] = NMOVES; + + /* Cycles *********************/ + edge_cycle[U][UF] = UR; + edge_cycle[U][UL] = UF; + edge_cycle[U][UB] = UL; + edge_cycle[U][UR] = UB; + edge_cycle[U][DF] = DF; + edge_cycle[U][DL] = DL; + edge_cycle[U][DB] = DB; + edge_cycle[U][DR] = DR; + edge_cycle[U][FR] = FR; + edge_cycle[U][FL] = FL; + edge_cycle[U][BL] = BL; + edge_cycle[U][BR] = BR; + + edge_cycle[x][UF] = DF; + edge_cycle[x][UL] = FL; + edge_cycle[x][UB] = UF; + edge_cycle[x][UR] = FR; + edge_cycle[x][DF] = DB; + edge_cycle[x][DL] = BL; + edge_cycle[x][DB] = UB; + edge_cycle[x][DR] = BR; + edge_cycle[x][FR] = DR; + edge_cycle[x][FL] = DL; + edge_cycle[x][BL] = UL; + edge_cycle[x][BR] = UR; + + edge_cycle[y][UF] = UR; + edge_cycle[y][UL] = UF; + edge_cycle[y][UB] = UL; + edge_cycle[y][UR] = UB; + edge_cycle[y][DF] = DR; + edge_cycle[y][DL] = DF; + edge_cycle[y][DB] = DL; + edge_cycle[y][DR] = DB; + edge_cycle[y][FR] = BR; + edge_cycle[y][FL] = FR; + edge_cycle[y][BL] = FL; + edge_cycle[y][BR] = BL; + + corner_cycle[U][UFR] = UBR; + corner_cycle[U][UFL] = UFR; + corner_cycle[U][UBL] = UFL; + corner_cycle[U][UBR] = UBL; + corner_cycle[U][DFR] = DFR; + corner_cycle[U][DFL] = DFL; + corner_cycle[U][DBL] = DBL; + corner_cycle[U][DBR] = DBR; + + corner_cycle[x][UFR] = DFR; + corner_cycle[x][UFL] = DFL; + corner_cycle[x][UBL] = UFL; + corner_cycle[x][UBR] = UFR; + corner_cycle[x][DFR] = DBR; + corner_cycle[x][DFL] = DBL; + corner_cycle[x][DBL] = UBL; + corner_cycle[x][DBR] = UBR; + + corner_cycle[y][UFR] = UBR; + corner_cycle[y][UFL] = UFR; + corner_cycle[y][UBL] = UFL; + corner_cycle[y][UBR] = UBL; + corner_cycle[y][DFR] = DBR; + corner_cycle[y][DFL] = DFR; + corner_cycle[y][DBL] = DFL; + corner_cycle[y][DBR] = DBL; + + center_cycle[U][U_center] = U_center; + center_cycle[U][D_center] = D_center; + center_cycle[U][R_center] = R_center; + center_cycle[U][L_center] = L_center; + center_cycle[U][F_center] = F_center; + center_cycle[U][B_center] = B_center; + + center_cycle[x][U_center] = F_center; + center_cycle[x][D_center] = B_center; + center_cycle[x][R_center] = R_center; + center_cycle[x][L_center] = L_center; + center_cycle[x][F_center] = D_center; + center_cycle[x][B_center] = U_center; + + center_cycle[y][U_center] = U_center; + center_cycle[y][D_center] = D_center; + center_cycle[y][R_center] = B_center; + center_cycle[y][L_center] = F_center; + center_cycle[y][F_center] = R_center; + center_cycle[y][B_center] = L_center; + + /* Flipped pieces *************/ + eofb_flipped[x][UF] = 1; + eofb_flipped[x][UB] = 1; + eofb_flipped[x][DF] = 1; + eofb_flipped[x][DB] = 1; + + eofb_flipped[y][FR] = 1; + eofb_flipped[y][FL] = 1; + eofb_flipped[y][BL] = 1; + eofb_flipped[y][BR] = 1; + + eorl_flipped[x][UF] = 1; + eorl_flipped[x][UL] = 1; + eorl_flipped[x][UB] = 1; + eorl_flipped[x][UR] = 1; + eorl_flipped[x][DF] = 1; + eorl_flipped[x][DL] = 1; + eorl_flipped[x][DB] = 1; + eorl_flipped[x][DR] = 1; + eorl_flipped[x][FR] = 1; + eorl_flipped[x][FL] = 1; + eorl_flipped[x][BL] = 1; + eorl_flipped[x][BR] = 1; + + eorl_flipped[y][FR] = 1; + eorl_flipped[y][FL] = 1; + eorl_flipped[y][BL] = 1; + eorl_flipped[y][BR] = 1; + + eoud_flipped[U][UF] = 1; + eoud_flipped[U][UL] = 1; + eoud_flipped[U][UB] = 1; + eoud_flipped[U][UR] = 1; + + eoud_flipped[x][UF] = 1; + eoud_flipped[x][UB] = 1; + eoud_flipped[x][DF] = 1; + eoud_flipped[x][DB] = 1; + + eoud_flipped[y][UF] = 1; + eoud_flipped[y][UL] = 1; + eoud_flipped[y][UB] = 1; + eoud_flipped[y][UR] = 1; + eoud_flipped[y][DF] = 1; + eoud_flipped[y][DL] = 1; + eoud_flipped[y][DB] = 1; + eoud_flipped[y][DR] = 1; + eoud_flipped[y][FR] = 1; + eoud_flipped[y][FL] = 1; + eoud_flipped[y][BL] = 1; + eoud_flipped[y][BR] = 1; + + coud_flipped[x][UFR] = 2; + coud_flipped[x][UFL] = 1; + coud_flipped[x][UBR] = 1; + coud_flipped[x][UBL] = 2; + coud_flipped[x][DFR] = 1; + coud_flipped[x][DFL] = 2; + coud_flipped[x][DBR] = 2; + coud_flipped[x][DBL] = 1; + + corl_flipped[U][UFR] = 1; + corl_flipped[U][UFL] = 2; + corl_flipped[U][UBL] = 1; + corl_flipped[U][UBR] = 2; + + corl_flipped[y][UFR] = 1; + corl_flipped[y][UFL] = 2; + corl_flipped[y][UBL] = 1; + corl_flipped[y][UBR] = 2; + corl_flipped[y][DFR] = 2; + corl_flipped[y][DFL] = 1; + corl_flipped[y][DBL] = 2; + corl_flipped[y][DBR] = 1; + + cofb_flipped[U][UFR] = 2; + cofb_flipped[U][UFL] = 1; + cofb_flipped[U][UBL] = 2; + cofb_flipped[U][UBR] = 1; + + cofb_flipped[x][UFR] = 1; + cofb_flipped[x][UFL] = 2; + cofb_flipped[x][UBL] = 1; + cofb_flipped[x][UBR] = 2; + cofb_flipped[x][DFR] = 2; + cofb_flipped[x][DFL] = 1; + cofb_flipped[x][DBL] = 2; + cofb_flipped[x][DBR] = 1; + + cofb_flipped[y][UFR] = 2; + cofb_flipped[y][UFL] = 1; + cofb_flipped[y][UBL] = 2; + cofb_flipped[y][UBR] = 1; + cofb_flipped[y][DFR] = 1; + cofb_flipped[y][DFL] = 2; + cofb_flipped[y][DBL] = 1; + cofb_flipped[y][DBR] = 2; + + /* Equivalent moves ***********/ + equiv_alg[NULLMOVE] = new_alg(""); + + equiv_alg[U] = new_alg(" U "); + equiv_alg[U2] = new_alg(" UU "); + equiv_alg[U3] = new_alg(" UUU "); + equiv_alg[D] = new_alg(" xx U xx "); + equiv_alg[D2] = new_alg(" xx UU xx "); + equiv_alg[D3] = new_alg(" xx UUU xx "); + equiv_alg[R] = new_alg(" yx U xxxyyy "); + equiv_alg[R2] = new_alg(" yx UU xxxyyy "); + equiv_alg[R3] = new_alg(" yx UUU xxxyyy "); + equiv_alg[L] = new_alg(" yyyx U xxxy "); + equiv_alg[L2] = new_alg(" yyyx UU xxxy "); + equiv_alg[L3] = new_alg(" yyyx UUU xxxy "); + equiv_alg[F] = new_alg(" x U xxx "); + equiv_alg[F2] = new_alg(" x UU xxx "); + equiv_alg[F3] = new_alg(" x UUU xxx "); + equiv_alg[B] = new_alg(" xxx U x "); + equiv_alg[B2] = new_alg(" xxx UU x "); + equiv_alg[B3] = new_alg(" xxx UUU x "); + + equiv_alg[Uw] = new_alg(" xx U xx y "); + equiv_alg[Uw2] = new_alg(" xx UU xx yy "); + equiv_alg[Uw3] = new_alg(" xx UUU xx yyy "); + equiv_alg[Dw] = new_alg(" U yyy "); + equiv_alg[Dw2] = new_alg(" UU yy "); + equiv_alg[Dw3] = new_alg(" UUU y "); + equiv_alg[Rw] = new_alg(" yyyx U xxxy x "); + equiv_alg[Rw2] = new_alg(" yyyx UU xxxy xx "); + equiv_alg[Rw3] = new_alg(" yyyx UUU xxxy xxx "); + equiv_alg[Lw] = new_alg(" yx U xxxyyy xxx "); + equiv_alg[Lw2] = new_alg(" yx UU xxxyyy xx "); + equiv_alg[Lw3] = new_alg(" yx UUU xxxyyy x "); + equiv_alg[Fw] = new_alg(" xxx U x yxxxyyy "); + equiv_alg[Fw2] = new_alg(" xxx UU x yxxyyy "); + equiv_alg[Fw3] = new_alg(" xxx UUU x yxyyy "); + equiv_alg[Bw] = new_alg(" x U xxx yxyyy "); + equiv_alg[Bw2] = new_alg(" x UU xxx yxxyyy "); + equiv_alg[Bw3] = new_alg(" x UUU xxx yxxxyyy "); + + equiv_alg[M] = new_alg(" yx U xx UUU yxyyy "); + equiv_alg[M2] = new_alg(" yx UU xx UU xxxy "); + equiv_alg[M3] = new_alg(" yx UUU xx U yxxxy "); + equiv_alg[S] = new_alg(" x UUU xx U yyyx "); + equiv_alg[S2] = new_alg(" x UU xx UU yyx "); + equiv_alg[S3] = new_alg(" x U xx UUU yx "); + equiv_alg[E] = new_alg(" U xx UUU xxyyy "); + equiv_alg[E2] = new_alg(" UU xx UU xxyy "); + equiv_alg[E3] = new_alg(" UUU xx U xxy "); + + equiv_alg[x] = new_alg(" x "); + equiv_alg[x2] = new_alg(" xx "); + equiv_alg[x3] = new_alg(" xxx "); + equiv_alg[y] = new_alg(" y "); + equiv_alg[y2] = new_alg(" yy "); + equiv_alg[y3] = new_alg(" yyy "); + equiv_alg[z] = new_alg(" yyy x y "); + equiv_alg[z2] = new_alg(" yy xx "); + equiv_alg[z3] = new_alg(" y x yyy "); +} + +static void +init_strings() +{ + strcpy(move_string [NULLMOVE], "-" ); + strcpy(move_string [U], "U" ); + strcpy(move_string [U2], "U2" ); + strcpy(move_string [U3], "U\'" ); + strcpy(move_string [D], "D" ); + strcpy(move_string [D2], "D2" ); + strcpy(move_string [D3], "D\'" ); + strcpy(move_string [R], "R" ); + strcpy(move_string [R2], "R2" ); + strcpy(move_string [R3], "R\'" ); + strcpy(move_string [L], "L" ); + strcpy(move_string [L2], "L2" ); + strcpy(move_string [L3], "L\'" ); + strcpy(move_string [F], "F" ); + strcpy(move_string [F2], "F2" ); + strcpy(move_string [F3], "F\'" ); + strcpy(move_string [B], "B" ); + strcpy(move_string [B2], "B2" ); + strcpy(move_string [B3], "B\'" ); + strcpy(move_string [Uw], "Uw" ); + strcpy(move_string [Uw2], "Uw2" ); + strcpy(move_string [Uw3], "Uw\'" ); + strcpy(move_string [Dw], "Dw" ); + strcpy(move_string [Dw2], "Dw2" ); + strcpy(move_string [Dw3], "Dw\'" ); + strcpy(move_string [Rw], "Rw" ); + strcpy(move_string [Rw2], "Rw2" ); + strcpy(move_string [Rw3], "Rw\'" ); + strcpy(move_string [Lw], "Lw" ); + strcpy(move_string [Lw2], "Lw2" ); + strcpy(move_string [Lw3], "Lw\'" ); + strcpy(move_string [Fw], "Fw" ); + strcpy(move_string [Fw2], "Fw2" ); + strcpy(move_string [Fw3], "Fw\'" ); + strcpy(move_string [Bw], "Bw" ); + strcpy(move_string [Bw2], "Bw2" ); + strcpy(move_string [Bw3], "Bw\'" ); + strcpy(move_string [M], "M" ); + strcpy(move_string [M2], "M2" ); + strcpy(move_string [M3], "M\'" ); + strcpy(move_string [S], "S" ); + strcpy(move_string [S2], "S2" ); + strcpy(move_string [S3], "S\'" ); + strcpy(move_string [E], "E" ); + strcpy(move_string [E2], "E2" ); + strcpy(move_string [E3], "E\'" ); + strcpy(move_string [x], "x" ); + strcpy(move_string [x2], "x2" ); + strcpy(move_string [x3], "x\'" ); + strcpy(move_string [y], "y" ); + strcpy(move_string [y2], "y2" ); + strcpy(move_string [y3], "y\'" ); + strcpy(move_string [z], "z" ); + strcpy(move_string [z2], "z2" ); + strcpy(move_string [z3], "z\'" ); + + strcpy(edge_string [UF], "UF" ); + strcpy(edge_string [UL], "UL" ); + strcpy(edge_string [UB], "UB" ); + strcpy(edge_string [UR], "UR" ); + strcpy(edge_string [DF], "DF" ); + strcpy(edge_string [DL], "DL" ); + strcpy(edge_string [DB], "DB" ); + strcpy(edge_string [DR], "DR" ); + strcpy(edge_string [FR], "FR" ); + strcpy(edge_string [FL], "FL" ); + strcpy(edge_string [BL], "BL" ); + strcpy(edge_string [BR], "BR" ); + + strcpy(corner_string [UFR], "UFR" ); + strcpy(corner_string [UFL], "UFL" ); + strcpy(corner_string [UBL], "UBL" ); + strcpy(corner_string [UBR], "UBR" ); + strcpy(corner_string [DFR], "DFR" ); + strcpy(corner_string [DFL], "DFL" ); + strcpy(corner_string [DBL], "DBL" ); + strcpy(corner_string [DBR], "DBR" ); + + strcpy(center_string [U_center], "U" ); + strcpy(center_string [D_center], "D" ); + strcpy(center_string [R_center], "R" ); + strcpy(center_string [L_center], "L" ); + strcpy(center_string [F_center], "F" ); + strcpy(center_string [B_center], "B" ); +} + +static void +init_symdata() +{ + int i; + + for (i = 0; i < n_all_symdata; i++) + gensym(all_sd[i]); +} + +static void +init_trans() { + Cube aux, cube, mirr, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_algs[i % NROTATIONS], (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_algs[m % NROTATIONS], (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move_aux[move], aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube, false) || + is_solved(mirr, false)) + moves_ttable[m][mi] = move; + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static void +init_trans_aux() +{ + ep_mirror[UF] = UF; + ep_mirror[UL] = UR; + ep_mirror[UB] = UB; + ep_mirror[UR] = UL; + ep_mirror[DF] = DF; + ep_mirror[DL] = DR; + ep_mirror[DB] = DB; + ep_mirror[DR] = DL; + ep_mirror[FR] = FL; + ep_mirror[FL] = FR; + ep_mirror[BR] = BL; + ep_mirror[BL] = BR; + + cp_mirror[UFR] = UFL; + cp_mirror[UFL] = UFR; + cp_mirror[UBL] = UBR; + cp_mirror[UBR] = UBL; + cp_mirror[DFR] = DFL; + cp_mirror[DFL] = DFR; + cp_mirror[DBL] = DBR; + cp_mirror[DBR] = DBL; + + cpos_mirror[U_center] = U_center; + cpos_mirror[D_center] = D_center; + cpos_mirror[R_center] = L_center; + cpos_mirror[L_center] = R_center; + cpos_mirror[F_center] = F_center; + cpos_mirror[B_center] = B_center; + + /* Is there a more elegant way? */ + rotation_algs[uf] = new_alg(""); + rotation_algs[ur] = new_alg("y"); + rotation_algs[ub] = new_alg("y2"); + rotation_algs[ul] = new_alg("y3"); + + rotation_algs[df] = new_alg("z2"); + rotation_algs[dr] = new_alg("y z2"); + rotation_algs[db] = new_alg("x2"); + rotation_algs[dl] = new_alg("y3 z2"); + + rotation_algs[rf] = new_alg("z3"); + rotation_algs[rd] = new_alg("z3 y"); + rotation_algs[rb] = new_alg("z3 y2"); + rotation_algs[ru] = new_alg("z3 y3"); + + rotation_algs[lf] = new_alg("z"); + rotation_algs[ld] = new_alg("z y3"); + rotation_algs[lb] = new_alg("z y2"); + rotation_algs[lu] = new_alg("z y"); + + rotation_algs[fu] = new_alg("x y2"); + rotation_algs[fr] = new_alg("x y"); + rotation_algs[fd] = new_alg("x"); + rotation_algs[fl] = new_alg("x y3"); + + rotation_algs[bu] = new_alg("x3"); + rotation_algs[br] = new_alg("x3 y"); + rotation_algs[bd] = new_alg("x3 y2"); + rotation_algs[bl] = new_alg("x3 y3"); +} + + +/* Public functions implementation *******************************************/ + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + + +Cube +apply_trans(Trans t, Cube cube) +{ + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +/*TODO maybe move the next two */ +uint64_t +cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +Cube +anti_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +uint64_t +epos_dependent(Cube c) +{ + return epos_dependent_aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +void +genptable(PruneData *pd) +{ + Move ms[NMOVES]; + int d; + uint64_t j, oldn = 0; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe crash gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + moveset_to_list(pd->moveset, NULL, ms); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update(pd, pd->coord->cube(j), 15); + + /*TODO: change, set to 0 for every solved state (might be more than 1)*/ + ptable_update(pd, (Cube){0}, 0); + pd->n = 1; + + for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { + genptable_bfs(pd, d, ms); + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + d, pd->n - oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); + + pd->generated = true; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +Move +inverse_move(Move m) +{ + return inverse_move_aux[m]; +} + +Trans +inverse_trans(Trans t) +{ + return inverse_trans_aux[t]; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube, bool reorient) +{ + int i; + + if (reorient) { + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube),false)) + return true; + return false; + } else { + return equal(cube, (Cube){0}); + } +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ +/* + CubeArray *arr = new_cubearray(cube, pf_all); + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[arr->ep[i]]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %c ", arr->eofb[i] + '0'); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[arr->cp[i]]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %c ", arr->coud[i] + '0'); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[arr->cpos[i]]); + printf("\n"); + + free_cubearray(arr, pf_all); +*/ + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); + +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +/* TODO: clean pre_trans or put it back */ +AlgList * +solve(Cube cube, Step step, SolveOptions *opts) +{ + /*AlgListNode *node;*/ + AlgList *sols = new_alglist(); + /*Cube c = apply_trans(opts->pre_trans, cube);*/ + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step.ready != NULL && !step.ready(cube)) { + fprintf(stderr, "Cube not ready for solving step\n"); + return sols; + } + + moveset_to_list(step.moveset, step.estimate, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && !(sols->len && opts->optimal_only); + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(cube, step, opts, &dd); + } + +/* + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(opts->pre_trans), node->alg); +*/ + + free_alg(dd.current_alg); + return sols; +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string[j][0]) { + m = j; + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1]=='\'' || str[i+1]=='3') { + m += 2; + i++; + } + append_move(alg, m, niss); + break; + } + } + } + + return alg; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string[alg->move[i]]); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval(pd, pd->coord->cube(i))]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + return ptableval_index(pd, pd->coord->index(cube)); +} + +Alg * +rotation_alg(Trans i) +{ + return rotation_algs[i % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +Center +what_center_at(Cube cube, Center c) +{ + return what_center_at_aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + return what_corner_at_aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + if (c < 7) + return (co / powint(3, c)) % 3; + else + return what_orientation_last_corner_aux[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return what_orientation_last_edge_aux[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + return where_is_center_aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + return where_is_corner_aux[cube.cp][c]; +} + + +void +init() +{ + /* Order is important! */ + init_environment(); + init_strings(); + init_moves_aux(); + init_moves(); + init_auxtables(); + init_cphtr_cosets(); + init_trans_aux(); + init_trans(); + init_symdata(); +} + diff --git a/old/2021-07-15-almostbeforerefactor/cube.h b/old/2021-07-15-almostbeforerefactor/cube.h @@ -0,0 +1,79 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "macros.h" +#include "cubetypes.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_drud; +extern Coordinate coord_coud_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_khuge; + +extern Trans trans_group_udfix[16]; +extern Trans trans_group_trivial[1]; + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_move(Move m, Cube cube); +Cube apply_trans(Trans t, Cube cube); +bool block_solved(Cube cube, Block); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */ +Cube anti_cphtr(uint64_t ind); /*TODO also this */ +uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +Move inverse_move(Move m); +Trans inverse_trans(Trans t); +bool is_admissible(Cube cube); +bool is_solved(Cube cube, bool reorient); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void print_cube(Cube cube); +Cube random_cube(); +AlgList * solve(Cube cube, Step step, SolveOptions *opts); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +bool check_centers(Cube cube); +bool check_corners(Cube cube); +bool check_cornershtr(Cube cube); +bool check_coud(Cube cube); +bool check_drud(Cube cube); +bool check_eofb(Cube cube); +bool check_eofbepos(Cube cube); +bool check_epose(Cube cube); +bool check_ep(Cube cube); +bool check_khuge(Cube cube); +bool check_nothing(Cube cube); + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +void init(); + +#endif + diff --git a/old/2021-07-15-almostbeforerefactor/cubetypes.h b/old/2021-07-15-almostbeforerefactor/cubetypes.h @@ -0,0 +1,252 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + Checker check; + uint64_t max; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; + int ntrans; + Trans * trans; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; +}; + +struct +step +{ + Estimator estimate; + Checker ready; + Moveset moveset; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/old/2021-07-15-almostbeforerefactor/macros.h b/old/2021-07-15-almostbeforerefactor/macros.h @@ -0,0 +1,23 @@ +#ifndef MACROS_H +#define MACROS_H + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NMOVES (z3+1) +#define NTRANS 48 +#define NROTATIONS 24 + +#endif diff --git a/old/2021-07-15-almostbeforerefactor/main.c b/old/2021-07-15-almostbeforerefactor/main.c @@ -0,0 +1,60 @@ +#include <stdio.h> +#include "cube.h" +#include "steps.h" + +int main() { + Alg *algo; + AlgList *sols; + Cube cube; + SolveOptions opts; + char line[1000]; + int i, ns = 1; +/* Move m;*/ +/* int nrand = 10000, sum1, sum2;*/ + + Step *stps[20] = {&optimal_HTM}; + char sss[30][30] = {"Optimal solve"}; + + opts = (SolveOptions) { + .min_moves = 0, + .max_moves = 20, + .optimal_only = true, + .max_solutions = 1, + .can_niss = false, + .feedback = true, + }; + + init(); + +/* + srand(time(NULL)); + sum1 = 0; + sum2 = 0; + for (i = 0; i < nrand; i++) { + cube = random_cube(); + sum1 += drud_HTM.estimate((CubeTarget){.cube = cube, .target = 20}); + sum2 += optimal_HTM.estimate((CubeTarget){.cube = cube, .target = 20}); + } + printf("Average drud pruning: %lf\n", ((double)sum1) / ((double) nrand)); + printf("Average corners htr pruning: %lf\n", ((double)sum2) / ((double) nrand)); +*/ + + + printf("Welcome to nissy 2.0! Insert a scramble:\n"); + + if (fgets(line, 1000, stdin) != NULL) { + algo = new_alg(line); + cube = apply_alg(algo, (Cube){0}); + + for (i = 0; i < ns; i++) { + sols = solve(cube, *stps[i], &opts); + printf("%s: %d solutions found:\n", sss[i], sols->len); + print_alglist(sols, true); + free_alglist(sols); + } + free_alg(algo); + } + + return 0; +} + diff --git a/old/2021-07-15-almostbeforerefactor/nissy b/old/2021-07-15-almostbeforerefactor/nissy Binary files differ. diff --git a/old/2021-07-15-almostbeforerefactor/steps.c b/old/2021-07-15-almostbeforerefactor/steps.c @@ -0,0 +1,284 @@ +#include "steps.h" + +/* Standard checkers (return lower bound) ************************************/ + +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .estimate = estimate_coud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .estimate = estimate_coud_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .estimate = estimate_corners_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_HTM = { + .estimate = estimate_cornershtr_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +cornershtr_URF = { + .estimate = estimate_cornershtr_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +corners_URF = { + .estimate = estimate_corners_URF, + .ready = check_nothing, + .moveset = moveset_URF +}; + +Step +drud_HTM = { + .estimate = estimate_drud_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .moveset = moveset_HTM +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM, + .ntrans = 1, + .trans = trans_group_trivial +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM, + .ntrans = 1, + .trans = trans_group_trivial +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "ptable_cornershtr_withcosets_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM, + .ntrans = 1, + .trans = trans_group_trivial +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM, + .ntrans = 1, + .trans = trans_group_trivial +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .coord = &coord_drud, + .moveset = moveset_HTM, + .ntrans = 1, + .trans = trans_group_trivial +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "ptable_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, + .ntrans = 16, + .trans = trans_group_udfix +}; + +PruneData +pd_khuge_HTM = { + .filename = "ptable_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM, + .ntrans = 16, + .trans = trans_group_udfix +}; + + +/* Standard checkers (return lower bound) ************************************/ + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + if (!pd_eofb_HTM.generated) + genptable(&pd_eofb_HTM); + + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + if (!pd_coud_HTM.generated) + genptable(&pd_coud_HTM); + + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + if (!pd_corners_HTM.generated) + genptable(&pd_corners_HTM); + + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + if (!pd_cornershtr_HTM.generated) + genptable(&pd_cornershtr_HTM); + + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ +/* + if (!pd_drud_HTM.generated) + genptable(&pd_drud_HTM); + + return ptableval(&pd_drud_HTM, ct.cube); +*/ + + if (!pd_drud_sym16_HTM.generated) + genptable(&pd_drud_sym16_HTM); + + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + if (!pd_khuge_HTM.generated) + genptable(&pd_khuge_HTM); + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(rf, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(fd, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + /* Michiel de Bondt's trick */ + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + return MAX(ret, dr3); +} diff --git a/old/2021-07-15-almostbeforerefactor/steps.h b/old/2021-07-15-almostbeforerefactor/steps.h @@ -0,0 +1,24 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +extern Step eofb_HTM; +extern Step coud_HTM; +extern Step coud_URF; +extern Step corners_HTM; +extern Step cornershtr_HTM; +extern Step corners_URF; +extern Step cornershtr_URF; +extern Step drud_HTM; +extern Step optimal_HTM; + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_cornershtreofb_HTM; +extern PruneData pd_drud_HTM; +extern PruneData pd_khuge_HTM; + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/alg.c b/old/2021-11-10-beforeremovingchecker/alg.c @@ -0,0 +1,366 @@ +#include "alg.h" + +/* Local functions ***********************************************************/ + +static void free_alglistnode(AlgListNode *aln); +static void realloc_alg(Alg *alg, int n); + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + +bool +moveset_eofb(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || b == R || b == L || + ((b == F || b == B) && m == b+1); +} + +bool +moveset_drud(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || + ((b == R || b == L || b == F || b == B) && m == b + 1); +} + +bool +moveset_htr(Move m) +{ + Move b = base_move(m); + + return moveset_HTM(m) && m == b + 1; +} + + +/* Functions *****************************************************************/ + +void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +void +compose_alg(Alg *alg1, Alg *alg2) +{ + int i; + + for (i = 0; i < alg2->len; i++) + append_move(alg1, alg2->move[i], alg2->inv[i]); +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +static void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Move +inverse_move(Move m) +{ + return m == NULLMOVE ? NULLMOVE : m + 2 - 2*((m-1) % 3); +} + +char * +move_string(Move m) +{ + static char move_string_aux[NMOVES][7] = { + [NULLMOVE] = "-", + [U] = "U", [U2] = "U2", [U3] = "U\'", + [D] = "D", [D2] = "D2", [D3] = "D\'", + [R] = "R", [R2] = "R2", [R3] = "R\'", + [L] = "L", [L2] = "L2", [L3] = "L\'", + [F] = "F", [F2] = "F2", [F3] = "F\'", + [B] = "B", [B2] = "B2", [B3] = "B\'", + [Uw] = "Uw", [Uw2] = "Uw2", [Uw3] = "Uw\'", + [Dw] = "Dw", [Dw2] = "Dw2", [Dw3] = "Dw\'", + [Rw] = "Rw", [Rw2] = "Rw2", [Rw3] = "Rw\'", + [Lw] = "Lw", [Lw2] = "Lw2", [Lw3] = "Lw\'", + [Fw] = "Fw", [Fw2] = "Fw2", [Fw3] = "Fw\'", + [Bw] = "Bw", [Bw2] = "Bw2", [Bw3] = "Bw\'", + [M] = "M", [M2] = "M2", [M3] = "M\'", + [E] = "E", [E2] = "E2", [E3] = "E\'", + [S] = "S", [S2] = "S2", [S3] = "S\'", + [x] = "x", [x2] = "x2", [x3] = "x\'", + [y] = "y", [y2] = "y2", [y3] = "y\'", + [z] = "z", [z2] = "z2", [z3] = "z\'", + }; + + return move_string_aux[m]; +} + +void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +void +moveset_to_list(Moveset ms, Move *r) +{ + int n = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) + if (ms(i)) + r[n++] = i; + + r[n] = NULLMOVE; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false, move_read; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + move_read = false; + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string(j)[0] || + (str[i] >= 'a' && str[i] <= 'z' && + str[i] == move_string(j)[0]-('A'-'a') && j<=B)) { + m = j; + if (str[i] >= 'a' && str[i] <= 'z' && j<=B) { + m += Uw - U; + } + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1] == '\'' || + str[i+1] == '3' || + str[i+1] == '`' ) { + m += 2; + i++; + } else if ((int)str[i+1] == -62 && + (int)str[i+2] == -76) { + /* Weird apostrophe */ + m += 2; + i += 2; + } else if ((int)str[i+1] == -30 && + (int)str[i+2] == -128 && + (int)str[i+3] == -103) { + /* MacOS apostrophe */ + m += 2; + i += 3; + } + append_move(alg, m, niss); + move_read = true; + break; + } + } + + if (!move_read) { + alg = new_alg(""); + return alg; + } + } + + return alg; +} + +AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + /* TODO: make it possible to print to stdout or to string */ + /* Maybe just return a string */ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string(alg->move[i])); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + diff --git a/old/2021-11-10-beforeremovingchecker/alg.h b/old/2021-11-10-beforeremovingchecker/alg.h @@ -0,0 +1,35 @@ +#ifndef ALG_H +#define ALG_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cubetypes.h" +#include "utils.h" + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); +bool moveset_eofb(Move m); +bool moveset_drud(Move m); +bool moveset_htr(Move m); + +void append_alg(AlgList *l, Alg *alg); +void append_move(Alg *alg, Move m, bool inverse); +void compose_alg(Alg *alg1, Alg *alg2); +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Move inverse_move(Move m); +char * move_string(Move m); +void movelist_to_position(Move *ml, int *pos); +void moveset_to_list(Moveset ms, Move *lst); +Alg * new_alg(char *str); +AlgList * new_alglist(); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); + +#endif + diff --git a/old/2021-11-10-beforeremovingchecker/commands.c b/old/2021-11-10-beforeremovingchecker/commands.c @@ -0,0 +1,329 @@ +#include "commands.h" + +/* Arg parsing functions *****************************************************/ + +CommandArgs * solvestep_parse_args(int c, char **v); +CommandArgs * help_parse_args(int c, char **v); +CommandArgs * print_parse_args(int c, char **v); +CommandArgs * parse_no_arg(int c, char **v); + +/* Exec functions ************************************************************/ + +static void solvestep_exec(CommandArgs *args); +static void steps_exec(CommandArgs *args); +static void commands_exec(CommandArgs *args); +static void print_exec(CommandArgs *args); +static void help_exec(CommandArgs *args); +static void quit_exec(CommandArgs *args); + +/* Local functions ***********************************************************/ + +static bool read_step(CommandArgs *args, char *str); +static bool read_scramble(int c, char **v, CommandArgs *args); + +/* Commands ******************************************************************/ + +Command +solvestep_cmd = { + .name = "solve", + .usage = "solve STEP [OPTIONS] SCRAMBLE", + .description = "Solve a step", + .parse_args = solvestep_parse_args, + .exec = solvestep_exec +}; + +Command +steps_cmd = { + .name = "steps", + .usage = "steps", + .description = "List available steps", + .parse_args = parse_no_arg, + .exec = steps_exec +}; + +Command +commands_cmd = { + .name = "commands", + .usage = "commands", + .description = "List available commands", + .parse_args = parse_no_arg, + .exec = commands_exec +}; + +Command +print_cmd = { + .name = "print", + .usage = "print SCRAMBLE", + .description = "Print written description of the cube", + .parse_args = print_parse_args, + .exec = print_exec, +}; + +Command +help_cmd = { + .name = "help", + .usage = "help [COMMAND]", + .description = "Display nissy manual page or help on specific command", + .parse_args = help_parse_args, + .exec = help_exec, +}; + +Command +quit_cmd = { + .name = "quit", + .usage = "quit", + .description = "Quit nissy", + .parse_args = parse_no_arg, + .exec = quit_exec, +}; + +Command *commands[NCOMMANDS] = { + &solvestep_cmd, + &steps_cmd, + &commands_cmd, + &help_cmd, + &print_cmd, + &quit_cmd +}; + +/* Arg parsing functions implementation **************************************/ + +CommandArgs * +solvestep_parse_args(int c, char **v) +{ + int i; + long val; + + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = false; + a->opts = malloc(sizeof(SolveOptions)); + a->step = steps[0]; + a->command = NULL; + a->scramble = NULL; + + a->opts->min_moves = 0; + a->opts->max_moves = 20; + a->opts->max_solutions = 1; + a->opts->optimal_only = false; + a->opts->can_niss = false; + a->opts->feedback = false; + a->opts->all = false; + a->opts->print_number = true; + + for (i = 0; i < c; i++) { + if (!strcmp(v[i], "-m")) { + val = strtol(v[++i], NULL, 10); + if (val < 0 || val > 100) { + fprintf(stderr, + "Invalid min number of moves.\n"); + return a; + } + a->opts->min_moves = val; + } else if (!strcmp(v[i], "-M")) { + val = strtol(v[++i], NULL, 10); + if (val < 0 || val > 100) { + fprintf(stderr, + "Invalid max number of moves.\n"); + return a; + } + a->opts->max_moves = val; + } else if (!strcmp(v[i], "-s")) { + val = strtol(v[++i], NULL, 10); + if (val < 1 || val > 1000000) { + fprintf(stderr, + "Invalid number of solutions.\n"); + return a; + } + a->opts->max_solutions = val; + } else if (!strcmp(v[i], "-o")) { + a->opts->optimal_only = true; + } else if (!strcmp(v[i], "-n")) { + a->opts->can_niss = true; + } else if (!strcmp(v[i], "-v")) { + a->opts->feedback = true; + } else if (!strcmp(v[i], "-a")) { + a->opts->all = true; + } else if (!strcmp(v[i], "-p")) { + a->opts->print_number = false; + } else if (!read_step(a, v[i])) { + break; + } + } + + a->success = read_scramble(c-i, &v[i], a); + return a; +} + +CommandArgs * +help_parse_args(int c, char **v) +{ + int i; + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->scramble = NULL; + a->opts = NULL; + a->step = NULL; + a->command = NULL; + + if (c == 1) { + for (i = 0; i < NCOMMANDS; i++) + if (commands[i] != NULL && + !strcmp(v[0], commands[i]->name)) + a->command = commands[i]; + if (a->command == NULL) + fprintf(stderr, "%s: command not found\n", v[0]); + } + + a->success = c == 0 || (c == 1 && a->command != NULL); + return a; +} + +CommandArgs * +parse_no_arg(int c, char **v) +{ + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->scramble = NULL; + a->opts = NULL; + a->step = NULL; + a->command = NULL; + + return a; +} + +CommandArgs * +print_parse_args(int c, char **v) +{ + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->opts = NULL; + a->step = NULL; + a->command = NULL; + + a->success = read_scramble(c-1, &v[1], a); + return a; +} + +/* Exec functions implementation *********************************************/ + +static void +solvestep_exec(CommandArgs *args) +{ + Cube c = apply_alg(args->scramble, (Cube){0}); + AlgList *sols = solve(c, args->step, args->opts); + print_alglist(sols, args->opts->print_number); + free_alglist(sols); +} + +static void +steps_exec(CommandArgs *args) +{ + int i; + + for (i = 0; i < NSTEPS && steps[i] != NULL; i++) + printf("%-15s %s\n", steps[i]->shortname, steps[i]->name); +} + +static void +commands_exec(CommandArgs *args) +{ + int i; + + for (i = 0; i < NCOMMANDS && commands[i] != NULL; i++) + printf("%s\n", commands[i]->usage); + +} + +static void +print_exec(CommandArgs *args) +{ + print_cube(apply_alg(args->scramble, (Cube){0})); +} + +static void +help_exec(CommandArgs *args) +{ + /* TODO: print full nissy manpage */ + if (args->command == NULL) { + printf("Type help COMMAND for information on a "); + printf("specific command.\n"); + printf("A more complete manual page is work in progress.\n"); + } else { + printf("Command %s: %s\nusage: %s\n", args->command->name, + args->command->description, args->command->usage); + } +} + +static void +quit_exec(CommandArgs *args) +{ + exit(0); +} + +/* Local functions implementation ********************************************/ + +static bool +read_step(CommandArgs *args, char *str) +{ + int i; + + for (i = 0; i < NSTEPS; i++) { + if (steps[i] != NULL && !strcmp(steps[i]->shortname, str)) { + args->step = steps[i]; + return true; + } + } + + return false; +} + +static bool +read_scramble(int c, char **v, CommandArgs *args) +{ + int i, k, n; + unsigned int j; + char *algstr; + + if (new_alg(v[0])->len == 0) { + fprintf(stderr, "%s: moves or option unrecognized\n", v[0]); + return false; + } + + n = 0; + for(i = 0; i < c; i++) + n += strlen(v[i]); + + algstr = malloc((n + 1) * sizeof(char)); + k = 0; + for (i = 0; i < c; i++) + for (j = 0; j < strlen(v[i]); j++) + algstr[k++] = v[i][j]; + algstr[k] = 0; + + args->scramble = new_alg(algstr); + free(algstr); + + if (args->scramble->len == 0) + fprintf(stderr, "Error reading scramble\n"); + + return args->scramble->len > 0; +} + +/* Public functions implementation *******************************************/ + +void +free_args(CommandArgs *args) +{ + if (args == NULL) + return; + + if (args->scramble != NULL) + free_alg(args->scramble); + if (args->opts != NULL) + free(args->opts); + + /* step and command must not be freed, they are static! */ + + free(args); +} diff --git a/old/2021-11-10-beforeremovingchecker/commands.h b/old/2021-11-10-beforeremovingchecker/commands.h @@ -0,0 +1,13 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +#include "solve.h" +#include "steps.h" + +#define NCOMMANDS 10 + +void free_args(CommandArgs *args); + +extern Command * commands[NCOMMANDS]; + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/coord.c b/old/2021-11-10-beforeremovingchecker/coord.c @@ -0,0 +1,629 @@ +#include "coord.h" + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_epud(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cp(uint64_t ind); +static Cube antindex_cphtr(uint64_t); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_cornershtrfin(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_drud_eofb(uint64_t ind); +static Cube antindex_htr_drud(uint64_t ind); +static Cube antindex_htrfin(uint64_t ind); + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_epud(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cp(Cube cube); +static uint64_t index_cphtr(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_cornershtrfin(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_drud_eofb(Cube cube); +static uint64_t index_htr_drud(Cube cube); +static uint64_t index_htrfin(Cube cube); + +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_cornershtrfin(); + + +/* All sorts of useful costants and tables **********************************/ + +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static int cornershtrfin_ind[FACTORIAL8]; +static int cornershtrfin_ant[24*24/6]; + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11, + .ntrans = 1, +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4, + .ntrans = 1, +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7, + .ntrans = 1, +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_cp = { + .index = index_cp, + .cube = antindex_cp, + .check = check_cp, + .max = FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_cphtr = { + .index = index_cphtr, + .cube = antindex_cphtr, + .check = check_cphtr, + .max = BINOM8ON4 * 6, + .ntrans = 1, +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6, + .ntrans = 1, +}; + +Coordinate +coord_cornershtrfin = { + .index = index_cornershtrfin, + .cube = antindex_cornershtrfin, + .check = check_cp, + .max = 24*24/6, + .ntrans = 1, +}; + +Coordinate +coord_epud = { + .index = index_epud, + .cube = antindex_epud, + .check = check_epud, + .max = FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4, + .ntrans = 1, +}; + +Coordinate +coord_htr_drud = { + .index = index_htr_drud, + .cube = antindex_htr_drud, + .check = check_drud, + .max = BINOM8ON4 * 6 * BINOM8ON4, + .ntrans = 1, +}; + +Coordinate +coord_htrfin = { + .index = index_htrfin, + .cube = antindex_htrfin, + .check = check_htr, + .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */ + .ntrans = 1, +}; + +Coordinate +coord_drud_eofb = { + .index = index_drud_eofb, + .cube = antindex_drud_eofb, + .check = check_drud, + .max = POW3TO7 * BINOM12ON4, + .ntrans = 1, +}; + +/* Functions *****************************************************************/ + +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + Cube ret = {0}; + + ret.eofb = ind % POW2TO11; + ret.epose = (ind / POW2TO11) * 24; + + return ret; +} + +static Cube +antindex_epud(uint64_t ind) +{ + static bool initialized = false; + static Cube epud_aux[FACTORIAL8]; + int a[12]; + uint64_t ui; + CubeArray arr; + + if (!initialized) { + a[FR] = FR; + a[FL] = FL; + a[BL] = BL; + a[BR] = BR; + for (ui = 0; ui < FACTORIAL8; ui++) { + index_to_perm(ui, 8, a); + arr.ep = a; + epud_aux[ui] = arrays_to_cube(&arr, pf_ep); + } + + initialized = true; + } + + return epud_aux[ind]; +} + +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +static Cube +antindex_cp(uint64_t ind) +{ + Cube c = {0}; + + c.cp = ind; + + return c; +} + +static Cube +antindex_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = antindex_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +static Cube +antindex_cornershtrfin(uint64_t ind) +{ + return (Cube){ .cp = cornershtrfin_ant[ind] }; +} + +static Cube +antindex_drud(uint64_t ind) +{ + uint64_t epos, eofb; + Cube c; + + eofb = ind % POW2TO11; + epos = ind / (POW2TO11 * POW3TO7); + c = antindex_eofbepos(eofb + POW2TO11 * epos); + + c.coud = (ind / POW2TO11) % POW3TO7; + + return c; +} + +static Cube +antindex_drud_eofb(uint64_t ind) +{ + return antindex_drud(ind * POW2TO11); +} + +static Cube +antindex_htr_drud(uint64_t ind) +{ + Cube ret; + + ret = antindex_cphtr(ind / BINOM8ON4); + ret.eposs = (ind % BINOM8ON4) * FACTORIAL4; + + return ret; +} + +static Cube +antindex_htrfin(uint64_t ind) +{ + Cube ret; + + ret = antindex_cornershtrfin(ind/(24*24*24)); + + ret.eposm = ind % 24; + ind /= 24; + ret.eposs = ind % 24; + ind /= 24; + ret.epose = ind % 24; + + return ret; +} + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cp(Cube cube) +{ + return cube.cp == 0; +} + +bool +check_cphtr(Cube cube) +{ + return index_cphtr(cube) == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && index_cphtr(cube) == 0; +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_htr(Cube cube) +{ + return check_cornershtr(cube) && + cube.eofb == 0 && cube.eorl == 0 && cube.eoud == 0; +} + +bool +check_drudfin_noE(Cube cube) +{ + return cube.eposs == 0 && cube.eposm == 0 && cube.cp == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_epud(Cube cube) +{ + return cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_epud(Cube cube) +{ + uint64_t ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); + + return ret; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cp(Cube cube) +{ + return cube.cp; +} + +static uint64_t +index_cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube); +} + +static uint64_t +index_cornershtrfin(Cube cube) +{ + return cornershtrfin_ind[cube.cp]; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_drud_eofb(Cube cube) +{ + return index_drud(cube) / POW2TO11; +} + +static uint64_t +index_htr_drud(Cube cube) +{ + return index_cphtr(cube) * BINOM8ON4 + + (cube.eposs / FACTORIAL4) % BINOM8ON4; +} + +static uint64_t +index_htrfin(Cube cube) +{ + uint64_t epe, eps, epm, cp, ep; + + epe = cube.epose % 24; + eps = cube.eposs % 24; + epm = cube.eposm % 24; + ep = (epe * 24 + eps) *24 + epm; + cp = index_cornershtrfin(cube); + + return cp * 24 * 24 * 24 + ep; +} + +/* Init functions implementation *********************************************/ + +/* + * There is certainly a better way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + /*jj = cp_mtable[moves[k]][next[j]];*/ + /* TODO fix formatting */ + jj = apply_move(moves[k], (Cube){.cp=next[j]}).cp; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_cornershtrfin() +{ + unsigned int i, j; + int n, c; + Move m; + + for (i = 0; i < FACTORIAL8; i++) + cornershtrfin_ind[i] = -1; + cornershtrfin_ind[0] = 0; + + /* 10-pass, I think 5 is enough, but just in case */ + n = 1; + for (i = 0; i < 10; i++) { + for (j = 0; j < FACTORIAL8; j++) { + if (cornershtrfin_ind[j] == -1) + continue; + for (m = U; m < NMOVES; m++) { + if (moveset_htr(m)) { + c = apply_move(m, (Cube){.cp = j}).cp; + if (cornershtrfin_ind[c] == -1) { + cornershtrfin_ind[c] = n; + cornershtrfin_ant[n] = c; + n++; + } + } + } + } + } +} + +void +init_coord() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_cphtr_cosets(); + init_cornershtrfin(); +} + diff --git a/old/2021-11-10-beforeremovingchecker/coord.h b/old/2021-11-10-beforeremovingchecker/coord.h @@ -0,0 +1,40 @@ +#ifndef COORD_H +#define COORD_H + +#include "trans.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_cp; +extern Coordinate coord_cphtr; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_cornershtrfin; +extern Coordinate coord_epud; +extern Coordinate coord_drud; +extern Coordinate coord_drud_eofb; +extern Coordinate coord_htr_drud; +extern Coordinate coord_htrfin; + +bool check_centers(Cube cube); +bool check_corners(Cube cube); +bool check_cp(Cube cube); +bool check_cphtr(Cube cube); +bool check_cornershtr(Cube cube); +bool check_coud(Cube cube); +bool check_drud(Cube cube); +bool check_htr(Cube cube); +bool check_drudfin_noE(Cube cube); +bool check_eofb(Cube cube); +bool check_eofbepos(Cube cube); +bool check_epose(Cube cube); +bool check_ep(Cube cube); +bool check_epud(Cube cube); +bool check_khuge(Cube cube); +bool check_nothing(Cube cube); + +void init_coord(); + +#endif + diff --git a/old/2021-11-10-beforeremovingchecker/cube.c b/old/2021-11-10-beforeremovingchecker/cube.c @@ -0,0 +1,716 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static int array_ep_to_epos(int *ep, int *eps_solved); +static int epos_from_arrays(int *epos, int *ep); + +/* Local functions implementation ********************************************/ + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +/* Public functions implementation *******************************************/ + +Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + + +/* TODO: consider if this is good here or better in coord.c + in any case it is used in transformation init at the moment */ +Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* TODO: check that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube) +{ + /* TODO: move somewhere else, like in solve.c + int i; + if (reorient) { + for (i = 0; i < NROTATIONS; i++) + if (is_solved(apply_alg(rotation_algs[i], cube),false)) + return true; + return false; + } else { + return equal(cube, (Cube){0}); + } + */ + + return equal(cube, (Cube){0}); +} + +bool +is_solved_block(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + static char edge_string[12][7] = { + [UF] = "UF", [UL] = "UL", [UB] = "UB", [UR] = "UR", + [DF] = "DF", [DL] = "DL", [DB] = "DB", [DR] = "DR", + [FR] = "FR", [FL] = "FL", [BL] = "BL", [BR] = "BR" + }; + + static char corner_string[8][7] = { + [UFR] = "UFR", [UFL] = "UFL", [UBL] = "UBL", [UBR] = "UBR", + [DFR] = "DFR", [DFL] = "DFL", [DBL] = "DBL", [DBR] = "DBR" + }; + + static char center_string[6][7] = { + [U_center] = "U", [D_center] = "D", + [R_center] = "R", [L_center] = "L", + [F_center] = "F", [B_center] = "B" + }; + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +Center +what_center_at(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][i] = arr->cpos[i]; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][i] = arr->cp[i]; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + + return aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + static bool initialized = false; + static int auxlast[POW3TO7]; + static int auxarr[8]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + auxlast[ui] = auxarr[7]; + } + + initialized = true; + } + + if (c < 7) + return (co / powint(3, c)) % 3; + else + return auxlast[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + static bool initialized = false; + static int auxlast[POW2TO11]; + static int auxarr[12]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + auxlast[ui] = auxarr[11]; + } + + initialized = true; + } + + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return auxlast[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][arr->cpos[i]] = i; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][arr->cp[i]] = i; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + return aux[cube.cp][c]; +} + +Edge +where_is_edge(Cube cube, Edge e) +{ + /* TODO: when I wrote this code I forgot to add the final + part, and now I can't remember how it was supposed to + work (i.e. how to recover the location of the edge + from these tables. I think it is either very easy or + wrong, in any case it is not a priority now. + Future Seba can deal with it. + + static bool initialized = false; + static Edge aux[3][FACTORIAL12/FACTORIAL8][12]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + initialized = true; + } + */ + + int i; + CubeArray *arr = new_cubearray(cube, pf_ep); + + for (i = 0; i < 12; i++) + if ((Edge)arr->ep[i] == e) + return i; + + return -1; +} diff --git a/old/2021-11-10-beforeremovingchecker/cube.h b/old/2021-11-10-beforeremovingchecker/cube.h @@ -0,0 +1,40 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <time.h> + +#include "pf.h" +#include "utils.h" + +Cube admissible_ep(Cube cube, PieceFilter f); /* TODO: move? */ +Cube arrays_to_cube(CubeArray *arr, PieceFilter f); /* TODO: remove */ +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +int edge_slice(Edge e); /* E=0, S=1, M=2 */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +bool is_admissible(Cube cube); +bool is_solved(Cube cube); +bool block_solved(Cube cube, Block); /*TODO: rename to is_solved_block()*/ +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void epos_to_partial_ep(int epos, int *ep, int *ss); +void free_cubearray(CubeArray *arr, PieceFilter f); /* TODO: remove */ +Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +CubeArray * new_cubearray(Cube cube, PieceFilter f); /* TODO: remove */ +void print_cube(Cube cube); +Cube random_cube(); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +#endif + diff --git a/old/2021-11-10-beforeremovingchecker/cubetypes.h b/old/2021-11-10-beforeremovingchecker/cubetypes.h @@ -0,0 +1,298 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +#include <stdbool.h> +#include <stdint.h> + +#define NMOVES 55 /* Actually 55, but one is NULLMOVE */ +#define NTRANS 48 +#define NROTATIONS 24 + + +/* Typedefs ******************************************************************/ + +typedef enum center Center; +typedef enum corner Corner; +typedef enum edge Edge; +typedef enum move Move; +typedef enum trans Trans; + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct command Command; +typedef struct commandargs CommandArgs; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef bool (*Validator) (Alg *); +typedef void (*Exec) (CommandArgs *); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); +typedef CommandArgs * (*ArgParser) (int, char **); +typedef Trans (*TransDetector) (Cube); + + +/* Enums *********************************************************************/ + +enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +}; + +enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +}; + +enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +}; + +enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +}; + +enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +}; + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +command +{ + /* TODO: more stuff to add? maybe complete help? */ + /* Maybe add list of options */ + char * name; + char * usage; + char * description; + ArgParser parse_args; + Exec exec; +}; + +struct +commandargs +{ + bool success; + Alg * scramble; + SolveOptions * opts; + Step * step; + Command * command; /* For help */ +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + Checker check; + uint64_t max; + int ntrans; + Trans * trans; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; +}; + +struct +solveoptions +{ + /* TODO: add option to list *all* solutions satisfying other + constraints (min/max moves and optimality) */ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool feedback; /* TODO: rename with "verbose" */ + bool all; + bool print_number; +}; + +struct +step +{ + char * shortname; + char * name; + Estimator estimate; + Checker ready; + char * ready_msg; + Validator is_valid; + Moveset moveset; + Trans pre_trans; + TransDetector detect; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/env.c b/old/2021-11-10-beforeremovingchecker/env.c @@ -0,0 +1,45 @@ +#include "env.h" + +bool initialized_env = false; +char *tabledir; + +void +init_env() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (initialized_env) + return; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } + + initialized_env = true; +} diff --git a/old/2021-11-10-beforeremovingchecker/env.h b/old/2021-11-10-beforeremovingchecker/env.h @@ -0,0 +1,15 @@ +#ifndef ENV_H +#define ENV_H + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> + +extern char *tabledir; + +void init_env(); + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/moves.c b/old/2021-11-10-beforeremovingchecker/moves.c @@ -0,0 +1,474 @@ +#include "moves.h" + +/* Local functions ***********************************************************/ + +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static bool read_mtables_file(); +static bool write_mtables_file(); + +/* Tables and other data *****************************************************/ + +/* Every move is translated to a an <U, x, y> alg before filling the + transition tables, see init_moves() */ + +static int edge_cycle[NMOVES][12] = +{ + [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, + [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, + [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL } +}; + +static int corner_cycle[NMOVES][8] = +{ + [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR }, + [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR }, + [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL } +}; + +static int center_cycle[NMOVES][6] = +{ + [x] = { F_center, B_center, R_center, L_center, D_center, U_center }, + [y] = { U_center, D_center, B_center, F_center, R_center, L_center } +}; + +static int eofb_flipped[NMOVES][12] = { + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; + +static int eorl_flipped[NMOVES][12] = { + [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; + +static int eoud_flipped[NMOVES][12] = { + [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } +}; + +static int coud_flipped[NMOVES][8] = { + [x] = { + [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, + [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 + } +}; + +static int corl_flipped[NMOVES][8] = { + [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + } +}; + +static int cofb_flipped[NMOVES][8] = { + [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + }, + [y] = { + [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1, + [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2 + } +}; + +static char equiv_alg_string[100][NMOVES] = { + [NULLMOVE] = "", + + [U] = " U ", + [U2] = " UU ", + [U3] = " UUU ", + [D] = " xx U xx ", + [D2] = " xx UU xx ", + [D3] = " xx UUU xx ", + [R] = " yx U xxxyyy ", + [R2] = " yx UU xxxyyy ", + [R3] = " yx UUU xxxyyy ", + [L] = " yyyx U xxxy ", + [L2] = " yyyx UU xxxy ", + [L3] = " yyyx UUU xxxy ", + [F] = " x U xxx ", + [F2] = " x UU xxx ", + [F3] = " x UUU xxx ", + [B] = " xxx U x ", + [B2] = " xxx UU x ", + [B3] = " xxx UUU x ", + + [Uw] = " xx U xx y ", + [Uw2] = " xx UU xx yy ", + [Uw3] = " xx UUU xx yyy ", + [Dw] = " U yyy ", + [Dw2] = " UU yy ", + [Dw3] = " UUU y ", + [Rw] = " yyyx U xxxy x ", + [Rw2] = " yyyx UU xxxy xx ", + [Rw3] = " yyyx UUU xxxy xxx ", + [Lw] = " yx U xxxyyy xxx ", + [Lw2] = " yx UU xxxyyy xx ", + [Lw3] = " yx UUU xxxyyy x ", + [Fw] = " xxx U x yxxxyyy ", + [Fw2] = " xxx UU x yxxyyy ", + [Fw3] = " xxx UUU x yxyyy ", + [Bw] = " x U xxx yxyyy ", + [Bw2] = " x UU xxx yxxyyy ", + [Bw3] = " x UUU xxx yxxxyyy ", + + [M] = " yx U xx UUU yxyyy ", + [M2] = " yx UU xx UU xxxy ", + [M3] = " yx UUU xx U yxxxy ", + [S] = " x UUU xx U yyyx ", + [S2] = " x UU xx UU yyx ", + [S3] = " x U xx UUU yx ", + [E] = " U xx UUU xxyyy ", + [E2] = " UU xx UU xxyy ", + [E3] = " UUU xx U xxy ", + + [x] = " x ", + [x2] = " xx ", + [x3] = " xxx ", + [y] = " y ", + [y2] = " yy ", + [y3] = " yyy ", + [z] = " yyy x y ", + [z2] = " yy xx ", + [z3] = " y x yyy " +}; + +/* Transition tables, to be loaded up at the beginning */ +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + + +/* Local functions implementation ********************************************/ + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + /*init_moves();*/ + + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +/* Public functions **********************************************************/ + +Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + /*init_moves();*/ + + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + +void +init_moves() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + Alg *equiv_alg[NMOVES]; + + for (i = 0; i < NMOVES; i++) + equiv_alg[i] = new_alg(equiv_alg_string[i]); + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); + + for (i = 0; i < NMOVES; i++) + free_alg(equiv_alg[i]); +} + +static bool +read_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +bool +commute(Move m1, Move m2) +{ + static bool initialized = false; + static bool commute_aux[NMOVES][NMOVES]; + + if (!initialized) { + Cube c1, c2; + int i, j; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute_aux[i][j] = equal(c1, c2) && i && j; + } + } + + initialized = true; + } + + return commute_aux[m1][m2]; +} + +bool +possible_next(Move m1, Move m2, Move m3) +{ + static bool initialized = false; + static bool paux[NMOVES][NMOVES][NMOVES]; + + if (!initialized) { + int i, j, k; + bool p, q, c; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p = j && base_move(j) == base_move(k); + q = i && base_move(i) == base_move(k); + c = commute(i, j); + paux[i][j][k] = !(p || (c && q)); + } + } + } + + initialized = true; + } + + return paux[m1][m2][m3]; +} diff --git a/old/2021-11-10-beforeremovingchecker/moves.h b/old/2021-11-10-beforeremovingchecker/moves.h @@ -0,0 +1,16 @@ +#ifndef MOVES_H +#define MOVES_H + +#include "alg.h" +#include "cube.h" +#include "env.h" + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +Cube apply_move(Move m, Cube cube); +bool commute(Move m1, Move m2); +bool possible_next(Move m1, Move m2, Move m3); + +void init_moves(); + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/pf.c b/old/2021-11-10-beforeremovingchecker/pf.c @@ -0,0 +1,80 @@ +#include "pf.h" + +PieceFilter +pf_all = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .eorl = true, + .eoud = true, + .cp = true, + .cofb = true, + .corl = true, + .coud = true, + .cpos = true +}; + +PieceFilter +pf_4val = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .coud = true, + .cp = true +}; + +PieceFilter +pf_epcp = { + .epose = true, + .eposs = true, + .eposm = true, + .cp = true +}; + +PieceFilter +pf_cpos = { + .cpos = true +}; + +PieceFilter +pf_cp = { + .cp = true +}; + +PieceFilter +pf_ep = { + .epose = true, + .eposs = true, + .eposm = true +}; + +PieceFilter +pf_e = { + .epose = true +}; + +PieceFilter +pf_s = { + .eposs = true +}; + +PieceFilter +pf_m = { + .eposm = true +}; + +PieceFilter +pf_eo = { + .eofb = true, + .eorl = true, + .eoud = true +}; + +PieceFilter +pf_co = { + .cofb = true, + .corl = true, + .coud = true +}; diff --git a/old/2021-11-10-beforeremovingchecker/pf.h b/old/2021-11-10-beforeremovingchecker/pf.h @@ -0,0 +1,18 @@ +#ifndef PF_H +#define PF_H + +#include "cubetypes.h" + +extern PieceFilter pf_all; +extern PieceFilter pf_4val; +extern PieceFilter pf_epcp; +extern PieceFilter pf_cpos; +extern PieceFilter pf_cp; +extern PieceFilter pf_ep; +extern PieceFilter pf_e; +extern PieceFilter pf_s; +extern PieceFilter pf_m; +extern PieceFilter pf_eo; +extern PieceFilter pf_co; + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/pruning.c b/old/2021-11-10-beforeremovingchecker/pruning.c @@ -0,0 +1,272 @@ +#include "pruning.h" + +static void genptable_bfs(PruneData *pd, int d, Move *ms); +static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void ptable_update_index(PruneData *pd, uint64_t ind, int m); +static int ptableval_index(PruneData *pd, uint64_t ind); +static bool read_ptable_file(PruneData *pd); +static bool write_ptable_file(PruneData *pd); + +PruneData +pd_eofb_HTM = { + .filename = "pt_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM, +}; + +PruneData +pd_coud_HTM = { + .filename = "pt_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM, +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "pt_cornershtr_withcosets_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM, +}; + +PruneData +pd_corners_HTM = { + .filename = "pt_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM, +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "pt_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, +}; + +PruneData +pd_drud_eofb = { + .filename = "pt_drud_eofb", + .coord = &coord_drud_eofb, + .moveset = moveset_eofb, +}; + +PruneData +pd_drudfin_noE_sym16_drud = { + .filename = "pt_drudfin_noE_sym16_drud", + .coord = &coord_drudfin_noE_sym16, + .moveset = moveset_drud, +}; + +PruneData +pd_htr_drud = { + .filename = "pt_htr_drud", + .coord = &coord_htr_drud, + .moveset = moveset_drud, +}; + +PruneData +pd_htrfin_htr = { + .filename = "pt_htrfin_htr", + .coord = &coord_htrfin, + .moveset = moveset_htr, +}; + +PruneData +pd_khuge_HTM = { + .filename = "pt_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM, +}; + +void +genptable(PruneData *pd) +{ + Move ms[NMOVES]; + int d; + uint64_t j, oldn; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe exit gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + pd->generated = true; + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + moveset_to_list(pd->moveset, ms); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update_index(pd, j, 15); + + for (j = 0; j < pd->coord->max; j++) + if (ptableval_index(pd, j) != 15) { + printf("Error, non-max value at index %lu!\n", j); + break; + } + printf("Table set, ready to start\n"); + + /*TODO: change, set to 0 for every solved state (might be more than 1)*/ + ptable_update(pd, (Cube){0}, 0); + pd->n = 1; + oldn = 0; + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + 0, pd->n - oldn, pd->n, pd->coord->max); + oldn = 1; + for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { + genptable_bfs(pd, d, ms); + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + d+1, pd->n - oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); +} + +static void +genptable_bfs(PruneData *pd, int d, Move *ms) +{ + uint64_t i; + + for (i = 0; i < pd->coord->max; i++) + if (ptableval_index(pd, i) == d) + genptable_branch(pd, i, d, ms); +} + +static void +genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms) +{ + int i, j; + Cube ci, cc, c; + + /* + * This is the only line of the whole program where we REALLY need an + * anti-indexer function. We could get rid of it if only we could save + * a cube object for each index value as we go, but then we would need + * an incredible amount of memory to generate each ptable: assuming + * fields in struct cube are 32 bit ints that would take 88 times the + * memory of the table to be generated, more than 120Gb for + * ptable_khuge for example! + */ + ci = pd->coord->cube(ind); + + for (i = 0; i < pd->coord->ntrans; i++) { + /* For simplicity trans[] is NULL when ntrans = 1 */ + c = i == 0 ? ci : + apply_trans(pd->coord->trans[i], ci); + for (j = 0; ms[j] != NULLMOVE; j++) { + cc = apply_move(ms[j], c); + if (ptableval(pd, cc) > d+1) + ptable_update(pd, cc, d+1); + } + } +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval_index(pd, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + ptable_update_index(pd, ind, n); +} + +static void +ptable_update_index(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + return ptableval_index(pd, pd->coord->index(cube)); +} + +static int +ptableval_index(PruneData *pd, uint64_t ind) +{ + if (!pd->generated) + genptable(pd); + + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + +static bool +read_ptable_file(PruneData *pd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +write_ptable_file(PruneData *pd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + diff --git a/old/2021-11-10-beforeremovingchecker/pruning.h b/old/2021-11-10-beforeremovingchecker/pruning.h @@ -0,0 +1,23 @@ +#ifndef PRUNING_H +#define PRUNING_H + +#include "symcoord.h" + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_drud_sym16_HTM; +extern PruneData pd_drud_eofb; +extern PruneData pd_drudfin_noE_sym16_drud; +extern PruneData pd_htr_drud; +extern PruneData pd_htrfin_htr; +extern PruneData pd_khuge_HTM; + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +#endif + diff --git a/old/2021-11-10-beforeremovingchecker/shell.c b/old/2021-11-10-beforeremovingchecker/shell.c @@ -0,0 +1,99 @@ +#include "shell.h" + +static void cleanwhitespaces(char *line); +static int parseline(char *line, char **v); + +static void +cleanwhitespaces(char *line) +{ + char *i; + + for (i = line; *i != 0; i++) + if (*i == '\t' || *i == '\n') + *i = ' '; +} + +/* This function assumes that **v is large enough */ +static int +parseline(char *line, char **v) +{ + char *t; + int n = 0; + + cleanwhitespaces(line); + + for (t = strtok(line, " "); t != NULL; t = strtok(NULL, " ")) + strcpy(v[n++], t); + + return n; +} + +void +exec_args(int c, char **v) +{ + int i; + Command *cmd = NULL; + CommandArgs *args; + + for (i = 0; i < NCOMMANDS; i++) + if (commands[i] != NULL && !strcmp(v[0], commands[i]->name)) + cmd = commands[i]; + + if (cmd == NULL) { + fprintf(stderr, "%s: command not found\n", v[0]); + return; + } + + args = cmd->parse_args(c-1, &v[1]); + if (!args->success) { + fprintf(stderr, "usage: %s\n", cmd->usage); + return; + } + + cmd->exec(args); + free_args(args); +} + +void +launch() +{ + int i, shell_argc; + char line[MAXLINELEN], **shell_argv; + + shell_argv = malloc(MAXNTOKENS * sizeof(char *)); + for (i = 0; i < MAXNTOKENS; i++) + shell_argv[i] = malloc((MAXTOKENLEN+1) * sizeof(char)); + + fprintf(stderr, "Welcome to Nissy 2.0 (demo version).\n"); + fprintf(stderr, "Limited commands available. "); + fprintf(stderr, "Type 'help' for a list.\n"); + + while (true) { + fprintf(stderr, "nissy-# "); + if (fgets(line, MAXLINELEN, stdin) == NULL) + break; + shell_argc = parseline(line, shell_argv); + exec_args(shell_argc, shell_argv); + } + + for (i = 0; i < MAXNTOKENS; i++) + free(shell_argv[i]); + free(shell_argv); +} + +/* We will have our main() here, for now */ +int +main(int argc, char *argv[]) +{ + init_moves(); + init_trans(); + init_coord(); + init_symcoord(); + + if (argc > 1) + exec_args(argc-1, &argv[1]); + else + launch(); + + return 0; +} diff --git a/old/2021-11-10-beforeremovingchecker/shell.h b/old/2021-11-10-beforeremovingchecker/shell.h @@ -0,0 +1,13 @@ +#ifndef SHELL_H +#define SHELL_H + +#include "commands.h" + +#define MAXLINELEN 10000 +#define MAXTOKENLEN 255 +#define MAXNTOKENS 255 + +void exec_args(int c, char **v); +void launch(); + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/solve.c b/old/2021-11-10-beforeremovingchecker/solve.c @@ -0,0 +1,209 @@ +#include "solve.h" + +/* Local functions ***********************************************************/ + +static bool allowed_next(Move move, DfsData *dd); +static void dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step *s, SolveOptions *os, DfsData *dd); +static bool dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd); + +/* Local functions ***********************************************************/ + +static bool +allowed_next(Move move, DfsData *dd) +{ + +/* TODO: remove the commented part, was added to moves.c + static bool initialized = false; + static bool commute[NMOVES][NMOVES], pnext[NMOVES][NMOVES][NMOVES]; + + if (!initialized) { + Cube c1, c2; + int i, j, k; + bool p1, p2, cij; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute[i][j] = equal(c1, c2) && i && j; + for (k = 0; k < NMOVES; k++) { + p1 = j && base_move(j) == base_move(k); + p2 = i && base_move(i) == base_move(k); + cij = commute[i][j]; + pnext[i][j][k] = !(p1 || (cij && p2)); + } + } + } + + initialized = true; + } + + if (!pnext[dd->last2][dd->last1][move]) + return false; + + if (commute[dd->last1][move]) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +*/ + + if (!possible_next(dd->last2, dd->last1, move)) + return false; + + if (commute(dd->last1, move)) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(s, opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + if (s->is_valid(dd->current_alg) || opts->all) + append_alg(dd->sols, dd->current_alg); + + if (opts->feedback) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s->estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s->estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +/* Public functions **********************************************************/ + +AlgList * +solve(Cube cube, Step *step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c; + + if (step->detect != NULL) + step->pre_trans = step->detect(cube); + c = apply_trans(step->pre_trans, cube); + + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step->ready != NULL && !step->ready(c)) { + fprintf(stderr, "Cube not ready for solving step: "); + fprintf(stderr, "%s\n", step->ready_msg); + return sols; + } + + moveset_to_list(step->moveset, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && + !(sols->len && opts->optimal_only) && + sols->len < opts->max_solutions; + dd.d++) { + if (opts->feedback) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(c, step, opts, &dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(step->pre_trans), node->alg); + + free_alg(dd.current_alg); + return sols; +} diff --git a/old/2021-11-10-beforeremovingchecker/solve.h b/old/2021-11-10-beforeremovingchecker/solve.h @@ -0,0 +1,9 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include "moves.h" +#include "trans.h" + +AlgList * solve(Cube cube, Step *step, SolveOptions *opts); + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/steps.c b/old/2021-11-10-beforeremovingchecker/steps.c @@ -0,0 +1,916 @@ +#include "steps.h" + +/* Standard checkers (return lower bound) ************************************/ + +static int estimate_eoany_HTM(CubeTarget ct); +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coany_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coany_URF(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drany_HTM(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_drud_eofb(CubeTarget ct); +static int estimate_dr_eofb(CubeTarget ct); +static int estimate_drudfin_drud(CubeTarget ct); +static int estimate_htr_drud(CubeTarget ct); +static int estimate_htrfin_htr(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +/* Validators ****************************************************************/ + +static bool always_valid(Alg *alg); +static bool validate_singlecw_ending(Alg *alg); + +/* Pre-transformation detectors **********************************************/ + +static Trans detect_pretrans_eofb(Cube cube); +static Trans detect_pretrans_drud(Cube cube); + +/* Messages for when cube is not ready ***************************************/ + +static char check_centers_msg[100] = "cube must be oriented (centers solved)"; +static char check_eo_msg[100] = "EO must be solved on given axis"; +static char check_dr_msg[100] = "DR must be solved on given axis"; +static char check_htr_msg[100] = "HTR must be solved"; +static char check_drany_msg[100] = "DR must be solved on at least one axis"; + +/* Steps *********************************************************************/ + +Step +optimal_HTM = { + .shortname = "optimal", + .name = "Optimal solve (in HTM)", + + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = always_valid, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +/* EO steps **************************/ +Step +eoany_HTM = { + .shortname = "eo", + .name = "EO on any axis", + + .estimate = estimate_eoany_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +eofb_HTM = { + .shortname = "eofb", + .name = "EO on F/B", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +eorl_HTM = { + .shortname = "eorl", + .name = "EO on R/L", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = ur, +}; + +Step +eoud_HTM = { + .shortname = "eoud", + .name = "EO on U/D", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +/* CO steps **************************/ +Step +coany_HTM = { + .shortname = "co", + .name = "CO on any axis", + + .estimate = estimate_coany_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +coud_HTM = { + .shortname = "coud", + .name = "CO on U/D", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +corl_HTM = { + .shortname = "corl", + .name = "CO on R/L", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = rf, +}; + +Step +cofb_HTM = { + .shortname = "cofb", + .name = "CO on F/B", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +Step +coany_URF = { + .shortname = "co-URF", + .name = "CO any axis (URF moveset)", + + .estimate = estimate_coany_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +coud_URF = { + .shortname = "coud-URF", + .name = "CO on U/D (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +corl_URF = { + .shortname = "corl-URF", + .name = "CO on R/L (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = rf, +}; + +Step +cofb_URF = { + .shortname = "cofb-URF", + .name = "CO on F/B (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = fd, +}; + +/* Misc corner steps *****************/ +Step +cornershtr_HTM = { + .shortname = "chtr", + .name = "Solve corners to HTR state", + + .estimate = estimate_cornershtr_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +cornershtr_URF = { + .shortname = "chtr-URF", + .name = "Solve corners to HTR state (URF moveset)", + + .estimate = estimate_cornershtr_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +corners_HTM = { + .shortname = "corners", + .name = "Solve corners", + + .estimate = estimate_corners_HTM, + .ready = NULL, + .is_valid = always_valid, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +corners_URF = { + .shortname = "corners-URF", + .name = "Solve corners (URF moveset)", + + .estimate = estimate_corners_URF, + .ready = NULL, + .is_valid = always_valid, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +/* DR steps **************************/ +Step +drany_HTM = { + .shortname = "dr", + .name = "DR on any axis", + + .estimate = estimate_drany_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +drud_HTM = { + .shortname = "drud", + .name = "DR on U/D", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +drrl_HTM = { + .shortname = "drrl", + .name = "DR on R/L", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = rf, +}; + +Step +drfb_HTM = { + .shortname = "drfb", + .name = "DR on F/B", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +/* DR from EO */ +Step +dr_eo = { + .shortname = "dr-eo", + .name = "DR without breaking EO (automatically detected)", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .detect = detect_pretrans_eofb, +}; + +Step +dr_eofb = { + .shortname = "dr-eofb", + .name = "DR on U/D or R/L without breaking EO on F/B", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = uf, +}; + +Step +dr_eorl = { + .shortname = "dr-eorl", + .name = "DR on U/D or F/B without breaking EO on R/L", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = ur, +}; + +Step +dr_eoud = { + .shortname = "dr-eoud", + .name = "DR on R/L or F/B without breaking EO on U/R", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fd, +}; + +Step +drud_eofb = { + .shortname = "drud-eofb", + .name = "DR on U/D without breaking EO on F/B", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = uf, +}; + +Step +drrl_eofb = { + .shortname = "drrl-eofb", + .name = "DR on R/L without breaking EO on F/B", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = rf, +}; + +Step +drud_eorl = { + .shortname = "drud-eorl", + .name = "DR on U/D without breaking EO on R/L", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = ur, +}; + +Step +drfb_eorl = { + .shortname = "drfb-eorl", + .name = "DR on F/B without breaking EO on R/L", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fr, +}; + +Step +drfb_eoud = { + .shortname = "drfb-eoud", + .name = "DR on F/B without breaking EO on U/D", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fd, +}; + +Step +drrl_eoud = { + .shortname = "drrl-eoud", + .name = "DR on R/L without breaking EO on U/D", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = rd, +}; + +/* DR finish steps */ +Step +dranyfin_DR = { + .shortname = "drfin", + .name = "DR finish on any axis without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .detect = detect_pretrans_drud, +}; + +Step +drudfin_drud = { + .shortname = "drudfin", + .name = "DR finish on U/D without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = uf, +}; + +Step +drrlfin_drrl = { + .shortname = "drrlfin", + .name = "DR finish on R/L without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = rf, +}; + +Step +drfbfin_drfb = { + .shortname = "drfbfin", + .name = "DR finish on F/B without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = fd, +}; + +/* HTR from DR */ +Step +htr_any = { + .shortname = "htr", + .name = "HTR from DR", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .detect = detect_pretrans_drud, +}; + +Step +htr_drud = { + .shortname = "htr-drud", + .name = "HTR from DR on U/D", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = uf, +}; + +Step +htr_drrl = { + .shortname = "htr-drrl", + .name = "HTR from DR on R/L", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = rf, +}; + +Step +htr_drfb = { + .shortname = "htr-drfb", + .name = "HTR from DR on F/B", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = fd, +}; + +/* HTR finish */ +Step +htrfin_htr = { + .shortname = "htrfin", + .name = "HTR finish without breaking HTR", + + .estimate = estimate_htrfin_htr, + .ready = check_htr, + .ready_msg = check_htr_msg, + .is_valid = always_valid, + .moveset = moveset_htr, + + .pre_trans = uf, +}; + +Step *steps[NSTEPS] = { + &optimal_HTM, /* first is default */ + + &eoany_HTM, + &eofb_HTM, + &eorl_HTM, + &eoud_HTM, + + &coany_HTM, + &coud_HTM, + &corl_HTM, + &cofb_HTM, + + &coany_URF, + &coud_URF, + &corl_URF, + &cofb_URF, + + &drany_HTM, + &drud_HTM, + &drrl_HTM, + &drfb_HTM, + + &dr_eo, + &dr_eofb, + &dr_eorl, + &dr_eoud, + &drud_eofb, + &drrl_eofb, + &drud_eorl, + &drfb_eorl, + &drfb_eoud, + &drrl_eoud, + + &dranyfin_DR, + &drudfin_drud, + &drrlfin_drrl, + &drfbfin_drfb, + + &htr_any, + &htr_drud, + &htr_drrl, + &htr_drfb, + + &htrfin_htr, + + &cornershtr_HTM, + &cornershtr_URF, + &corners_HTM, + &corners_URF, +}; + +/* Standard checkers (return lower bound) ************************************/ + +static int +estimate_eoany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_eofb_HTM, ct.cube); + r2 = ptableval(&pd_eofb_HTM, apply_trans(ur, ct.cube)); + r3 = ptableval(&pd_eofb_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_coud_HTM, ct.cube); + r2 = ptableval(&pd_coud_HTM, apply_trans(rf, ct.cube)); + r3 = ptableval(&pd_coud_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coany_URF(CubeTarget ct) +{ + int r1, r2, r3; + CubeTarget ct2, ct3; + + ct2.cube = apply_trans(rf, ct.cube); + ct2.target = ct.target; + + ct3.cube = apply_trans(fd, ct.cube); + ct3.target = ct.target; + + r1 = estimate_coud_URF(ct); + r2 = estimate_coud_URF(ct2); + r3 = estimate_coud_URF(ct3); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_drud_sym16_HTM, ct.cube); + r2 = ptableval(&pd_drud_sym16_HTM, apply_trans(rf, ct.cube)); + r3 = ptableval(&pd_drud_sym16_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +static int +estimate_drud_eofb(CubeTarget ct) +{ + return ptableval(&pd_drud_eofb, ct.cube); +} + +static int +estimate_dr_eofb(CubeTarget ct) +{ + int r1, r2; + + r1 = ptableval(&pd_drud_eofb, ct.cube); + r2 = ptableval(&pd_drud_eofb, apply_trans(rf, ct.cube)); + + return MIN(r1, r2); +} + +static int +estimate_drudfin_drud(CubeTarget ct) +{ + int val = ptableval(&pd_drudfin_noE_sym16_drud, ct.cube); + + if (val != 0) + return val; + + return ct.cube.epose % 24 == 0 ? 0 : 1; +} + +static int +estimate_htr_drud(CubeTarget ct) +{ + return ptableval(&pd_htr_drud, ct.cube); +} + +static int +estimate_htrfin_htr(CubeTarget ct) +{ + return ptableval(&pd_htrfin_htr, ct.cube); +} + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(rf, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(fd, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + /* Michiel de Bondt's trick */ + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + return MAX(ret, dr3); +} + +/* Validators ****************************************************************/ + +static bool +always_valid(Alg *alg) +{ + return true; +} + +static bool +validate_singlecw_ending(Alg *alg) +{ + int i; + bool nor, inv; + Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE; + + for (i = 0; i < alg->len; i++) { + if (alg->inv[i]) { + l2i = l1i; + l1i = alg->move[i]; + } else { + l2 = l1; + l1 = alg->move[i]; + } + } + + nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2)); + inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i)); + + return nor && inv; +} + +/* Pre-transformation detectors **********************************************/ + +static Trans +detect_pretrans_eofb(Cube cube) +{ + Trans i; + + for (i = 0; i < NROTATIONS; i++) + if (check_eofb(apply_trans(i, cube))) + return i; + + return 0; +} + +static Trans +detect_pretrans_drud(Cube cube) +{ + Trans i; + + for (i = 0; i < NROTATIONS; i++) + if (check_drud(apply_trans(i, cube))) + return i; + + return 0; +} diff --git a/old/2021-11-10-beforeremovingchecker/steps.h b/old/2021-11-10-beforeremovingchecker/steps.h @@ -0,0 +1,10 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "pruning.h" + +#define NSTEPS 50 + +extern Step * steps[NSTEPS]; + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/symcoord.c b/old/2021-11-10-beforeremovingchecker/symcoord.c @@ -0,0 +1,359 @@ +#include "symcoord.h" + +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_cp_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_drudfin_noE_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_cp_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_drudfin_noE_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static void gensym(SymData *sd); +static bool read_symdata_file(SymData *sd); +static bool write_symdata_file(SymData *sd); + +/* Transformation groups and symmetry data ***********************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +static SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static SymData +sd_cp_16 = { + .filename = "sd_cp_16", + .coord = &coord_cp, + .sym_coord = &coord_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int nsymdata = 3; +static SymData * all_sd[] = { + &sd_coud_16, + &sd_cp_16, + &sd_eofbepos_16, +}; + + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_cp_sym16 = { + .index = index_cp_sym16, + .cube = antindex_cp_sym16, + .check = check_cp, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drudfin_noE_sym16 = { + .index = index_drudfin_noE_sym16, + .cube = antindex_drudfin_noE_sym16, + .check = check_drudfin_noE, + .max = FACTORIAL8 * 2768, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +/* Functions *****************************************************************/ + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_cp_sym16(uint64_t ind) +{ + return sd_cp_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = antindex_eofbepos_sym16(ind/POW3TO7); + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_drudfin_noE_sym16(uint64_t ind) +{ + Cube c1, c2; + + c1 = coord_epud.cube(ind % FACTORIAL8); + c2 = antindex_cp_sym16(ind/FACTORIAL8); + c1.cp = c2.cp; + + return c1; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = antindex_eofbepos_sym16(ind/(FACTORIAL4*POW3TO7)); + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[coord_coud.index(cube)]; +} + +static uint64_t +index_cp_sym16(Cube cube) +{ + return sd_cp_16.class[coord_cp.index(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_drudfin_noE_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_cp_16.transtorep[coord_cp.index(cube)]; + c = apply_trans(t, cube); + + return index_cp_sym16(c) * FACTORIAL8 + coord_epud.index(c); +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[coord_eofbepos.index(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + +/* Other functions ***********************************************************/ + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static bool +read_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +void +init_symcoord() +{ + int i; + + static bool initialized = false; + if (initialized) + return; + initialized = true; + + for (i = 0; i < nsymdata; i++) + gensym(all_sd[i]); +} + diff --git a/old/2021-11-10-beforeremovingchecker/symcoord.h b/old/2021-11-10-beforeremovingchecker/symcoord.h @@ -0,0 +1,15 @@ +#ifndef SYMCOORD_H +#define SYMCOORD_H + +#include "coord.h" + +extern Coordinate coord_coud_sym16; +extern Coordinate coord_cp_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_drudfin_noE_sym16; +extern Coordinate coord_khuge; + +void init_symcoord(); + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/trans.c b/old/2021-11-10-beforeremovingchecker/trans.c @@ -0,0 +1,372 @@ +#include "trans.h" + +/* Local functions ***********************************************************/ + +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static bool write_ttables_file(); + +/* Tables and other data *****************************************************/ + +static int ep_mirror[12] = { + [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL, + [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL, + [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL +}; + +static int cp_mirror[8] = { + [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL, + [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL +}; + +static int cpos_mirror[6] = { + [U_center] = U_center, [D_center] = D_center, + [R_center] = L_center, [L_center] = R_center, + [F_center] = F_center, [B_center] = B_center +}; + +/* TODO Is there a more elegant way? */ +static char rotation_alg_string[100][NROTATIONS] = { + [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3", + [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2", + [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3", + [lf] = "z", [ld] = "z y3", [lb] = "z y2", [lu] = "z y", + [fu] = "x y2", [fr] = "x y", [fd] = "x", [fl] = "x y3", + [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3", +}; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +/* Local functions implementation ********************************************/ + +void +init_trans() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + Cube aux, cube, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_alg(i), (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_alg(m), (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + /* Old version: + * + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube) || is_solved(mirr)) + moves_ttable[m][mi] = move; + } + */ + + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + if (is_solved(cube)) { + moves_ttable[m][mi] = move; + break; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + +static bool +read_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fread(co_ttable[m], b, me[5], f) == me[5]; + r = r && fread(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fread(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_alg(r)); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_alg(r), ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static bool +write_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fwrite(co_ttable[m], b, me[5], f) == me[5]; + r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +/* Public functions **********************************************************/ + +Cube +apply_trans(Trans t, Cube cube) +{ + /*init_trans();*/ + + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Trans +inverse_trans(Trans t) +{ + /* TODO is there a more elegant way? */ + static Trans inverse_trans_aux[NTRANS] = { + [uf] = uf, [ur] = ul, [ul] = ur, [ub] = ub, + [df] = df, [dr] = dr, [dl] = dl, [db] = db, + [rf] = lf, [rd] = bl, [rb] = rb, [ru] = fr, + [lf] = rf, [ld] = br, [lb] = lb, [lu] = fl, + [fu] = fu, [fr] = ru, [fd] = bu, [fl] = lu, + [bu] = fd, [br] = ld, [bd] = bd, [bl] = rd, + + [uf_mirror] = uf_mirror, [ur_mirror] = ur_mirror, + [ul_mirror] = ul_mirror, [ub_mirror] = ub_mirror, + [df_mirror] = df_mirror, [dr_mirror] = dl_mirror, + [dl_mirror] = dr_mirror, [db_mirror] = db_mirror, + [rf_mirror] = rf_mirror, [rd_mirror] = br_mirror, + [rb_mirror] = lb_mirror, [ru_mirror] = fl_mirror, + [lf_mirror] = lf_mirror, [ld_mirror] = bl_mirror, + [lb_mirror] = rb_mirror, [lu_mirror] = fr_mirror, + [fu_mirror] = fu_mirror, [fr_mirror] = lu_mirror, + [fd_mirror] = bu_mirror, [fl_mirror] = ru_mirror, + [bu_mirror] = fd_mirror, [br_mirror] = rd_mirror, + [bd_mirror] = bd_mirror, [bl_mirror] = ld_mirror + }; + + return inverse_trans_aux[t]; +} + +Alg * +rotation_alg(Trans t) +{ + int i; + + static Alg *rotation_alg_arr[NROTATIONS]; + static bool initialized = false; + + if (!initialized) { + for (i = 0; i < NROTATIONS; i++) + rotation_alg_arr[i] = new_alg(rotation_alg_string[i]); + + initialized = true; + } + + return rotation_alg_arr[t % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + /*init_trans();*/ + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} diff --git a/old/2021-11-10-beforeremovingchecker/trans.h b/old/2021-11-10-beforeremovingchecker/trans.h @@ -0,0 +1,13 @@ +#ifndef TRANS_H +#define TRANS_H + +#include "moves.h" + +Cube apply_trans(Trans t, Cube cube); +Trans inverse_trans(Trans t); +Alg * rotation_alg(Trans i); +void transform_alg(Trans i, Alg *alg); + +void init_trans(); + +#endif diff --git a/old/2021-11-10-beforeremovingchecker/utils.c b/old/2021-11-10-beforeremovingchecker/utils.c @@ -0,0 +1,274 @@ +#include "utils.h" + +void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + diff --git a/old/2021-11-10-beforeremovingchecker/utils.h b/old/2021-11-10-beforeremovingchecker/utils.h @@ -0,0 +1,41 @@ +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +void apply_permutation(int *perm, int *set, int n); +int binomial(int n, int k); +int digit_array_to_int(int *a, int n, int b); +int factorial(int n); +void index_to_perm(int p, int n, int *r); +void index_to_subset(int s, int n, int k, int *r); +void int_to_digit_array(int a, int b, int n, int *r); +void int_to_sum_zero_array(int x, int b, int n, int *a); +int invert_digits(int a, int b, int n); +bool is_perm(int *a, int n); +bool is_subset(int *a, int n, int k); +int perm_sign(int *a, int n); +int perm_to_index(int *a, int n); +int powint(int a, int b); +int subset_to_index(int *a, int n, int k); +void sum_arrays_mod(int *src, int *dst, int n, int m); +void swap(int *a, int *b); + +#endif diff --git a/old/backup-manysteps-pretrans/steps.c b/old/backup-manysteps-pretrans/steps.c @@ -0,0 +1,369 @@ +#include "steps.h" + +/* Check functions ***********************************************************/ + +static int check_nothing(Cube cube); +static int check_eofb_HTM(Cube cube); +static int check_coud_HTM(Cube cube); +static int check_coud_URF(Cube cube); +static int check_corners_HTM(Cube cube); +static int check_corners_URF(Cube cube); +static int check_edges_HTM(Cube cube); +static int check_drud_HTM(Cube cube); +static int check_optimal_HTM(Cube cube); + + +/* Index functions ***********************************************************/ + +static uint64_t index_eofb(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_ep(Cube cube); +static uint64_t index_drud(Cube cube); + + +/* Steps *********************************************************************/ + +Step +eofb_HTM = { + .check = check_eofb_HTM, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_HTM +}; + +Step +eorl_HTM = { + .check = check_eofb_HTM, + .ready = check_nothing, + .pre_trans = ur, + .moveset = moveset_HTM +}; + +Step +eoud_HTM = { + .check = check_eofb_HTM, + .ready = check_nothing, + .pre_trans = bu, + .moveset = moveset_HTM +}; + +Step +coud_HTM = { + .check = check_coud_HTM, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_HTM +}; + +Step +corl_HTM = { + .check = check_coud_HTM, + .ready = check_nothing, + .pre_trans = rf, + .moveset = moveset_HTM +}; + +Step +cofb_HTM = { + .check = check_coud_HTM, + .ready = check_nothing, + .pre_trans = fd, + .moveset = moveset_HTM +}; + +Step +coud_URF = { + .check = check_coud_URF, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_URF +}; + +Step +corl_URF = { + .check = check_coud_URF, + .ready = check_nothing, + .pre_trans = rf, + .moveset = moveset_URF +}; + +Step +cofb_URF = { + .check = check_coud_URF, + .ready = check_nothing, + .pre_trans = fd, + .moveset = moveset_URF +}; + +Step +corners_HTM = { + .check = check_corners_HTM, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_HTM +}; + +Step +corners_URF = { + .check = check_corners_URF, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_URF +}; + +Step +edges_HTM = { + .check = check_edges_HTM, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_HTM +}; + +Step +drud_HTM = { + .check = check_drud_HTM, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_HTM +}; + +Step +optimal_HTM = { + .check = check_optimal_HTM, + .ready = check_nothing, + .pre_trans = uf, + .moveset = moveset_HTM +}; + + +/* Pruning tables ************************************************************/ + +PruneData +pd_eofb_HTM = { + .filename = "ptable_eofb_HTM", + .size = POW2TO11, + .index = index_eofb, + .moveset = moveset_HTM +}; + +PruneData +pd_coud_HTM = { + .filename = "ptable_coud_HTM", + .size = POW3TO7, + .index = index_coud, + .moveset = moveset_HTM +}; + +PruneData +pd_corners_HTM = { + .filename = "ptable_corners_HTM", + .size = POW3TO7 * FACTORIAL8, + .index = index_corners, + .moveset = moveset_HTM +}; + +PruneData +pd_ep_HTM = { + .filename = "ptable_ep_HTM", + .size = FACTORIAL12, + .index = index_ep, + .moveset = moveset_HTM +}; + +PruneData +pd_drud_HTM = { + .filename = "ptable_drud_HTM", + .size = POW2TO11 * POW3TO7 * BINOM12ON4, + .index = index_drud, + .moveset = moveset_HTM +}; + + +/* Check functions implementation ********************************************/ + +static int +check_nothing(Cube cube) +{ + /* At least we check that it is admissible */ + return is_admissible(cube); +} + +static int +check_eofb_HTM(Cube cube) +{ + if (!pd_eofb_HTM.generated) + generate_ptable(&pd_eofb_HTM); + + return PTABLEVAL(pd_eofb_HTM.ptable, cube.eofb); +} + +static int +check_coud_HTM(Cube cube) +{ + if (!pd_coud_HTM.generated) + generate_ptable(&pd_coud_HTM); + + return PTABLEVAL(pd_coud_HTM.ptable, cube.coud); +} + +static int +check_coud_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + int ud = check_coud_HTM(cube); + int rl = check_coud_HTM(apply_move(z, cube)); + int fb = check_coud_HTM(apply_move(x, cube)); + + return MIN(ud, MIN(rl, fb)); +} + +static int +check_corners_HTM(Cube cube) +{ + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + + return PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); +} + +static int +check_corners_URF(Cube cube) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) + ret = MIN(ret,check_corners_HTM(apply_alg(trans_alg(i),cube))); + + return ret; +} + +static int +check_edges_HTM(Cube cube) +{ + int ret = 0; + + if (!pd_ep_HTM.generated) + generate_ptable(&pd_ep_HTM); + + ret = MAX(ret, PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube))); + ret = MAX(ret, check_eofb_HTM(cube)); + ret = MAX(ret, check_eofb_HTM(apply_trans(ur, cube))); + ret = MAX(ret, check_eofb_HTM(apply_trans(fd, cube))); + + return ret; +} + +static int +check_drud_HTM(Cube cube) +{ + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + + return PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); +} + +static int +check_optimal_HTM(Cube cube) +{ + int dr1, dr2, dr3, drmax, cor; /*ep;*/ + + if (!pd_drud_HTM.generated) + generate_ptable(&pd_drud_HTM); + if (!pd_corners_HTM.generated) + generate_ptable(&pd_corners_HTM); + /* + *if (!pd_ep_HTM.generated) + * generate_ptable(&pd_ep_HTM); + */ + + dr1 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(cube)); + dr2 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(rf, cube))); + dr3 = PTABLEVAL(pd_drud_HTM.ptable, index_drud(apply_trans(fd, cube))); + + drmax = MAX(dr1, MAX(dr2, dr3)); + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + drmax++; + + cor = PTABLEVAL(pd_corners_HTM.ptable, index_corners(cube)); + /* ep = PTABLEVAL(pd_ep_HTM.ptable, index_ep(cube)); */ + + /*return MAX(drmax, MAX(ep, cor));*/ + if (drmax == 0 && cor == 0) + return is_solved(cube, false) ? 0 : 1; + return MAX(drmax, cor); +} + + +/* Index functions implementation ********************************************/ + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_ep(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eposs; + b = (cube.epose % FACTORIAL4) + epos_dependent_cube(cube)*FACTORIAL4; + c = cube.eposm % FACTORIAL4; + + b *= FACTORIAL4 * BINOM12ON4; + c *= FACTORIAL4 * BINOM12ON4 * FACTORIAL4 * BINOM8ON4; + + return a + b + c; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + diff --git a/old/backup-manysteps-pretrans/steps.h b/old/backup-manysteps-pretrans/steps.h @@ -0,0 +1,36 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "cube.h" + +/* Steps *********************************************************************/ + +extern Step eofb_HTM; +extern Step eorl_HTM; +extern Step eoud_HTM; +extern Step coud_HTM; +extern Step corl_HTM; +extern Step cofb_HTM; +extern Step coud_URF; +extern Step corl_URF; +extern Step cofb_URF; +extern Step corners_HTM; +extern Step corners_URF; +extern Step edges_HTM; +extern Step drud_HTM; +extern Step optimal_HTM; + +/* Pruning tables ************************************************************/ + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_ep_HTM; +extern PruneData pd_drud_HTM; + +/* Movesets ******************************************************************/ + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); + +#endif diff --git a/old/coord.c b/old/coord.c @@ -0,0 +1,1014 @@ +#include "coord.h" + +static Cube admissible_eos_from_eofbepos(Cube cube); +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_epud(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cp(uint64_t ind); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_cp_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_drud_eofb(uint64_t ind); +static Cube antindex_drudfin_noE_sym16(uint64_t ind); +static Cube antindex_htrfin(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); +static int epos_dependent_pos(int pos1, int pos2); +static void gensym(SymData *sd); +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_epud(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cp(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_cp_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_drud_eofb(Cube cube); +static uint64_t index_drudfin_noE_sym16(Cube cube); +static uint64_t index_htrfin(Cube cube); +static uint64_t index_khuge(Cube cube); +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_symdata(); +static bool read_symdata_file(SymData *sd); +static bool write_symdata_file(SymData *sd); + +/* All sorts of useful costants and tables **********************************/ + +/* TODO: Can I move inside functions that use them? + Maybe I need to pass them as argument to some + secondary function */ +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; + + +/* Symmetry data for some coordinates ****************************************/ + +static Trans +trans_group_trivial[1] = { uf }; + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_cp_16 = { + .filename = "sd_cp_16", + .coord = &coord_cp, + .sym_coord = &coord_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int n_all_symdata = 3; +static SymData * all_sd[3] = { + &sd_coud_16, + &sd_cp_16, + &sd_eofbepos_16, +}; + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .check = check_eofb, + .max = POW2TO11, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .check = check_eofbepos, + .max = POW2TO11 * BINOM12ON4, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .check = check_coud, + .max = POW3TO7, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .check = check_corners, + .max = POW3TO7 * FACTORIAL8, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_cp = { + .index = index_cp, + .cube = antindex_cp, + .check = check_cp, + .max = FACTORIAL8, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .check = check_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .check = check_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_htrfin = { + .index = index_htrfin, + .cube = antindex_htrfin, + .check = check_htrfin, + .max = 24 * 24 * 24 *24 * 24, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_drud_eofb = { + .index = index_drud_eofb, + .cube = antindex_drud_eofb, + .check = check_drud, + .max = POW3TO7 * BINOM12ON4, + .ntrans = 1, + .trans = trans_group_trivial, +}; + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .check = check_eofbepos, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .check = check_coud, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_cp_sym16 = { + .index = index_cp_sym16, + .cube = antindex_cp_sym16, + .check = check_cp, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .check = check_drud, + .max = POW3TO7 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drudfin_noE_sym16 = { + .index = index_drudfin_noE_sym16, + .cube = antindex_drudfin_noE_sym16, + .check = check_drudfin_noE, + .max = FACTORIAL8 * 2768, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .check = check_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +/* Functions *****************************************************************/ + +static Cube +admissible_eos_from_eofbepos(Cube cube) +{ + Edge e; + Cube ret; + CubeArray *arr = new_cubearray(cube, pf_all); + + memcpy(arr->eorl, arr->eofb, 12 * sizeof(int)); + memcpy(arr->eoud, arr->eofb, 12 * sizeof(int)); + + for (e = 0; e < 12; e++) { + if ((edge_slice(e) != 0 && edge_slice(arr->ep[e]) == 0) || + (edge_slice(e) == 0 && edge_slice(arr->ep[e]) != 0)) + arr->eorl[e] = 1 - arr->eorl[e]; + if ((edge_slice(e) != 2 && edge_slice(arr->ep[e]) == 2) || + (edge_slice(e) == 2 && edge_slice(arr->ep[e]) != 2)) + arr->eoud[e] = 1 - arr->eoud[e]; + } + + ret = arrays_to_cube(arr, pf_all); + free_cubearray(arr, pf_all); + + return ret; +} + + +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + static bool initialized = false; + static Cube admissible_ee_aux[POW2TO11*BINOM12ON4]; + static Cube c1; + static int k; + static uint64_t ui; + + if (!initialized) { + for (ui = 0; ui < POW2TO11*BINOM12ON4; ui++) { + k = (ui / POW2TO11) * 24; + c1 = admissible_ep((Cube){ .epose = k }, pf_e); + c1.eofb = ui % POW2TO11; + c1 = admissible_eos_from_eofbepos(c1); + admissible_ee_aux[ui] = c1; + } + + initialized = true; + } + + return admissible_ee_aux[ind]; +} + +static Cube +antindex_epud(uint64_t ind) +{ + static bool initialized = false; + static Cube epud_aux[FACTORIAL8]; + int a[12]; + uint64_t ui; + CubeArray arr; + + if (!initialized) { + a[FR] = FR; + a[FL] = FL; + a[BL] = BL; + a[BR] = BR; + for (ui = 0; ui < FACTORIAL8; ui++) { + index_to_perm(ui, 8, a); + arr.ep = a; + epud_aux[ui] = arrays_to_cube(&arr, pf_ep); + } + + initialized = true; + } + + return epud_aux[ind]; +} + +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +static Cube +antindex_cp(uint64_t ind) +{ + Cube c = {0}; + + c.cp = ind; + + return c; +} + +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = anti_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +/* TODO: admissible eos and cos */ +/* DONE: temporary fix, make it better */ +/* Or maybe it's ok like this? */ +static Cube +antindex_drud(uint64_t ind) +{ + uint64_t epos, eofb; + Cube c; + + eofb = ind % POW2TO11; + epos = ind / (POW2TO11 * POW3TO7); + c = antindex_eofbepos(eofb + POW2TO11 * epos); + + c.coud = (ind / POW2TO11) % POW3TO7; + c.corl = c.coud; + c.cofb = c.coud; + + return c; +} + +static Cube +antindex_drud_eofb(uint64_t ind) +{ + return antindex_drud(ind * POW2TO11); +} + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_cp_sym16(uint64_t ind) +{ + return sd_cp_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/POW3TO7]; + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_drudfin_noE_sym16(uint64_t ind) +{ + Cube c1, c2; + + c1 = antindex_epud(ind % FACTORIAL8); + c2 = sd_cp_16.rep[ind/FACTORIAL8]; + c1.cp = c2.cp; + + return c1; +} + +static Cube +antindex_htrfin(uint64_t ind) +{ + Cube ret = {0}; + uint64_t cp1, cp2; + + static bool initialized = false; + static int i, j, k, c[8], c1[4], c2[4], cp[24][24]; + static int c1solved[4] = {UFR, UBL, DFL, DBR}; + static int c2solved[4] = {UFL, UBR, DFR, DBL}; + + if (!initialized) { + for (i = 0; i < 24; i++) { + for (j = 0; j < 24; j++) { + index_to_perm(i, 4, c1); + index_to_perm(j, 4, c2); + for (k = 0; k < 8; k++) + if (k == UFR || k == UBL || + k == DFL || k == DBR) + c[k] = c1[c1solved[k/2]]; + else + c[k] = c2[c2solved[k/2]]; + + cp[i][j] = perm_to_index(c, 8); + } + } + + initialized = true; + } + + cp2 = ind % 24; + ind /= 24; + cp1 = ind % 24; + ret.cp = cp[cp1][cp2]; + + ind /= 24; + ret.eposm = ind % 24; + ind /= 24; + ret.eposs = ind % 24; + ret.epose = ind / 24; + + return ret; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = sd_eofbepos_16.rep[ind/(FACTORIAL4*POW3TO7)]; + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + +bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +bool +check_corners(Cube cube) +{ + return cube.cp == 0 && cube.coud == 0; +} + +bool +check_cp(Cube cube) +{ + return cube.cp == 0; +} + +bool +check_cornershtr(Cube cube) +{ + return cube.coud == 0 && cphtr(cube) == 0; /* TODO: use array cphtrcosets*/ +} + +bool +check_coud(Cube cube) +{ + return cube.coud == 0; +} + +bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +bool +check_htr(Cube cube) +{ + return check_cornershtr(cube) && + cube.eofb == 0 && cube.eorl == 0 && cube.eoud == 0; +} + +bool +check_htrfin(Cube cube) +{ + return cube.cp == 0 && + cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_drudfin_noE(Cube cube) +{ + return cube.eposs == 0 && cube.eposm == 0 && cube.cp == 0; +} + +bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +bool +check_eofbepos(Cube cube) +{ + return cube.eofb == 0 && cube.epose / 24 == 0; +} + +bool +check_epose(Cube cube) +{ + return cube.epose == 0; +} + +bool +check_ep(Cube cube) +{ + return cube.epose == 0 && cube.eposs == 0 && cube.eposm == 0; +} + +bool +check_khuge(Cube cube) +{ + return check_drud(cube) && cube.epose % 24 == 0; +} + +bool +check_nothing(Cube cube) +{ + return is_admissible(cube); /*TODO: maybe change?*/ +} + +static int +epos_dependent_pos(int poss, int pose) +{ + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + int ep[12] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int ep8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + epos_to_partial_ep(poss*FACTORIAL4, ep, eps_solved); + epos_to_partial_ep(pose*FACTORIAL4, ep, epe_solved); + + for (i = 0, j = 0; i < 12; i++) + if (edge_slice(ep[i]) != 0) + ep8[j++] = (edge_slice(ep[i]) == 1) ? 1 : 0; + + swap(&ep8[1], &ep8[4]); + swap(&ep8[3], &ep8[6]); + + return subset_to_index(ep8, 8, 4); +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_epud(Cube cube) +{ + uint64_t ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); + + return ret; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cp(Cube cube) +{ + return cube.cp; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + cphtr(cube); +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_drud_eofb(Cube cube) +{ + return index_drud(cube) / POW2TO11; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[index_coud(cube)]; +} + +static uint64_t +index_cp_sym16(Cube cube) +{ + return sd_cp_16.class[index_cp(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_drudfin_noE_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_cp_16.transtorep[index_cp(cube)]; + c = apply_trans(t, cube); + + return index_cp_sym16(c) * FACTORIAL8 + index_epud(c); +} + +static uint64_t +index_htrfin(Cube cube) +{ + uint64_t epe, eps, epm, cp, ep; + + static bool initialized = false; + static uint64_t cp1[FACTORIAL8], cp2[FACTORIAL8]; + static unsigned int i; + static int j, n1, n2, c[8], c1[4], c2[4]; + + if (!initialized) { + for (i = 0; i < FACTORIAL8; i++) { + index_to_perm(i, 8, c); + n1 = 0; + n2 = 0; + for (j = 0; j < 8; j++) + if (c[j] == UFR || c[j] == UBL || + c[j] == DFL || c[j] == DBR) + c1[n1++] = c[j] / 2; + else + c2[n2++] = c[j] / 2; + + cp1[i] = perm_to_index(c1, 4); + cp2[i] = perm_to_index(c2, 4); + } + + initialized = true; + } + + epe = cube.epose % 24; + eps = cube.eposs % 24; + epm = cube.eposm % 24; + + cp = cp1[cube.cp] * 24 + cp2[cube.cp]; + ep = (epe * 24 + eps) *24 + epm; + + return ep * 24 * 24 + cp; +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[index_eofbepos(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[index_eofbepos(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + +static bool +read_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +/* Init functions implementation *********************************************/ + +/* + * There is certainly a bette way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, k, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + Move moves[6] = {U2, D2, R2, L2, F2, B2}; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = 0; k < 6; k++) { + /*jj = cp_mtable[moves[k]][next[j]];*/ + /* TODO fix formatting */ + jj = apply_move(moves[k], (Cube){.cp=next[j]}).cp; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + /* TODO: use antindexer, it's nicer */ + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_symdata() +{ + int i; + + for (i = 0; i < n_all_symdata; i++) + gensym(all_sd[i]); +} + +/*TODO maybe move the next two */ +uint64_t +cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +Cube +anti_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +uint64_t +epos_dependent(Cube c) +{ + static int initialized = false; + static int aux[BINOM12ON4][BINOM12ON4]; + static uint64_t ui, uj; + + if (!initialized) { + for (ui = 0; ui < BINOM12ON4; ui++) + for (uj = 0; uj < BINOM12ON4; uj++) + aux[ui][uj] = epos_dependent_pos(ui, uj); + + initialized = true; + } + + return aux[c.eposs/FACTORIAL4][c.epose/FACTORIAL4]; +} + +void +init_coord() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_cphtr_cosets(); + init_symdata(); +} + diff --git a/old/coord.h b/old/coord.h @@ -0,0 +1,45 @@ +#ifndef COORD_H +#define COORD_H + +#include "trans.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_cp; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_drud; +extern Coordinate coord_coud_sym16; +extern Coordinate coord_drud_eofb; +extern Coordinate coord_cp_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_drudfin_noE_sym16; +extern Coordinate coord_htrfin; +extern Coordinate coord_khuge; + +uint64_t cphtr(Cube cube); /* TODO: rename (something with cosets) */ +Cube anti_cphtr(uint64_t ind); /*TODO also this */ +uint64_t epos_dependent(Cube cube); /* TODO: rename and turn into an indexer */ + +bool check_centers(Cube cube); +bool check_corners(Cube cube); +bool check_cp(Cube cube); +bool check_cornershtr(Cube cube); +bool check_coud(Cube cube); +bool check_drud(Cube cube); +bool check_htr(Cube cube); +bool check_htrfin(Cube cube); +bool check_drudfin_noE(Cube cube); +bool check_eofb(Cube cube); +bool check_eofbepos(Cube cube); +bool check_epose(Cube cube); +bool check_ep(Cube cube); +bool check_khuge(Cube cube); +bool check_nothing(Cube cube); + +void init_coord(); + +#endif + diff --git a/old/utils.c b/old/utils.c @@ -0,0 +1,294 @@ +#include "utils.h" + +/* Generic utility functions *************************************************/ + +void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + intarrcopy(aux, set, n); + free(aux); +} + +void +intarrcopy(int *src, int *dst, int n) +{ + int i; + for (i = 0; i < n; i++) + dst[i] = src[i]; +} + +bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +int +sum(int *a, int n) +{ + int i, ret = 0; + + for (i = 0; i < n; i++) + ret += a[i]; + + return ret; +} + +void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; +} + +/* Standard mathematical functions *******************************************/ + +int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +int +powint(int a, int b) +{ + if (b < 0) + return 0; /* Truncate */ + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +/* Conversions to and from int (base b digits, permutations...) **************/ + +int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +int +invert_digits(int a, int b, int n) +{ + int i, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + return digit_array_to_int(r, n, b); +} + +void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +int subset_to_index(int *a, int n, int k) { + int i, ret = 0; + + /* TODO: better checks */ + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} diff --git a/old/utils.h b/old/utils.h @@ -0,0 +1,59 @@ +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> +#include <stdlib.h> + +/* Constants and macros *****************************************************/ + +#define pow2to11 2048 +#define pow2to12 4096 +#define pow3to7 2187 +#define pow3to8 6561 +#define pow12to4 20736 +#define factorial4 24 +#define factorial6 720 +#define factorial8 40320 +#define factorial12 479001600 +#define binom12on4 495 +#define binom8on4 70 + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* Generic utility functions *************************************************/ + +void apply_permutation(int *perm, int *set, int n); +void intarrcopy(int *src, int *dst, int n); +bool is_perm(int *a, int n); +bool is_subset(int *a, int n, int k); +int sum(int *a, int n); +void sum_arrays_mod(int *src, int *dst, int n, int m); +void swap(int *a, int *b); + +/* Standard mathematical functions *******************************************/ + +int binomial(int n, int k); +int factorial(int n); +int perm_sign(int a[], int n); +int powint(int a, int b); + +/* Conversions to and from int (base b digits, permutations...) **************/ + +int digit_array_to_int(int *a, int n, int b); +void int_to_digit_array(int a, int b, int n, int *r); +void int_to_sum_zero_array(int x, int b, int n, int *a); +int invert_digits(int a, int b, int n); + +void index_to_perm(int p, int n, int *r); +int perm_to_index(int *a, int n); + +void index_to_subset(int s, int n, int k, int *r); +int subset_to_index(int *a, int n, int k); + +/* Am I not using these two? +void index_to_ordered_subset(int s, int n, int k, int *r); +int ordered_subset_to_index(int *a, int n, int k); +*/ + +#endif diff --git a/src/alg.c b/src/alg.c @@ -0,0 +1,364 @@ +#include "alg.h" + +/* Local functions ***********************************************************/ + +static void free_alglistnode(AlgListNode *aln); +static void realloc_alg(Alg *alg, int n); + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + +bool +moveset_eofb(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || b == R || b == L || + ((b == F || b == B) && m == b+1); +} + +bool +moveset_drud(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || + ((b == R || b == L || b == F || b == B) && m == b + 1); +} + +bool +moveset_htr(Move m) +{ + Move b = base_move(m); + + return moveset_HTM(m) && m == b + 1; +} + + +/* Functions *****************************************************************/ + +void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +void +compose_alg(Alg *alg1, Alg *alg2) +{ + int i; + + for (i = 0; i < alg2->len; i++) + append_move(alg1, alg2->move[i], alg2->inv[i]); +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +static void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Move +inverse_move(Move m) +{ + return m == NULLMOVE ? NULLMOVE : m + 2 - 2*((m-1) % 3); +} + +char * +move_string(Move m) +{ + static char move_string_aux[NMOVES][7] = { + [NULLMOVE] = "-", + [U] = "U", [U2] = "U2", [U3] = "U\'", + [D] = "D", [D2] = "D2", [D3] = "D\'", + [R] = "R", [R2] = "R2", [R3] = "R\'", + [L] = "L", [L2] = "L2", [L3] = "L\'", + [F] = "F", [F2] = "F2", [F3] = "F\'", + [B] = "B", [B2] = "B2", [B3] = "B\'", + [Uw] = "Uw", [Uw2] = "Uw2", [Uw3] = "Uw\'", + [Dw] = "Dw", [Dw2] = "Dw2", [Dw3] = "Dw\'", + [Rw] = "Rw", [Rw2] = "Rw2", [Rw3] = "Rw\'", + [Lw] = "Lw", [Lw2] = "Lw2", [Lw3] = "Lw\'", + [Fw] = "Fw", [Fw2] = "Fw2", [Fw3] = "Fw\'", + [Bw] = "Bw", [Bw2] = "Bw2", [Bw3] = "Bw\'", + [M] = "M", [M2] = "M2", [M3] = "M\'", + [E] = "E", [E2] = "E2", [E3] = "E\'", + [S] = "S", [S2] = "S2", [S3] = "S\'", + [x] = "x", [x2] = "x2", [x3] = "x\'", + [y] = "y", [y2] = "y2", [y3] = "y\'", + [z] = "z", [z2] = "z2", [z3] = "z\'", + }; + + return move_string_aux[m]; +} + +void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +void +moveset_to_list(Moveset ms, Move *r) +{ + int n = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) + if (ms(i)) + r[n++] = i; + + r[n] = NULLMOVE; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false, move_read; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + move_read = false; + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string(j)[0] || + (str[i] >= 'a' && str[i] <= 'z' && + str[i] == move_string(j)[0]-('A'-'a') && j<=B)) { + m = j; + if (str[i] >= 'a' && str[i] <= 'z' && j<=B) { + m += Uw - U; + } + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1] == '\'' || + str[i+1] == '3' || + str[i+1] == '`' ) { + m += 2; + i++; + } else if ((int)str[i+1] == -62 && + (int)str[i+2] == -76) { + /* Weird apostrophe */ + m += 2; + i += 2; + } else if ((int)str[i+1] == -30 && + (int)str[i+2] == -128 && + (int)str[i+3] == -103) { + /* MacOS apostrophe */ + m += 2; + i += 3; + } + append_move(alg, m, niss); + move_read = true; + break; + } + } + + if (!move_read) { + alg = new_alg(""); + return alg; + } + } + + return alg; +} + +AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string(alg->move[i])); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + diff --git a/src/alg.h b/src/alg.h @@ -0,0 +1,35 @@ +#ifndef ALG_H +#define ALG_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cubetypes.h" +#include "utils.h" + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); +bool moveset_eofb(Move m); +bool moveset_drud(Move m); +bool moveset_htr(Move m); + +void append_alg(AlgList *l, Alg *alg); +void append_move(Alg *alg, Move m, bool inverse); +void compose_alg(Alg *alg1, Alg *alg2); +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Move inverse_move(Move m); +char * move_string(Move m); +void movelist_to_position(Move *ml, int *pos); +void moveset_to_list(Moveset ms, Move *lst); +Alg * new_alg(char *str); +AlgList * new_alglist(); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); + +#endif + diff --git a/src/commands.c b/src/commands.c @@ -0,0 +1,346 @@ +#include "commands.h" + +/* Arg parsing functions *****************************************************/ + +CommandArgs * solve_parse_args(int c, char **v); +CommandArgs * help_parse_args(int c, char **v); +CommandArgs * print_parse_args(int c, char **v); +CommandArgs * parse_no_arg(int c, char **v); + +/* Exec functions ************************************************************/ + +static void solve_exec(CommandArgs *args); +static void steps_exec(CommandArgs *args); +static void commands_exec(CommandArgs *args); +static void print_exec(CommandArgs *args); +static void help_exec(CommandArgs *args); +static void quit_exec(CommandArgs *args); +static void version_exec(CommandArgs *args); + +/* Local functions ***********************************************************/ + +static bool read_step(CommandArgs *args, char *str); +static bool read_scramble(int c, char **v, CommandArgs *args); + +/* Commands ******************************************************************/ + +Command +solve_cmd = { + .name = "solve", + .usage = "solve STEP [OPTIONS] SCRAMBLE", + .description = "Solve a step", + .parse_args = solve_parse_args, + .exec = solve_exec +}; + +Command +steps_cmd = { + .name = "steps", + .usage = "steps", + .description = "List available steps", + .parse_args = parse_no_arg, + .exec = steps_exec +}; + +Command +commands_cmd = { + .name = "commands", + .usage = "commands", + .description = "List available commands", + .parse_args = parse_no_arg, + .exec = commands_exec +}; + +Command +print_cmd = { + .name = "print", + .usage = "print SCRAMBLE", + .description = "Print written description of the cube", + .parse_args = print_parse_args, + .exec = print_exec, +}; + +Command +help_cmd = { + .name = "help", + .usage = "help [COMMAND]", + .description = "Display nissy manual page or help on specific command", + .parse_args = help_parse_args, + .exec = help_exec, +}; + +Command +quit_cmd = { + .name = "quit", + .usage = "quit", + .description = "Quit nissy", + .parse_args = parse_no_arg, + .exec = quit_exec, +}; + +Command +version_cmd = { + .name = "version", + .usage = "version", + .description = "print nissy version", + .parse_args = parse_no_arg, + .exec = version_exec, +}; + +Command *commands[NCOMMANDS] = { + &commands_cmd, + &help_cmd, + &print_cmd, + &quit_cmd, + &solve_cmd, + &steps_cmd, + &version_cmd, +}; + +/* Arg parsing functions implementation **************************************/ + +CommandArgs * +solve_parse_args(int c, char **v) +{ + int i; + long val; + + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = false; + a->opts = malloc(sizeof(SolveOptions)); + a->step = steps[0]; + a->command = NULL; + a->scramble = NULL; + + a->opts->min_moves = 0; + a->opts->max_moves = 20; + a->opts->max_solutions = 1; + a->opts->optimal_only = false; + a->opts->can_niss = false; + a->opts->verbose = false; + a->opts->all = false; + a->opts->print_number = true; + + for (i = 0; i < c; i++) { + if (!strcmp(v[i], "-m")) { + val = strtol(v[++i], NULL, 10); + if (val < 0 || val > 100) { + fprintf(stderr, + "Invalid min number of moves.\n"); + return a; + } + a->opts->min_moves = val; + } else if (!strcmp(v[i], "-M")) { + val = strtol(v[++i], NULL, 10); + if (val < 0 || val > 100) { + fprintf(stderr, + "Invalid max number of moves.\n"); + return a; + } + a->opts->max_moves = val; + } else if (!strcmp(v[i], "-s")) { + val = strtol(v[++i], NULL, 10); + if (val < 1 || val > 1000000) { + fprintf(stderr, + "Invalid number of solutions.\n"); + return a; + } + a->opts->max_solutions = val; + } else if (!strcmp(v[i], "-o")) { + a->opts->optimal_only = true; + } else if (!strcmp(v[i], "-n")) { + a->opts->can_niss = true; + } else if (!strcmp(v[i], "-v")) { + a->opts->verbose = true; + } else if (!strcmp(v[i], "-a")) { + a->opts->all = true; + } else if (!strcmp(v[i], "-p")) { + a->opts->print_number = false; + } else if (!read_step(a, v[i])) { + break; + } + } + + a->success = read_scramble(c-i, &v[i], a); + return a; +} + +CommandArgs * +help_parse_args(int c, char **v) +{ + int i; + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->scramble = NULL; + a->opts = NULL; + a->step = NULL; + a->command = NULL; + + if (c == 1) { + for (i = 0; i < NCOMMANDS; i++) + if (commands[i] != NULL && + !strcmp(v[0], commands[i]->name)) + a->command = commands[i]; + if (a->command == NULL) + fprintf(stderr, "%s: command not found\n", v[0]); + } + + a->success = c == 0 || (c == 1 && a->command != NULL); + return a; +} + +CommandArgs * +parse_no_arg(int c, char **v) +{ + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = true; + + return a; +} + +CommandArgs * +print_parse_args(int c, char **v) +{ + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = read_scramble(c, v, a); + return a; +} + +/* Exec functions implementation *********************************************/ + +static void +solve_exec(CommandArgs *args) +{ + Cube c; + AlgList *sols; + + init_symcoord(); + + c = apply_alg(args->scramble, (Cube){0}); + sols = solve(c, args->step, args->opts); + + print_alglist(sols, args->opts->print_number); + free_alglist(sols); +} + +static void +steps_exec(CommandArgs *args) +{ + int i; + + for (i = 0; i < NSTEPS && steps[i] != NULL; i++) + printf("%-15s %s\n", steps[i]->shortname, steps[i]->name); +} + +static void +commands_exec(CommandArgs *args) +{ + int i; + + for (i = 0; i < NCOMMANDS && commands[i] != NULL; i++) + printf("%s\n", commands[i]->usage); + +} + +static void +print_exec(CommandArgs *args) +{ + init_moves(); + print_cube(apply_alg(args->scramble, (Cube){0})); +} + +static void +help_exec(CommandArgs *args) +{ + /* TODO: print full nissy manpage */ + if (args->command == NULL) { + printf("Type help COMMAND for information on a "); + printf("specific command.\n"); + printf("A more complete manual page is work in progress.\n"); + } else { + printf("Command %s: %s\nusage: %s\n", args->command->name, + args->command->description, args->command->usage); + } +} + +static void +quit_exec(CommandArgs *args) +{ + exit(0); +} + +static void +version_exec(CommandArgs *args) +{ + printf(VERSION"\n"); +} + +/* Local functions implementation ********************************************/ + +static bool +read_step(CommandArgs *args, char *str) +{ + int i; + + for (i = 0; i < NSTEPS; i++) { + if (steps[i] != NULL && !strcmp(steps[i]->shortname, str)) { + args->step = steps[i]; + return true; + } + } + + return false; +} + +static bool +read_scramble(int c, char **v, CommandArgs *args) +{ + int i, k, n; + unsigned int j; + char *algstr; + + if (new_alg(v[0])->len == 0) { + fprintf(stderr, "%s: moves or option unrecognized\n", v[0]); + return false; + } + + n = 0; + for(i = 0; i < c; i++) + n += strlen(v[i]); + + algstr = malloc((n + 1) * sizeof(char)); + k = 0; + for (i = 0; i < c; i++) + for (j = 0; j < strlen(v[i]); j++) + algstr[k++] = v[i][j]; + algstr[k] = 0; + + args->scramble = new_alg(algstr); + free(algstr); + + if (args->scramble->len == 0) + fprintf(stderr, "Error reading scramble\n"); + + return args->scramble->len > 0; +} + +/* Public functions implementation *******************************************/ + +void +free_args(CommandArgs *args) +{ + if (args == NULL) + return; + + if (args->scramble != NULL) + free_alg(args->scramble); + if (args->opts != NULL) + free(args->opts); + + /* step and command must not be freed, they are static! */ + + free(args); +} diff --git a/src/commands.h b/src/commands.h @@ -0,0 +1,13 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +#include "solve.h" +#include "steps.h" + +#define NCOMMANDS 10 + +void free_args(CommandArgs *args); + +extern Command * commands[NCOMMANDS]; + +#endif diff --git a/src/coord.c b/src/coord.c @@ -0,0 +1,522 @@ +#include "coord.h" + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_epud(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cp(uint64_t ind); +static Cube antindex_cphtr(uint64_t); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_cornershtrfin(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_drud_eofb(uint64_t ind); +static Cube antindex_htr_drud(uint64_t ind); +static Cube antindex_htrfin(uint64_t ind); + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_epud(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cp(Cube cube); +static uint64_t index_cphtr(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_cornershtrfin(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_drud_eofb(Cube cube); +static uint64_t index_htr_drud(Cube cube); +static uint64_t index_htrfin(Cube cube); + +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_cornershtrfin(); + + +/* All sorts of useful costants and tables **********************************/ + +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static int cornershtrfin_ind[FACTORIAL8]; +static int cornershtrfin_ant[24*24/6]; + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .max = POW2TO11, + .ntrans = 1, +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .max = POW2TO11 * BINOM12ON4, + .ntrans = 1, +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .max = POW3TO7, + .ntrans = 1, +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .max = POW3TO7 * FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_cp = { + .index = index_cp, + .cube = antindex_cp, + .max = FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_cphtr = { + .index = index_cphtr, + .cube = antindex_cphtr, + .max = BINOM8ON4 * 6, + .ntrans = 1, +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6, + .ntrans = 1, +}; + +Coordinate +coord_cornershtrfin = { + .index = index_cornershtrfin, + .cube = antindex_cornershtrfin, + .max = 24*24/6, + .ntrans = 1, +}; + +Coordinate +coord_epud = { + .index = index_epud, + .cube = antindex_epud, + .max = FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4, + .ntrans = 1, +}; + +Coordinate +coord_htr_drud = { + .index = index_htr_drud, + .cube = antindex_htr_drud, + .max = BINOM8ON4 * 6 * BINOM8ON4, + .ntrans = 1, +}; + +Coordinate +coord_htrfin = { + .index = index_htrfin, + .cube = antindex_htrfin, + .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */ + .ntrans = 1, +}; + +Coordinate +coord_drud_eofb = { + .index = index_drud_eofb, + .cube = antindex_drud_eofb, + .max = POW3TO7 * BINOM12ON4, + .ntrans = 1, +}; + +/* Functions *****************************************************************/ + +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + Cube ret = {0}; + + ret.eofb = ind % POW2TO11; + ret.epose = (ind / POW2TO11) * 24; + + return ret; +} + +static Cube +antindex_epud(uint64_t ind) +{ + static bool initialized = false; + static Cube epud_aux[FACTORIAL8]; + int a[12]; + uint64_t ui; + CubeArray arr; + + if (!initialized) { + a[FR] = FR; + a[FL] = FL; + a[BL] = BL; + a[BR] = BR; + for (ui = 0; ui < FACTORIAL8; ui++) { + index_to_perm(ui, 8, a); + arr.ep = a; + epud_aux[ui] = arrays_to_cube(&arr, pf_ep); + } + + initialized = true; + } + + return epud_aux[ind]; +} + +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +static Cube +antindex_cp(uint64_t ind) +{ + Cube c = {0}; + + c.cp = ind; + + return c; +} + +static Cube +antindex_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = antindex_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +static Cube +antindex_cornershtrfin(uint64_t ind) +{ + return (Cube){ .cp = cornershtrfin_ant[ind] }; +} + +static Cube +antindex_drud(uint64_t ind) +{ + uint64_t epos, eofb; + Cube c; + + eofb = ind % POW2TO11; + epos = ind / (POW2TO11 * POW3TO7); + c = antindex_eofbepos(eofb + POW2TO11 * epos); + + c.coud = (ind / POW2TO11) % POW3TO7; + + return c; +} + +static Cube +antindex_drud_eofb(uint64_t ind) +{ + return antindex_drud(ind * POW2TO11); +} + +static Cube +antindex_htr_drud(uint64_t ind) +{ + Cube ret; + + ret = antindex_cphtr(ind / BINOM8ON4); + ret.eposs = (ind % BINOM8ON4) * FACTORIAL4; + + return ret; +} + +static Cube +antindex_htrfin(uint64_t ind) +{ + Cube ret; + + ret = antindex_cornershtrfin(ind/(24*24*24)); + + ret.eposm = ind % 24; + ind /= 24; + ret.eposs = ind % 24; + ind /= 24; + ret.epose = ind % 24; + + return ret; +} + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_epud(Cube cube) +{ + uint64_t ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); + + return ret; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cp(Cube cube) +{ + return cube.cp; +} + +static uint64_t +index_cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube); +} + +static uint64_t +index_cornershtrfin(Cube cube) +{ + return cornershtrfin_ind[cube.cp]; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_drud_eofb(Cube cube) +{ + return index_drud(cube) / POW2TO11; +} + +static uint64_t +index_htr_drud(Cube cube) +{ + return index_cphtr(cube) * BINOM8ON4 + + (cube.eposs / FACTORIAL4) % BINOM8ON4; +} + +static uint64_t +index_htrfin(Cube cube) +{ + uint64_t epe, eps, epm, cp, ep; + + epe = cube.epose % 24; + eps = cube.eposs % 24; + epm = cube.eposm % 24; + ep = (epe * 24 + eps) *24 + epm; + cp = index_cornershtrfin(cube); + + return cp * 24 * 24 * 24 + ep; +} + +/* Init functions implementation *********************************************/ + +/* + * There is certainly a better way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + + Move k; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = U2; k < B3; k++) { + if (!moveset_htr(k)) + continue; + jj = apply_move(k, (Cube){ .cp = next[j] }).cp; + + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_cornershtrfin() +{ + unsigned int i, j; + int n, c; + Move m; + + for (i = 0; i < FACTORIAL8; i++) + cornershtrfin_ind[i] = -1; + cornershtrfin_ind[0] = 0; + + /* 10-pass, I think 5 is enough, but just in case */ + n = 1; + for (i = 0; i < 10; i++) { + for (j = 0; j < FACTORIAL8; j++) { + if (cornershtrfin_ind[j] == -1) + continue; + for (m = U; m < NMOVES; m++) { + if (moveset_htr(m)) { + c = apply_move(m, (Cube){.cp = j}).cp; + if (cornershtrfin_ind[c] == -1) { + cornershtrfin_ind[c] = n; + cornershtrfin_ant[n] = c; + n++; + } + } + } + } + } +} + +void +init_coord() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_trans(); + + init_cphtr_cosets(); + init_cornershtrfin(); +} + diff --git a/src/coord.h b/src/coord.h @@ -0,0 +1,23 @@ +#ifndef COORD_H +#define COORD_H + +#include "trans.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_cp; +extern Coordinate coord_cphtr; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_cornershtrfin; +extern Coordinate coord_epud; +extern Coordinate coord_drud; +extern Coordinate coord_drud_eofb; +extern Coordinate coord_htr_drud; +extern Coordinate coord_htrfin; + +void init_coord(); + +#endif + diff --git a/src/coordinates.c b/src/coordinates.c @@ -1,286 +0,0 @@ -#include <stdio.h> - -#include "utils.h" -#include "coordinates.h" - -/* Names of pieces and moves. */ -char edge_string_list[12][5] = { - "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" -}; - -char corner_string_list[8][5] = { - "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR" -}; - -char move_string_list[19][5] = { - "-", - "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", - "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'" -}; - -int inverse_move[19] = { - -1, U3, U2, U, D3, D2, D, R3, R2, R, L3, L2, L, F3, F2, F, B3, B2, B -}; - -/* Convert piece representation from integer to array. - * Come convertions are not "perfect": for example, and epud type of piece - * is represented by a permutation index in 8! elements, but it as an array - * it is converted to the first 8 elements of a 12 elements ep array (with - * meaningless values for the other 4 elements). */ - -void ep_int_to_array(int ep, int a[12]) { - index_to_perm(ep, 12, a); -} - -void epud_int_to_array(int epud, int a[12]) { - index_to_perm(epud, 8, a); /* Last 4 elements are left untouched. */ -} - -void epfb_int_to_array(int epfb, int a[12]) { - int edges[] = {UF, UB, DF, DB, FR, FL, BL, BR}; - int b[8]; - index_to_perm(epfb, 8, b); - for (int i = 0; i < 8; i++) - a[edges[i]] = edges[b[i]]; -} - -void eprl_int_to_array(int eprl, int a[12]) { - int edges[] = {UL, UR, DL, DR, FR, FL, BL, BR}; - int b[8]; - index_to_perm(eprl, 8, b); - for (int i = 0; i < 8; i++) - a[edges[i]] = edges[b[i]]; -} - -void epose_int_to_array(int epos, int a[12]) { - int edges[] = {FR, FL, BL, BR}; - index_to_subset(epos, 12, 4, a); - for (int i = 0, j = 0; i < 12; i++) - a[i] = (a[i] == 1) ? edges[j++] : -1; -} - -void eposs_int_to_array(int epos, int a[12]) { - int edges[] = {UL, UR, DL, DR}; - index_to_subset(epos, 12, 4, a); - for (int i = 0, j = 0; i < 12; i++) - a[i] = (a[i] == 1) ? edges[j++] : -1; - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); -} - -void eposm_int_to_array(int epos, int a[12]) { - int edges[] = {UF, UB, DF, DB}; - index_to_subset(epos, 12, 4, a); - for (int i = 0, j = 0; i < 12; i++) - a[i] = (a[i] == 1) ? edges[j++] : -1; - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); -} - -void epe_int_to_array(int epe, int a[12]) { - index_to_perm(epe, 4, a+8); - for (int i = 0; i < 4; i++) - a[i+8] += 8; -} - -void eps_int_to_array(int eps, int a[12]) { - int edges[] = {UL, UR, DL, DR}; - int b[4]; - index_to_perm(eps, 4, b); - for (int i = 0; i < 4; i++) - a[edges[i]] = edges[b[i]]; -} - -void epm_int_to_array(int epm, int a[12]) { - int edges[] = {UF, UB, DF, DB}; - int b[4]; - index_to_perm(epm, 4, b); - for (int i = 0; i < 4; i++) - a[edges[i]] = edges[b[i]]; -} - -void emslices_int_to_array(int emslices, int a[12]) { - int b[] = {0,0,0,0,0,0,0,0}; - int eslice[] = {FR, FL, BL, BR}; - int mslice[] = {UF, UB, DF, DB}; - - index_to_subset(emslices % binom12on4, 12, 4, a); - index_to_subset(emslices / binom12on4, 8, 4, b); - - if (emslices % binom12on4 == 0) { - swap(&b[UF], &b[DL]); - swap(&b[UB], &b[DR]); - /*for (int i = 0; i < 4; i++) - swap(&b[mslice[i]], &b[i+4]);*/ - } - - for (int i = 0, j = 0; j < 8; i++, j++) { - while (a[i]) - i++; - a[i] = b[j] ? 2 : -1; - } - for (int i = 0, j1 = 0, j2 = 0; i < 12; i++) { - if (a[i] == 1) - a[i] = eslice[j1++]; - if (a[i] == 2) - a[i] = mslice[j2++]; - } -} - -void cp_int_to_array(int cp, int a[8]) { - index_to_perm(cp, 8, a); -} - -void eo_11bits_to_array(int eo, int a[12]) { - int_to_sum_zero_array(eo, 2, 12, a); -} - -void co_7trits_to_array(int co, int a[8]) { - int_to_sum_zero_array(co, 3, 8, a); -} - - - - - -int ep_array_to_int(int ep[12]) { - return perm_to_index(ep, 12); -} - -int epud_array_to_int(int ep[12]) { - return perm_to_index(ep, 8); /* Last 4 elements are ignored */ -} - -int epfb_array_to_int(int ep[12]) { - int index[] = {0, -1, 1, -1, 2, -1, 3, -1, 4, 5, 6, 7}; - int b[8]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 8); -} - -int eprl_array_to_int(int ep[12]) { - int index[] = {-1, 0, -1, 1, -1, 2, -1, 3, 4, 5, 6, 7}; - int b[8]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 8); -} - -int epose_array_to_int(int ep[12]) { - int a[12]; - for (int i = 0; i < 12; i++) - a[i] = (ep[i] >= FR); - return subset_to_index(a, 12, 4); -} - -int eposs_array_to_int(int ep[12]) { - int a[12]; - int edges[] = {UL, UR, DL, DR}; - for (int i = 0; i < 12; i++) - a[i] = (ep[i] == UL || ep[i] == UR || ep[i] == DL || ep[i] == DR); - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); - return subset_to_index(a, 12, 4); -} - -int eposm_array_to_int(int ep[12]) { - int a[12]; - int edges[] = {UF, UB, DF, DB}; - for (int i = 0; i < 12; i++) - a[i] = (ep[i] == UF || ep[i] == UB || ep[i] == DF || ep[i] == DB); - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); - return subset_to_index(a, 12, 4); -} - -int epe_array_to_int(int ep[12]) { - int b[4]; - for (int i = 0; i < 4; i++) - b[i] = ep[i+8] - 8; - return perm_to_index(b, 4); -} - -int eps_array_to_int(int ep[12]) { - int index[] = {-1, 0, -1, 1, -1, 2, -1, 3, -1, -1, -1, -1}; - int b[4]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 4); -} - -int epm_array_to_int(int ep[12]) { - int index[] = {0, -1, 1, -1, 2, -1, 3, -1, -1, -1, -1, -1}; - int b[4]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 4); -} - -int emslices_array_to_int(int ep[12]) { - int a[12], b[12], c[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int edges[] = {UF, UB, DF, DB};*/ - for (int i = 0; i < 12; i++) { - a[i] = (ep[i] >= FR) ? 1 : 0; - b[i] = (ep[i] == UF || ep[i] == UB || ep[i] == DF || ep[i] == DB) ? 1 : 0; - } - - /*for ( int i = 0; i < 12; i++) - printf("%d ", ep[i]); - printf("\n");*/ - - for (int i = 0, j = 0; i < 12; i++, j++) { - if (a[i]) - j--; - if (b[i]) - c[j] = 1; - } - - int epose = subset_to_index(a, 12, 4); - - /*if (epose == 0) { - printf("Before: "); - for (int i = 0; i < 8; i++) - printf("%d ", c[i]); - printf("\n"); - for (int i = 0; i < 4; i++) - swap(&c[edges[i]], &c[i+4]); - printf("After: "); - for (int i = 0; i < 8; i++) - printf("%d ", c[i]); - printf("\n"); - }*/ - if (epose == 0) { - swap(&c[UF], &c[DL]); - swap(&c[UB], &c[DR]); - } - - /*for ( int i = 0; i < 8; i++) - printf("%d ", c[i]); - printf("\n");*/ - int eposm = subset_to_index(c, 8, 4); - - return epose + 495*eposm; -} - - -int cp_array_to_int(int cp[8]) { - return perm_to_index(cp, 8); -} - -int eo_array_to_11bits(int a[12]) { - return digit_array_to_int(a, 11, 2); -} - -int co_array_to_7trits(int a[8]) { - return digit_array_to_int(a, 7, 3); -} - diff --git a/src/coordinates.h b/src/coordinates.h @@ -1,92 +0,0 @@ -/* General rule for piece numbering (visually nicer): - * - * 0 1 2 3 4 5 6 7 8 9 10 11 - * UF UL UB UR DF DL DB DR FR FL BL BR - * UFR UFL UBL UBR DFR DFL DBL DBR - * - * The order of moves is - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - * - U U2 U' D D2 D' R R2 R' L L2 L' F F2 F' B B2 B' - * (0 is reserved for no move) */ - -#define UF 0 -#define UL 1 -#define UB 2 -#define UR 3 -#define DF 4 -#define DL 5 -#define DB 6 -#define DR 7 -#define FR 8 -#define FL 9 -#define BL 10 -#define BR 11 - -#define UFR 0 -#define UFL 1 -#define UBL 2 -#define UBR 3 -#define DFR 4 -#define DFL 5 -#define DBL 6 -#define DBR 7 - -#define U 1 -#define U2 2 -#define U3 3 -#define D 4 -#define D2 5 -#define D3 6 -#define R 7 -#define R2 8 -#define R3 9 -#define L 10 -#define L2 11 -#define L3 12 -#define F 13 -#define F2 14 -#define F3 15 -#define B 16 -#define B2 17 -#define B3 18 - -extern char edge_string_list[12][5]; -extern char corner_string_list[8][5]; -extern char move_string_list[19][5]; -extern int inverse_move[19]; - -/* Convert piece representation from integer to array. - * Come convertions are not "perfect": for example, and epud type of piece - * is represented by a permutation index in 8! elements, but it as an array - * it is converted to the first 8 elements of a 12 elements ep array (with - * meaningless values for the other 4 elements). */ - -void ep_int_to_array(int ep, int a[12]); -void epud_int_to_array(int epud, int a[12]); -void epfb_int_to_array(int epfb, int a[12]); -void eprl_int_to_array(int eprl, int a[12]); -void epose_int_to_array(int epos, int a[12]); -void eposs_int_to_array(int epos, int a[12]); -void eposm_int_to_array(int epos, int a[12]); -void epe_int_to_array(int epe, int a[12]); -void epm_int_to_array(int epe, int a[12]); -void eps_int_to_array(int epe, int a[12]); -void emslices_int_to_array(int emslices, int a[12]); -void cp_int_to_array(int cp, int a[8]); -void eo_11bits_to_array(int eo, int a[12]); -void co_7trits_to_array(int co, int a[8]); - -int ep_array_to_int(int ep[12]); -int epud_array_to_int(int ep[12]); -int epfb_array_to_int(int ep[12]); -int eprl_array_to_int(int ep[12]); -int epose_array_to_int(int ep[12]); -int eposs_array_to_int(int ep[12]); -int eposm_array_to_int(int ep[12]); -int epe_array_to_int(int epe[12]); -int epm_array_to_int(int epe[12]); -int eps_array_to_int(int epe[12]); -int emslices_array_to_int(int ep[12]); -int cp_array_to_int(int cp[8]); -int eo_array_to_11bits(int a[12]); -int co_array_to_7trits(int a[8]); diff --git a/src/cube.c b/src/cube.c @@ -0,0 +1,701 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static int array_ep_to_epos(int *ep, int *eps_solved); +static int epos_from_arrays(int *epos, int *ep); + +/* Local functions implementation ********************************************/ + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +/* Public functions implementation *******************************************/ + +Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* check also that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube) +{ + return equal(cube, (Cube){0}); +} + +bool +is_block_solved(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + static char edge_string[12][7] = { + [UF] = "UF", [UL] = "UL", [UB] = "UB", [UR] = "UR", + [DF] = "DF", [DL] = "DL", [DB] = "DB", [DR] = "DR", + [FR] = "FR", [FL] = "FL", [BL] = "BL", [BR] = "BR" + }; + + static char corner_string[8][7] = { + [UFR] = "UFR", [UFL] = "UFL", [UBL] = "UBL", [UBR] = "UBR", + [DFR] = "DFR", [DFL] = "DFL", [DBL] = "DBL", [DBR] = "DBR" + }; + + static char center_string[6][7] = { + [U_center] = "U", [D_center] = "D", + [R_center] = "R", [L_center] = "L", + [F_center] = "F", [B_center] = "B" + }; + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +Center +what_center_at(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][i] = arr->cpos[i]; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][i] = arr->cp[i]; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + + return aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + static bool initialized = false; + static int auxlast[POW3TO7]; + static int auxarr[8]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + auxlast[ui] = auxarr[7]; + } + + initialized = true; + } + + if (c < 7) + return (co / powint(3, c)) % 3; + else + return auxlast[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + static bool initialized = false; + static int auxlast[POW2TO11]; + static int auxarr[12]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + auxlast[ui] = auxarr[11]; + } + + initialized = true; + } + + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return auxlast[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][arr->cpos[i]] = i; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][arr->cp[i]] = i; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + return aux[cube.cp][c]; +} + +Edge +where_is_edge(Cube cube, Edge e) +{ + /* TODO: when I wrote this code I forgot to add the final + part, and now I can't remember how it was supposed to + work (i.e. how to recover the location of the edge + from these tables. I think it is either very easy or + wrong, in any case it is not a priority now. + Future Seba can deal with it. + + static bool initialized = false; + static Edge aux[3][FACTORIAL12/FACTORIAL8][12]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + initialized = true; + } + */ + + int i; + CubeArray *arr = new_cubearray(cube, pf_ep); + + for (i = 0; i < 12; i++) + if ((Edge)arr->ep[i] == e) + return i; + + return -1; +} diff --git a/src/cube.h b/src/cube.h @@ -0,0 +1,40 @@ +#ifndef CUBE_H +#define CUBE_H + +#include <stdio.h> +#include <time.h> + +#include "pf.h" +#include "utils.h" + +Cube admissible_ep(Cube cube, PieceFilter f); +Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +int edge_slice(Edge e); /* E=0, S=1, M=2 */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +bool is_admissible(Cube cube); +bool is_solved(Cube cube); +bool is_block_solved(Cube cube, Block); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void epos_to_partial_ep(int epos, int *ep, int *ss); +void free_cubearray(CubeArray *arr, PieceFilter f); +Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +CubeArray * new_cubearray(Cube cube, PieceFilter f); +void print_cube(Cube cube); +Cube random_cube(); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +#endif + diff --git a/src/cubetypes.h b/src/cubetypes.h @@ -0,0 +1,286 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +#include <stdbool.h> +#include <stdint.h> + +#define NMOVES 55 /* Actually 55, but one is NULLMOVE */ +#define NTRANS 48 +#define NROTATIONS 24 + +/* Enums *********************************************************************/ + +typedef enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +} Center; + +typedef enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +} Corner; + +typedef enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +} Edge; + +typedef enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +} Move; + +typedef enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +} Trans; + + +/* Typedefs ******************************************************************/ + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct command Command; +typedef struct commandargs CommandArgs; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef bool (*Validator) (Alg *); +typedef void (*Exec) (CommandArgs *); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); +typedef CommandArgs * (*ArgParser) (int, char **); +typedef Trans (*TransDetector) (Cube); + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +command +{ + char * name; + char * usage; + char * description; + ArgParser parse_args; + Exec exec; +}; + +struct +commandargs +{ + bool success; + Alg * scramble; + SolveOptions * opts; + Step * step; + Command * command; /* For help */ +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + uint64_t max; + int ntrans; + Trans * trans; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool verbose; + bool all; + bool print_number; +}; + +struct +step +{ + char * shortname; + char * name; + Estimator estimate; + Checker ready; + char * ready_msg; + Validator is_valid; + Moveset moveset; + Trans pre_trans; + TransDetector detect; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/src/env.c b/src/env.c @@ -0,0 +1,45 @@ +#include "env.h" + +bool initialized_env = false; +char *tabledir; + +void +init_env() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (initialized_env) + return; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } + + initialized_env = true; +} diff --git a/src/env.h b/src/env.h @@ -0,0 +1,15 @@ +#ifndef ENV_H +#define ENV_H + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> + +extern char *tabledir; + +void init_env(); + +#endif diff --git a/src/helppages.h b/src/helppages.h @@ -1,689 +0,0 @@ -/* To generate this help page, use the script makedoc.sh */ - -int Npages = 22; - -char *helppages[][10] = { - -{ "add", -"\ -\n\ -HELP PAGE FOR COMMAND add\n\ -\n\ -SYNTAX\n\ -add [MOVES|$ID1|@ID1] $ID2\n\ -\n\ -DESCRIPTION\n\ -Appends either MOVES, the scramble memorized under $ID1 or the output sequence\n\ -memorized under @ID1 at the end of the scramble memorized under $ID2. If none\n\ -of MOVES, $ID1 or @ID1 is specified, the user will be asked to type the moves.\n\ -Menmonic: \"add x to y\" or just \"add to y\".\n\ -\n\ -EXAMPLES\n\ -add $1\n\ - The user is required to type the moves that will be appended to $1.\n\ -add F R B $1\n\ - Appends the moves F R B to scramble $1. Now scramble $1 ends with F R B.\n\ -\n\ -" -}, - -{ "change", -"\ -\n\ -HELP PAGE FOR COMMAND change\n\ -\n\ -SYNTAX\n\ -change $ID1 [MOVES|$ID2|@ID2]\n\ -\n\ -DESCRIPTION\n\ -Changes the scramble $ID1 to either MOVES, the scramble $ID2, the output @ID2\n\ -or, if none is specified, the moves entered by the user. The scramble that was\n\ -memorized under $ID1 is then lost.\n\ -Mnemonic: \"change x to y\", or just \"change x\".\n\ -\n\ -EXAMPLES\n\ -change $1\n\ - The user is required to type the moves that will replace $1.\n\ -change $2 $3\n\ - Saves the scramble that was saved under $3 in $2. Now $2 and $3 are the same\n\ - scrambles, and the old $2 is lost.\n\ -change $1 U R\n\ - Saves U R as scramble $1.\n\ -\n\ -" -}, - -{ "clear", -"\ -\n\ -HELP PAGE FOR COMMAND clear\n\ -\n\ -SYNTAX\n\ -clear\n\ -\n\ -DESCRIPTION\n\ -Resets all saved scrambles and output sequences.\n\ -\n\ -" -}, - -{ "co", -"\ -\n\ -HELP PAGE FOR COMMAND co\n\ -\n\ -SYNTAX\n\ -co [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves CO for a given scramble. A scramble can be given as last argument of the\n\ -command, or an ID of a saved scramble can be provided. If none of the two is\n\ -given, a prompt will ask the user to input a new scramble.\n\ -\n\ -OPTIONS\n\ -axis={fb,rl,ud} Specify the axis for the CO. One to three axes can be given,\n\ - comma separated, no spaces.\n\ - Default: CO on any of the three axis (omitting the option is\n\ - the same as specifying axis=fb,rl,ud).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden COs.\n\ - Default, if an CO ending in e.g. F is shown, the equivalent\n\ - one ending in F' is hidden.\n\ -i Ignore centers. By default the CO is aligned with centers.\n\ -niss Use NISS.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of COs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -co axis=fb $1\n\ - Finds one optimal CO on fb for the first saved scramble.\n\ -\n\ -co n=5 b=4 U R F \n\ - Finds up to 5 COs of length at most 4 for scramble U R F.\n\ -\n\ -co n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F\n\ - Finds up to 100 COs of lenth at most 4, possibly using NISS, including\n\ - \"hidden\" COs, excluding the rl axis.\n\ -\n\ -" -}, - -{ "dr", -"\ -\n\ -HELP PAGE FOR COMMAND dr\n\ -\n\ -SYNTAX\n\ -dr [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves DR for a given scramble. A scramble can be given as last argument of the\n\ -command, or an ID of a saved scramble can be provided. If none of the two is\n\ -given, a prompt will ask the user to input a new scramble.\n\ -If the option \"from\" is given (see below), it solves DR from an EO (if edges\n\ -are oriented) without breaking that EO.\n\ -The first time this command is called without the option from (and, to some\n\ -extent, also the first time it is called with the option from), nissy loads\n\ -some pruning tables that were not loaded on startup, causing a small but\n\ -noticeable delay.\n\ -\n\ -OPTIONS\n\ -axis={fb,rl,ud} Specify the axis for the DR. One to three axes can be given,\n\ - comma separated, no spaces.\n\ - Default: DR on any of the three axis (omitting the option is\n\ - the same as specifying axis=fb,rl,ud).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden DRs.\n\ - Default, if an DR ending in e.g. R is shown, the equivalent\n\ - one ending in R' is hidden.\n\ -from {fb|rl|ud} Solve DR from the specified EO, which must be solved,\n\ - without breaking the EO.\n\ -niss Use NISS. It works only if solving DR from EO.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of EOs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -dr from rl axis=ud $1\n\ - Finds optimal DR on ud, starting from EO on rl, for the first saved scramble.\n\ -\n\ -dr niss from fb n=10 m=6 F2 R L B' F D U' R2 L' F D B\n\ - Finds up to 10 DRs of length at most 6 from EO on fb, possibly using NISS.\n\ -\n\ -dr n=100 axis=ud h\n\ - Finds 100 DRs on ud, including \"hidden\" DRs.\n\ -\n\ -" -}, - -{ "drcorners", -"\ -\n\ -HELP PAGE FOR COMMAND drcorners\n\ -\n\ -SYNTAX\n\ -drcorners [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Similar to drfinish, but only solves corners. CO must be solved. The scramble\n\ -can be given as the last argument of the command, or it may be given as an $ID\n\ -or @ID, or it can be typed out on the following line.\n\ -\n\ -OPTIONS\n\ -from {ud|fb|rl} Allows to specify on which axis the CO is solved. It is\n\ - usually not necessary, since nissy will find a CO on any\n\ - axis.\n\ -i Ignores E-layer centers. By default cornersare solved\n\ - relatively to centers; this options allows for solutions\n\ - which solve corners relatively to each other and to the\n\ - U and D sides, but not to the E layer (or any equivalent\n\ - if the CO is not on U/D).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -n=N Specify a maximum number of solutions to be output. N must\n\ - be a number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -drcorners n=3 R' D R2 D' R' U2 R D R' U2 R' D' R\n\ - Produces the following output:\n\ -Found 3 results.\n\ -@1: U' F2 U R2 U2 F2 U F2 U R2 (10)\n\ -@2: U' F2 U R2 U2 B2 U R2 D R2 (10)\n\ -@3: U' F2 U R2 U2 B2 U L2 U L2 (10)\n\ -\n\ -" -}, - -{ "drfinish", -"\ -\n\ -HELP PAGE FOR COMMAND drfinish\n\ -\n\ -SYNTAX\n\ -drfinish [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves the given scramble using the DR moveset. DR must be solved. The scramble\n\ -can be given as the last argument of the command, or it may be given as an $ID\n\ -or @ID, or it can be typed out on the following line.\n\ -\n\ -OPTIONS\n\ -from {ud|fb|rl} Allows to specify on which axis the DR is solved. It is\n\ - usually not necessary, since nissy will find a DR on any\n\ - axis, but it can be useful if one want to e.g. solve an HTR\n\ - state allowing quarter-turns from a specific DR.\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -n=N Specify a maximum number of solutions to be output. N must\n\ - be a number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -dr from ud R L' U2 R' L F2\n\ - Solves the given scramble using the moveset <U,D,R2,L2,F2,B2>. In this case:\n\ -@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)\n\ -drfinish b=7 n=10 $1\n\ - Finds (at most) 10 solutions of length at most 7 for the scramble $1.\n\ -\n\ -" -}, - -{ "eo", -"\ -\n\ -HELP PAGE FOR COMMAND eo\n\ -\n\ -SYNTAX\n\ -eo [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves EO for a given scramble. A scramble can be given as last argument of the\n\ -command, or an ID of a saved scramble can be provided. If none of the two is\n\ -given, a prompt will ask the user to input a new scramble.\n\ -\n\ -OPTIONS\n\ -axis={fb,rl,ud} Specify the axis for the EO. One to three axes can be given,\n\ - comma separated, no spaces.\n\ - Default: EO on any of the three axis (omitting the option is\n\ - the same as specifying axis=fb,rl,ud).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden EOs.\n\ - Default, if an EO ending in e.g. F is shown, the equivalent\n\ - one ending in F' is hidden.\n\ -niss Use NISS.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of EOs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -eo axis=fb $1\n\ - Finds one optimal EO on fb for the first saved scramble.\n\ -\n\ -eo n=5 b=4 U R F \n\ - Finds up to 5 EOs of length at most 4 for scramble U R F.\n\ -\n\ -eo n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F\n\ - Finds up to 100 EOs of lenth at most 4, possibly using NISS, including\n\ - \"hidden\" EOs, excluding the rl axis.\n\ -\n\ -" -}, - -{ "exit", -"\ -\n\ -HELP PAGE FOR COMMAND exit\n\ -\n\ -SYNTAX\n\ -exit\n\ -\n\ -DESCRIPTION\n\ -Exits nissy.\n\ -\n\ -" -}, - -{ "help", -"\ -\n\ -HELP PAGE FOR COMMAND help\n\ -\n\ -SYNTAX\n\ -help [nissy|COMMAND]\n\ -\n\ -DESCRIPTION\n\ -'help nissy' prints a general user manual. 'help COMMAND' prints a detailed\n\ -help page for the command COMMAND, if it exists. 'help' prints a list of all\n\ -available commands a short description for each.\n\ -\n\ -EXAMPLES\n\ -help help\n\ - Prints this help page.\n\ -\n\ -" -}, - -{ "htr", -"\ -\n\ -HELP PAGE FOR COMMAND htr\n\ -\n\ -SYNTAX\n\ -htr [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Finds HTR for a given scramble. DR must be solved. A scramble can be given as\n\ -last argument of the command, or an ID of a saved scramble can be provided. If\n\ -none of the two is given, a prompt will ask the user to input a new scramble.\n\ -\n\ -OPTIONS\n\ -from {ud|fb|rl} Allows to specify on which axis the DR is. This is usually\n\ - not needed, since nissy will automatically find it out.\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden HTRs.\n\ - Default, if an HTR ending in e.g. R is shown, the equivalent\n\ - one ending in R' is hidden.\n\ -niss Use NISS.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of HTRs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -eo n=10 b=7 niss $1\n\ - Finds up to 100 HTRs of lenth at most 7, possibly using NISS, including\n\ - \"hidden\" HTRs, for scramble $1. DR must be solved.\n\ -\n\ -" -}, - -{ "htrfinish", -"\ -\n\ -HELP PAGE FOR COMMAND htrfinish\n\ -\n\ -SYNTAX\n\ -htrfinish [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Similar to drfinish, but uses the moveset <U2,D2,R2,L2,F2,B2>. HTR must be\n\ -solved. The scramble can be given as the last argument of the command, or it\n\ -may be given as an $ID or @ID, or it can be typed out on the following line.\n\ -\n\ -OPTIONS\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -n=N Specify a maximum number ofsolutions to be output. N must be\n\ - a number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -htrfinish R L' U2 R' L F2\n\ - Produces the following solution:\n\ -@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)\n\ -\n\ -" -}, - -{ "invert", -"\ -\n\ -HELP PAGE FOR COMMAND invert\n\ -\n\ -SYNTAX\n\ -invert [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Inverts a sequence of moves, which can be given also as $ID or @ID. The given\n\ -sequence must not use NISS (if it does, use the command unniss first).\n\ -\n\ -EXAMPLES\n\ -invert F R D'\n\ - Prints D R' F'\n\ -\n\ -" -}, - -{ "nissy", -"\ -\n\ -*******************************************************************************\n\ -********************* NISSY: a cube solver and FMC helper *********************\n\ -*******************************************************************************\n\ -\n\ -If you just want to solve the cube, type 'solve' followed by the scramble. This\n\ -will not always give you an optimal solution, unless it is 10 moves or less or\n\ -you use the \"o\" option. Finding the optimal solution might take very long if it\n\ -is 16 moves or more, especially for the first time.\n\ -\n\ -Now the fun stuff. With nissy you can save and manipulate move sequences, for\n\ -example:\n\ -\n\ -nissy-# save R' U' F\n\ -$1: R' U' F\n\ -nissy-# add L2D' $1\n\ -$1: R' U' F L2 D'\n\ -\n\ -You can then ask nissy to solve certain substepson a saved scramble:\n\ -\n\ -nissy-# eo axis=rl $1\n\ -@1: U D F' R (4)\n\ -\n\ -And of course it uses also NISS, if you ask:\n\ -\n\ -nissy-# eo niss axis=rl $1\n\ -@1: (R) (1)\n\ -\n\ -Notice that the sequences you save are marked with a $, while the \"output\"\n\ -sequences are marked with @. The difference between these two type of sequences\n\ -is that those marked with @ are temporary and get lost once you get new output.\n\ -Most commands accept as input either a move sequence typed out, a $-sequence or\n\ -a @-sequence. For example, you can however save a @-sequence and make it\n\ -persistent:\n\ -\n\ -nissy-# save @1\n\ -$2: (R)\n\ -\n\ -Nissy also understands NISS. Let's see a more complicated example where you\n\ -save a scramble, ask for some EOs (using NISS) and then a DR on inverse:\n\ -\n\ -nissy-# save R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F\n\ -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F \n\ -nissy-# eo n=10 niss axis=fb,rl $3\n\ -Found 10 results.\n\ -@1: (U' L B D F) (5)\n\ -@2: (U' L B' D F) (5)\n\ -@3: (L B U D F) (5)\n\ -@4: (L B' U D F) (5)\n\ -@5: R U B U L (5)\n\ -@6: R U' L (B L) (5)\n\ -@7: R U' B U L (5)\n\ -@8: R L (L B L) (5)\n\ -@9: R (U2 D' F R) (5)\n\ -@10: R (U2 F D' R) (5)\n\ -nissy-# add @6 $3\n\ -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) \n\ -nissy-# unniss $3\n\ -@1: L' B' R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L \n\ -nissy-# invert @1\n\ -@1: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L \n\ -nissy-# save @1\n\ -$5: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L \n\ -nissy-# dr from rl $5\n\ -@1: F2 U D2 F' B D B (7)\n\ -nissy-# \n\ -\n\ -If you ask nissy to solve a substep (or the whole cube) using a sequence with\n\ -NISS as scramble, it will first un-NISS it (but without saving the unNISSed\n\ -scramble anywhere):\n\ -\n\ -print $3\n\ -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) \n\ -nissy-# solve $3\n\ -@1: F U' R2 F2 U2 F U2 R2 L2 D R2 U B2 D' L2 D B2 D2 (18)\n\ -\n\ -Nissy knows how to solve certain common sub-steps for DR (or Thistlethwaite /\n\ -Kociemba algorithms). For now it does know more common speedsolving methods.\n\ -\n\ -For a full list of commands type \"help\". For a more detailed help on a specific\n\ -command, type \"help (command)\". The help pages can also be found in the docs\n\ -folder.\n\ -\n\ -If you want to report a bug (I'm sure there are many!) or give a suggestion,\n\ -you can send an email to sebastiano.tronto@gmail.com.\n\ -\n\ -Have fun!\n\ -\n\ -" -}, - -{ "pic", -"\ -\n\ -HELP PAGE FOR COMMAND pic\n\ -\n\ -SYNTAX\n\ -pic [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Prints the cube state after applying the given scramble.\n\ -\n\ -EXAMPLES\n\ -pic R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F \n\ - Gives the following output:\n\ - UF UL UB UR DF DL DB DR FR FL BL BR \n\ -EP: FR UL FL UR UF DB DR BL UB BR DL DF \n\ -EO(F/B): x x x x x x x x \n\ -\n\ - UFR UFL UBL UBR DFR DFL DBL DBR \n\ -CP: UBR UFR DFL DBL UFL UBL DBR DFR \n\ -CO(U/D): ccw cw ccw cw\n\ -\n\ -" -}, - -{ "print", -"\ -\n\ -HELP PAGE FOR COMMAND print\n\ -\n\ -SYNTAX\n\ -print [$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Prints memorized sequences. If no argument is given, it prints all memorized\n\ -scrambles ($ only). If $ID or @ID is specified, it only prints the relative\n\ -memorized sequence.\n\ -\n\ -EXAMPLES\n\ -print\n\ - Prints a list of all memorized scrambles (only $).\n\ -print $2\n\ - Prints the second memorized scramble.\n\ -print @13\n\ - Prints the 13th sequence that was part of the output of the last command.\n\ -\n\ -" -}, - -{ "quit", -"\ -\n\ -HELP PAGE FOR COMMAND quit\n\ -\n\ -SYNTAX\n\ -quit\n\ -\n\ -DESCRIPTION\n\ -Exits nissy.\n\ -\n\ -" -}, - -{ "replace", -"\ -\n\ -HELP PAGE FOR COMMAND replace\n\ -\n\ -SYNTAX\n\ -eo [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Looks for non-optimal subsequences and replaces them to shorten the given\n\ -sequence. By default it tries to shorten every subsequence of up to 10 moves,\n\ -but this can be change with the \"b\" option.\n\ -It outputs at most 10 equivalent optimal sequences for each replaceable part.\n\ -\n\ -OPTIONS\n\ -b=N Finds non-optimal subsequences of up to N moves.\n\ -\n\ -EXAMPLES\n\ -replace D2 F' D2 U2 F' L2 R2 U' D B2 D B2 U B2 F L2 R' F' D U' \n\ - Produces the following output:\n\ -Replace [ R2 U' D B2 D B2 ] (moves 7-12) with: [ D R2 D U' ] (-6+4)\n\ -Replace [ R2 U' D B2 D B2 U ] (moves 7-13) with: [ D R2 D ] (-7+3)\n\ -Replace [ U' D B2 D B2 U ] (moves 8-13) with: [ R2 D R2 D ] (-6+4)\n\ -\n\ -" -}, - -{ "save", -"\ -\n\ -HELP PAGE FOR COMMAND save\n\ -\n\ -SYNTAX\n\ -save [MOVES|@ID|$ID]\n\ -\n\ -DESCRIPTION\n\ -Memorizes the scramble specified by MOVES, given as input or temporarily saved\n\ -as @ID, where ID is a number ('help nissy' for for more on IDs). If an $ID is\n\ -given, it makes a copy of the scramble. An identifier of the form $ID, where ID\n\ -is a number, is assigned to the memorized scramble.\n\ -\n\ -EXAMPLES\n\ -save R U R' U'\n\ - Saves the scramble R U R' U'.\n\ -save F (B)\n\ - Saves the scramble F (B) (NISS notation).\n\ -save @3\n\ - Saves the third output sequence of the last command.\n\ -save $2\n\ - Makes a copy of the second saved scramble.\n\ -\n\ -" -}, - -{ "scramble", -"\ -\n\ -HELP PAGE FOR COMMAND scramble\n\ -\n\ -SYNTAX\n\ -scramble [OPTIONS]\n\ -\n\ -DESCRIPTION\n\ -Produces a random-state scramble. There are options to get a corners-only,\n\ -edges-only or dr-state scramble.\n\ -\n\ -OPTIONS\n\ -c Scrambles corners only (edges are solved).\n\ -e Scrambles edges only (corners are solved).\n\ -dr DR-state scramble. The DR is always on the U/D axis.\n\ -\n\ -\n\ -EXAMPLES\n\ -scramble\n\ - Gives a random-state scramble\n\ -scramble dr\n\ - Gives a random-DR-state scramble\n\ -\n\ -" -}, - -{ "solve", -"\ -\n\ -HELP PAGE FOR COMMAND solve\n\ -\n\ -SYNTAX\n\ -solve [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves the given scramble, which can be given as a sequence of moves or as $ID\n\ -or @ID. If none is given, the user can type it on the next line.\n\ -The algorithm first tries to find a short (<=10 moves) solution, and then\n\ -switches to a 2-step algorithm (unless the option \"o\" is specified, in which\n\ -case it keeps looking for an optimal solution).\n\ -The first time it uses the 2-step algorithm it needs to load some tables, which\n\ -can take a few seconds. It runs much faster after that. If the option \"o\" is\n\ -specified, the first time it loads some large tables, which can take a minute\n\ -or two.\n\ -\n\ -OPTIONS\n\ -b=N Only looks for solutions up to N moves.\n\ -n=N Tries to find multiple solutions, at most N. Multiple\n\ - Solutions will only be found if they are <=10 moves.\n\ -o Looks for optimal solution.\n\ -\n\ -\n\ -EXAMPLES\n\ -solve R' U' F\n\ - Solves the scramble R' U' F.\n\ -solve o b=14 $1\n\ - Tries to solve the scramble $1 optimally, but stops if no solution of 14\n\ - moves or shorter is found.\n\ -solve n=16 R L' U2 R' L F2\n\ - Finds the 16 shortest solutions for the scramble above.\n\ -\n\ -" -}, - -{ "unniss", -"\ -\n\ -HELP PAGE FOR COMMAND unniss\n\ -\n\ -SYNTAX\n\ -unniss [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Removes NISS from a sequence of moves, which can be given also as $ID or @ID.\n\ -A sequence of the form A (B) is translated to B' A.\n\ -\n\ -EXAMPLES\n\ -invert F R (D' L2)\n\ - Prints L2 D F R\n\ -\n\ -" -}, -}; diff --git a/src/io.c b/src/io.c @@ -1,232 +0,0 @@ -#include <stdio.h> -#include <stdint.h> -#include <string.h> - -#include "coordinates.h" -#include "moves.h" -#include "utils.h" - -/* Functions for nice output */ -char *edge_string(int i) { - return (i > -1 && i < 12) ? edge_string_list[i] : "-"; -} - -char *corner_string(int i) { - return (i > -1 && i < 8) ? corner_string_list[i] : "-"; -} - -char *move_string(int i) { - return (i > -1 && i < 19) ? move_string_list[i] : "err"; -} - -void print_ep_array(int ep[12]) { - for (int i = 0; i < 12; i++) - printf(" %s ", edge_string(ep[i])); -} - -void print_ep_int(int ep) { - int aux[12]; - ep_int_to_array(ep, aux); - print_ep_array(aux); -} - -void print_cp_array(int cp[8]) { - for (int i = 0; i < 8; i++) - printf(" %s ", corner_string(cp[i])); -} - -void print_cp_int(int cp) { - int aux[8]; - cp_int_to_array(cp, aux); - print_cp_array(aux); -} - -void print_eo_array(int eo[12]) { - for (int i = 0; i < 12; i++) { - if (eo[i]) - printf(" x "); - else - printf(" "); - } -} - -void print_eo_int(int eo) { - int aux[12]; - eo_11bits_to_array(eo, aux); - print_eo_array(aux); -} - -void print_co_array(int co[8]) { - for (int i = 0; i < 8; i++) { - if (co[i] == 0) - printf(" "); - if (co[i] == 1) - printf(" cw "); - if (co[i] == 2) - printf(" ccw "); - } -} - -void print_co_int(int co) { - int aux[8]; - co_7trits_to_array(co, aux); - print_co_array(aux); -} - -void print_cube_scram(int *scram) { - int ep = 0, cp = 0, eofb = 0, coud = 0; - for (int i = 0; scram[i]; i++) { - ep = apply_move_ep_int(scram[i], ep); - cp = cp_transition_table[cp][scram[i]]; - eofb = eofb_transition_table[eofb][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - } - printf("\t\t"); print_ep_int(0); printf("\n"); - printf("EP:\t\t"); print_ep_int(ep); printf("\n"); - printf("EO(F/B):\t"); print_eo_int(eofb); printf("\n"); - printf("\n"); - printf("\t\t"); print_cp_int(0); printf("\n"); - printf("CP:\t\t"); print_cp_int(cp); printf("\n"); - printf("CO(U/D):\t"); print_co_int(coud); printf("\n"); -} - - -void copy_moves(int *src, int *dst) { - for (int i = 0; (dst[i] = src[i]); i++); -} - -void append_moves(int *src, int *dst) { - int n = 0; - for (; dst[n]; n++); - copy_moves(src, dst+n); -} - -/* Parse a string and saves the move in a. Supports NISS notation. - * Returns the number of moves, or -1 in case of error. */ -int read_moves(char *str, int *a) { - int count = 0; - int niss = 0; - for (int i = 0; str[i] && str[i] != '\n'; i++) { - while (str[i] == ' ' || str[i] == '\t') i++; - switch (str[i]) { - case 'U': - a[count++] = niss ? -U : U; - break; - case 'D': - a[count++] = niss ? -D : D; - break; - case 'R': - a[count++] = niss ? -R : R; - break; - case 'L': - a[count++] = niss ? -L : L; - break; - case 'F': - a[count++] = niss ? -F : F; - break; - case 'B': - a[count++] = niss ? -B : B; - break; - case '(': - if (niss) - return -1; - else - niss = 1; - break; - case ')': - if (!niss) - return -1; - else - niss = 0; - break; - default: - return -1; - } - switch (str[++i]) { - case '2': - a[count-1] += niss ? -1 : 1; - break; - case '\'': - case '3': - a[count-1] += niss ? -2 : 2; - break; - case '1': - default: - --i; - } - } - a[count] = 0; - return count; -} - -/* Read moves from standard input, after a prompt. */ -int read_moves_from_prompt(int *a) { - char str[1000]; - printf("Enter moves: "); - if (fgets(str, 1000, stdin) == NULL) - return -1; - return read_moves(str, a); -} - -/* Read moves from a list of token, each containing one or more moves. */ -int read_moves_from_tok(int n, char tok[][100], int *a) { - char str[1000] = ""; - for (int i = 0; i < n; i++) - strcat(str, tok[i]); - return read_moves(str, a); -} - -/* Checks if a sequence of moves uses NISS */ -int uses_niss(int *str) { - for (int i = 0; str[i]; i++) - if (str[i] < 0) - return 1; - return 0; -} - -/* A (B) -> B' A */ -int unniss(int *src, int *dst) { - int n = 0; - for (int i = 0; src[i]; i++) - if (src[i] < 0) - n++; - - int norm_count = n, inv_count = n-1; - for (int i = 0; src[i]; i++) - if (src[i] > 0) - dst[norm_count++] = src[i]; - else - dst[inv_count--] = inverse_move[-src[i]]; - - dst[norm_count] = 0; - - return n; -} - -int invert(int *src, int *dst) { - int aux[255]; - for (int i = 0; (aux[i] = -src[i]); i++); - return unniss(aux, dst); -} - -int len(int *scram) { - int m; - for (m = 0; scram[m]; m++); - return m; -} - -void print_moves(int moves_list[]) { - int niss = 0; - for (int i = 0; moves_list[i]; i++) { - if (!niss && moves_list[i] < 0) { - printf("("); - niss = 1; - } - printf("%s", move_string_list[abs(moves_list[i])]); - if (niss && moves_list[i+1] >= 0) { - niss = 0; - printf(")"); - } - printf(" "); - } -} diff --git a/src/io.h b/src/io.h @@ -1,21 +0,0 @@ -#include <stdint.h> - -char *edge_string(int edge); -char *corner_string(int edge); -char *move_string(int move); - -void print_cube_scram(int *scram); - -void copy_moves(int *src, int *dst); -void append_moves(int *src, int *dst); - -int read_moves(char *str, int *a); -int read_moves_from_prompt(int *a); -int read_moves_from_tok(int n, char tok[][100], int *a); - -int uses_niss(int *str); -int unniss(int *src, int *dst); -int invert(int *src, int *dst); -int len(int *scram); - -void print_moves(int move_list[]); diff --git a/src/main.c b/src/main.c @@ -1,937 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include "utils.h" -#include "coordinates.h" -#include "io.h" -#include "moves.h" -#include "solver.h" -#include "string.h" -#include "helppages.h" -/* Saved sequences of moves */ -int scr_count=1, tmp_count=1, max_tmp=999; -int scrambles[255][255], tmp[1000][255]; - -int read_moves_from_variable(char *id, int *dst) { - char c = id[0]; - if (c != '$' && c != '@') - return -1; - int n = atoi(id+1); - if (n <= 0 || n >= (c == '$' ? scr_count : tmp_count)) - return -1; - copy_moves(c == '$' ? scrambles[n] : tmp[n], dst); - return n; -} - -int read_moves_from_argument(int n, char tok[][100], int *dst) { - int r = read_moves_from_variable(tok[0], dst); - return (r != -1) ? r : read_moves_from_tok(n, tok, dst); -} - -void print_results(int n, int res[][30]) { - if (n == -1) - printf("Pre-conditions not satisfied (or other error).\n"); - - if (n == 0) - printf("No result found (try different bounds).\n"); - - if (n > 1) - printf("Found %d results.\n", n); - tmp_count = 1; /* Reset temporary count */ - for (int i = 0; i < n; i++) { - if (i < max_tmp) { - copy_moves(res[i], tmp[tmp_count]); - printf("@%d:\t", tmp_count++); - } else { - printf(" \t"); - } - print_moves(res[i]); - printf("(%d)\n", len(res[i])); - } -} - -/* Removes extra white spaces from the input string */ -int parsecmd(char *cmd, char cmdtok[][100]) { - char *i = cmd, *j = cmd; - while (*j != '\n' && *j != EOF) { - *i = *j; - if (*i == ' ' || *i == '\t') - *i = ' '; - ++j; - if (*i == ' ' || *i == '\t') - while (*j == ' ' || *j == '\t') - ++j; - ++i; - } - if (*(i-1) == ' ') - *(i-1) = 0; - else - *i = 0; - - int n = 0; - char *s = strtok(cmd, " "); - while (s != NULL) { - strcpy(cmdtok[n++], s); - s = strtok(NULL, " "); - } - return n; -} - -void scramble_cmd(int n, char cmdtok[][100]) { - int c = 0, e = 0, dr = 0; - - if (n > 2 || cmdtok[0][0] != 's') { /* Second case avoids warning */ - printf("scramble: wrong syntax\n"); - return; - } else if (n == 2) { - if (!strcmp(cmdtok[1], "c")) { - c = 1; - } else if (!strcmp(cmdtok[1], "e")) { - e = 1; - } else if (!strcmp(cmdtok[1], "dr")) { - dr = 1; - } else { - printf("scramble: wrong syntax\n"); - return; - } - } - - int scram[2][30]; - - srand(time(NULL)); - int eofb = rand() % pow2to11; - int coud = rand() % pow3to7; - int ep = rand() % factorial12; - int cp = rand() % factorial8; - - if (c) { - eofb = ep = 0; - } else if (e) { - coud = cp = 0; - } else if (dr) { - eofb = coud = 0; - int epud = rand() % factorial8; - int epe = rand() % factorial4; - int ep_arr[12]; - epud_int_to_array(epud, ep_arr); - epe_int_to_array(epe, ep_arr); - ep = ep_array_to_int(ep_arr); - while (perm_sign_int(ep, 12) != perm_sign_int(cp, 8)) - cp = (cp+1) % factorial8; - } else { - while (perm_sign_int(ep, 12) != perm_sign_int(cp, 8)) - cp = (cp+1) % factorial8; - } - - reach_state(eofb, coud, ep, cp, scram); - /* Debug */ - /* printf("State: %d %d %d %d\n", eofb, coud, ep, cp); */ - print_results(1, scram); -} - -void save_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("save: error reading moves. Not saved.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("save: error reading moves or ID. Not saved.\n"); - return; - } - - copy_moves(scram, scrambles[scr_count]); - - printf("$%d:\t", scr_count); - print_moves(scrambles[scr_count]); - printf("\n"); - scr_count++; -} - -void change_cmd(int n, char cmdtok[][100]) { - int id, scram[255]; - if (n == 1) { - printf("change: you must specify an $ID.\n"); - return; - } else if (cmdtok[1][0] != '$') { - printf("change: invalid $ID.\n"); - return; - } else { - id = atoi(cmdtok[1]+1); - if (id <= 0 || id >= scr_count) { - printf("change: invalid $ID.\n"); - return; - } - if (n == 2) { - if (read_moves_from_prompt(scram) == -1) { - printf("change: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-2, cmdtok+2, scram) == -1 ) { - printf("change: error reading moves or ID.\n"); - return; - } - } - - copy_moves(scram, scrambles[id]); - - printf("$%d:\t", id); - print_moves(scrambles[id]); - printf("\n"); -} - -void print_cmd(int n, char cmdtok[][100]) { - if (n == 1) { - for (int i = 1; i < scr_count; i++) { - printf("$%d:\t", i); - print_moves(scrambles[i]); - printf("\n"); - } - } else if (n == 2) { - int i = atoi(cmdtok[1]+1); - char sign = cmdtok[1][0]; - if (sign != '$' && sign != '@') { - printf("print: invalid ID (must start with $ or @).\n"); - return; - } - if (i > 0 && i < (sign == '$' ? scr_count : tmp_count)) { - printf("%c%d:\t", sign, i); - print_moves(sign == '$' ? scrambles[i] : tmp[i]); - printf("\n"); - } else { - printf("print: invalid ID.\n"); - return; - } - } else { - printf("print: wrong syntax.\n"); - } -} - -void add_cmd(int n, char cmdtok[][100]) { - int id, scram[255]; - if (n == 1) { - printf("add: you must specify a destination $ID.\n"); - return; - } else if (cmdtok[n-1][0] != '$') { - printf("add: invalid destination $ID.\n"); - return; - } else { - id = atoi(cmdtok[n-1]+1); - if (id <= 0 || id >= scr_count) { - printf("add: invalid destination $ID.\n"); - return; - } - if (n == 2) { - if (read_moves_from_prompt(scram) == -1) { - printf("add: error reading moves.\n"); - return; - } - } else { - if (read_moves_from_argument(n-2, cmdtok+1, scram) == -1) { - printf("add: error reading moves or ID.\n"); - return; - } - } - } - - append_moves(scram, scrambles[id]); - - printf("$%d:\t", id); - print_moves(scrambles[id]); - printf("\n"); -} - -void invert_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("invert: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("invert: error reading moves or ID.\n"); - return; - } - - if (uses_niss(scram)) { - printf("invert: cannot invert NISS.\n"); - return; - } - - invert(scram, tmp[1]); - tmp_count = 2; - - printf("@1:\t"); - print_moves(tmp[1]); - printf("\n"); -} - -void unniss_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("unniss: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("unniss: error reading moves or ID.\n"); - return; - } - - unniss(scram, tmp[1]); - tmp_count = 2; - - printf("@1:\t"); - print_moves(tmp[1]); - printf("\n"); -} - -void pic_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("pic: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("pic: error reading moves or ID.\n"); - return; - } - print_cube_scram(scram); -} - -void solve_cmd(int n, char cmdtok[][100]) { - int m = 1, b = 25, optimal = 0; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("solve: bad option b.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("solve: bad option n.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "o")) { - optimal = 1; - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("solve: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("solve: error reading moves.\n"); - return; - } - } - - /* Call solver and print results */ - unniss(scram, scram_unnissed); - int sol[m+2][30]; - int s = solve_scram(scram_unnissed, sol, m, b, optimal); - print_results(s, sol); -} - -void replace_cmd(int n, char cmdtok[][100]) { - int m = 10; /* max length */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "b=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("replace: bad option n.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("replace: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("replace: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - int l = len(scram_unnissed); - int aux1[255], aux2[15][30], aux3[30]; - for (int i = 0; i < l; i++) { - for (int j = 2; j <= m && i + j <= l; j++) { - copy_moves(scram_unnissed+i, aux1); - aux1[j] = 0; - int s = solve_scram(aux1, aux2, 10, j-1, 1); - for (int k = 0; k < s; k++) { - invert(aux2[k], aux3); - /* TODO: the following part should also chek for the case when - * the last moves are R L or similar. */ - if (aux3[0] != aux1[0] && aux3[len(aux3)-1] != aux1[len(aux1)-1]) { - printf("Replace [ "); - print_moves(aux1); - printf("] (moves %d-%d) with: [ ", i+1, i+j); - print_moves(aux3); - printf("] (-%d+%d)\n", j, len(aux3)); - } - } - } - } -} - -void clear_cmd(int n, char cmdtok[][100]) { - if (n > 1 || cmdtok[0][0] != 'c') { /* Avoid unused variable warning */ - printf("clear: syntax error.\n"); - return; - } - scr_count = tmp_count = 1; -} - -void eo_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20; - int niss = 0, hide = 1; - int fb = 1, rl = 1, ud = 1; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strncmp(cmdtok[i], "axis=", 5)) { - fb = rl = ud = 0; - if (strstr(cmdtok[i], "fb") != NULL) - fb = 1; - if (strstr(cmdtok[i], "rl") != NULL) - rl = 1; - if (strstr(cmdtok[i], "ud") != NULL) - ud = 1; - if (fb + rl + ud == 0) { - printf("eo: bad axis option.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("eo: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("eo: bad option b.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("eo: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("eo: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver and print results */ - int eo_list[m+5][30]; - int neo = eo_scram_spam(scram_unnissed, eo_list, fb, rl, ud, m, b, niss, - hide); - print_results(neo, eo_list); -} - -void co_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20, ignore = 0; - int niss = 0, hide = 1; - int fb = 1, rl = 1, ud = 1; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strcmp(cmdtok[i], "i")) { - ignore = 1; - } else if (!strncmp(cmdtok[i], "axis=", 5)) { - fb = rl = ud = 0; - if (strstr(cmdtok[i], "fb") != NULL) - fb = 1; - if (strstr(cmdtok[i], "rl") != NULL) - rl = 1; - if (strstr(cmdtok[i], "ud") != NULL) - ud = 1; - if (fb + rl + ud == 0) { - printf("co: bad axis option.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("co: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("co: bad option b.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("co: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("co: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver and print results */ - int co_list[m+5][30]; - int nco = co_scram_spam(scram_unnissed, co_list, fb, rl, ud, m, b, niss, - hide, ignore); - print_results(nco, co_list); -} - -void dr_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20; - int niss = 0, hide = 1; - int from = 0; /* 0: direct dr; {1,2,3}: from {eofb,eorl,eoud} */ - int fb = 1, rl = 1, ud = 1; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strncmp(cmdtok[i], "axis=", 5)) { - fb = rl = ud = 0; - if (strstr(cmdtok[i], "fb") != NULL) - fb = 1; - if (strstr(cmdtok[i], "rl") != NULL) - rl = 1; - if (strstr(cmdtok[i], "ud") != NULL) - ud = 1; - if (fb + rl + ud == 0) { - printf("dr: bad axis option.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("dr: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("dr: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"fb", "rl", "ud"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("dr: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("dr: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("dr: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int dr_list[m+5][30], ndr; - if (from) { - ndr = drfrom_scram_spam(scram_unnissed, dr_list, from, fb, rl, ud, - m, b, niss, hide); - if (ndr == -1) { - printf("dr: from given, but EO not found (possibly other error).\n"); - return; - } - } else { - if (niss) - printf("Warning: not using NISS for direct DR.\n"); - ndr = dr_scram_spam(scram_unnissed, dr_list, fb, rl, ud, m, b, hide); - } - print_results(ndr, dr_list); -} - -void htr_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20; - int niss = 0, hide = 1; - int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("htr: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("htr: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"ud", "fb", "rl"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("htr: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("htr: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("htr: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int htr_list[m+5][30], nhtr; - nhtr = htr_scram_spam(scram_unnissed, htr_list, from, m, b, niss, hide); - print_results(nhtr, htr_list); -} - -void drfinish_cmd(int n, char cmdtok[][100]) { - /* Default values */ - int m = 1, b = 20; - int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("drfinish: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("drfinish: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"ud", "fb", "rl"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("drfinish: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("drfinish: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("drfinish: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int c_list[m+5][30], nc; - nc = dr_finish_scram_spam(scram_unnissed, c_list, from, m, b); - print_results(nc, c_list); -} - -void htrfinish_cmd(int n, char cmdtok[][100]) { - /* Default values */ - int m = 1, b = 20; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("htrfinish: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("htrfinish: bad option b.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("htrfinish: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("htrfinish: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int c_list[m+5][30], nc; - nc = htr_finish_scram_spam(scram_unnissed, c_list, m, b); - print_results(nc, c_list); -} - -void drcorners_cmd(int n, char cmdtok[][100]) { - /* Default values */ - int m = 1, b = 20, ignore = 0; - int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("drcorners: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("drcorners: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "i")) { - ignore = 1; - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"ud", "fb", "rl"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("drcorners: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("drcorners: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("drcorners: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int c_list[m+5][30], nc; - nc = dr_corners_scram_spam(scram_unnissed, c_list, from, m, b, ignore); - print_results(nc, c_list); -} - - -void exit_quit_cmd(int n, char cmdtok[][100]) { - if (n == 1) - exit(0); - else - printf("%s: wrong synstax.\n", cmdtok[0]); -} - -/***************************************************************/ -/* List of all commands */ -/* Important: they must be in the same order in the two arrays */ -/***************************************************************/ - -char *commands[][10] = { - {"help", "[COMMAND]", - "Print this help, or a help page for COMMAND."}, - {"scramble", "[OPTIONS]", - "Prints a random-state scramble."}, - {"save", "[MOVES|@ID|$ID]", - "Save or copy a scramble."}, - {"change", "$ID1 [MOVES|$ID2|@ID2]", - "Change a memorized scramble."}, - {"print", "[$ID|@ID]", - "Print memorized sequences."}, - {"add", "[MOVES|$ID1|@ID1] $ID2", - "Add moves at the end of a memorized scramble."}, - {"invert", "[MOVES|$ID|@ID]", - "Inverts the given sequence of moves."}, - {"unniss", "[MOVES|$ID|@ID]}", - "Removes NISS: A (B) -> B\' A."}, - {"pic", "[MOVES|$ID|@ID]", - "Show a text description of the scrambled cube."}, - {"solve", "[MOVES|$ID|@ID]", - "Solves a scramble."}, - {"replace", "[MOVES|$ID|@ID]", - "Find non-optimal subsequences."}, - {"clear", "", - "Delete saved scrambles and output sequences."}, - {"eo", "[MOVES|$ID|@ID]", - "Solves EO."}, - {"co", "[MOVES|$ID|@ID]", - "Solves CO."}, - {"dr", "[MOVES|$ID|@ID]", - "Solves DR, either directly or from eo."}, - {"htr", "[MOVES|$ID|@ID]", - "Solves HTR from DR."}, - {"drfinish", "[MOVES|$ID|@ID]", - "Solves the cube after DR."}, - {"htrfinish", "[MOVES|$ID|@ID]", - "Solves the cube using only half turns."}, - {"drcorners", "[MOVES|$ID|@ID]", - "Solves corners after DR."}, - {"exit", "", - "Exit nissy."}, - {"quit", "", - "Exit nissy."}, - {"", "", ""} -}; - -void help_cmd(int n, char cmdtok[][100]) { - if (n == 1) { - printf("\n"); - for (int i = 0; commands[i][0][0]; i++) - printf("%-10s%-25s%s\n", commands[i][0], commands[i][1], commands[i][2]); - printf("\n"); - printf("Type \'help\' followed by a command for a detailed help page.\n"); - printf("Type \'help nissy\' for a general user guide.\n"); - } else if (n == 2) { - for (int i = 0; i < Npages; i++) { - if (!strcmp(helppages[i][0], cmdtok[1])) { - printf("%s", helppages[i][1]); - return; - } - } - printf("No help page for %s.\n", cmdtok[1]); - return; - } else { - printf("help: wrong syntax.\n"); - } -} - -void (*cmd_list[])(int n, char cmdtok[][100]) = { - help_cmd, scramble_cmd, save_cmd, change_cmd, print_cmd, - add_cmd, invert_cmd, unniss_cmd, pic_cmd, - solve_cmd, replace_cmd, clear_cmd, - eo_cmd, co_cmd, dr_cmd, htr_cmd, - drfinish_cmd, htrfinish_cmd, drcorners_cmd, - exit_quit_cmd, exit_quit_cmd, NULL -}; - - -void execcmd(int n, char cmdtok[][100]) { - int i = 0; - while (strcmp(commands[i][0], cmdtok[0]) && strcmp(commands[i][0], "")) - i++; - if (strcmp(commands[i][0], "")) - (*cmd_list[i])(n, cmdtok); - else - printf("%s: not a command.\n", cmdtok[0]); -} - - -/* Main loop */ - -int main() { - init_transition_table(); - init_possible_next(); - - printf("Type help for a list of commands.\n"); - - char cmd[1000] = ""; - while (1) { - printf("nissy-# "); - if (fgets(cmd, 1000, stdin) == NULL) - break; - char cmdtok[100][100]; - int n = parsecmd(cmd, cmdtok); - if (n == 0) - continue; - execcmd(n, cmdtok); - } - - return 0; -} diff --git a/src/moves.c b/src/moves.c @@ -1,521 +1,475 @@ -/* This file contains the definitions of the basic moves of the cube. - * There is no object or type representing the cube. - * Data about the cube can be represented by arrays (describing the position - * of pieces of certain types), integers (representing for example a bitmask - * for the orientation of pieces of certain type, or the permutation index of - * an array representing the permutation of pieces). - * Each of the moves functions operates on one such piece of data. - * - * For example, a way of representing the cube can be: - * - An integer eo, which is a bitmask for the orientation of the edges. - * - An integer co, same for corners. - * - An array ep[12], where a[i]=j means that the edge j is in place i. - * - An integer cp representing the permutation index of a permutation array - * which is the analogue of that described for edges. - * - * Different representations will be used for different use-cases. */ - -#include "coordinates.h" #include "moves.h" -/* possible_next[i][j] is a bitmask representing the possible - * next moves we can apply. For example, if the last moves a 0 R then it does - * not make sense to apply R, R2 or R'. If they are U D2 it does not make - * sense to apply any U* or D*. */ -int possible_next[19][19]; +/* Local functions ***********************************************************/ -int parallel(int m1, int m2) { - if (m1 == 0 || m2 == 0) return 0; - return ((m1-1)/6 == (m2-1)/6); -} - -int compute_possible_next(int last1, int last2) { - if (last1 == 0) return move_mask_all; - - /* Removes the 2 or ' (e.g. turns U2 to U, R' to R). */ - last2 = (last2 == 0) ? last2 : 3*((last2-1)/3) + 1; - last1 = 3*((last1-1)/3) + 1; +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static bool read_mtables_file(); +static bool write_mtables_file(); - int mask = move_mask_all ^ (7 << last1); +/* Tables and other data *****************************************************/ - if (parallel(last1, last2)) - mask ^= 7 << last2; - else if (last1 % 6 == 4) /*Always U before D, R before L, F before B*/ - mask ^= 7 << (last1-3); +/* Every move is translated to a an <U, x, y> alg before filling the + transition tables, see init_moves() */ - return mask; -} - -void init_possible_next() { - for (int i = 0; i < 19; i++) - for (int j = 0; j < 19; j++) - possible_next[i][j] = compute_possible_next(i, j); -} - -/* Piece cycles depending on the move. For example edge_cycle[U2][UF] - * gives the piece in position UF after applying U2 to a solved cube */ - -int edge_cycle[19][12] = { - {UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR}, /* - */ - {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, /* U */ - {UB, UR, UF, UL, DF, DL, DB, DR, FR, FL, BL, BR}, /* U2 */ - {UL, UB, UR, UF, DF, DL, DB, DR, FR, FL, BL, BR}, /* U' */ - {UF, UL, UB, UR, DL, DB, DR, DF, FR, FL, BL, BR}, /* D */ - {UF, UL, UB, UR, DB, DR, DF, DL, FR, FL, BL, BR}, /* D2 */ - {UF, UL, UB, UR, DR, DF, DL, DB, FR, FL, BL, BR}, /* D' */ - {UF, UL, UB, FR, DF, DL, DB, BR, DR, FL, BL, UR}, /* R */ - {UF, UL, UB, DR, DF, DL, DB, UR, BR, FL, BL, FR}, /* R2 */ - {UF, UL, UB, BR, DF, DL, DB, FR, UR, FL, BL, DR}, /* R' */ - {UF, BL, UB, UR, DF, FL, DB, DR, FR, UL, DL, BR}, /* L */ - {UF, DL, UB, UR, DF, UL, DB, DR, FR, BL, FL, BR}, /* L2 */ - {UF, FL, UB, UR, DF, BL, DB, DR, FR, DL, UL, BR}, /* L' */ - {FL, UL, UB, UR, FR, DL, DB, DR, UF, DF, BL, BR}, /* F */ - {DF, UL, UB, UR, UF, DL, DB, DR, FL, FR, BL, BR}, /* F2 */ - {FR, UL, UB, UR, FL, DL, DB, DR, DF, UF, BL, BR}, /* F' */ - {UF, UL, BR, UR, DF, DL, BL, DR, FR, FL, UB, DB}, /* B */ - {UF, UL, DB, UR, DF, DL, UB, DR, FR, FL, BR, BL}, /* B2 */ - {UF, UL, BL, UR, DF, DL, BR, DR, FR, FL, DB, UB} /* B' */ +static int edge_cycle[NMOVES][12] = +{ + [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, + [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, + [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL } }; -int corner_cycle[19][8] = { - {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, /* - */ - {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, /* U */ - {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR}, /* U2 */ - {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR}, /* U' */ - {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR}, /* D */ - {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL}, /* D2 */ - {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL}, /* D' */ - {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR}, /* R */ - {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR}, /* R2 */ - {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR}, /* R' */ - {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR}, /* L */ - {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR}, /* L2 */ - {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR}, /* L' */ - {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR}, /* F */ - {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR}, /* F2 */ - {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR}, /* F' */ - {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL}, /* B */ - {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL}, /* B2 */ - {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR}, /* U' */ +static int corner_cycle[NMOVES][8] = +{ + [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR }, + [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR }, + [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL } }; -/* Transition tables */ - -int eofb_transition_table[pow2to11][19]; -int eorl_transition_table[pow2to11][19]; -int eoud_transition_table[pow2to11][19]; -int coud_transition_table[pow3to7][19]; -int cofb_transition_table[pow3to7][19]; -int corl_transition_table[pow3to7][19]; -int epud_transition_table[factorial8][19]; -int eprl_transition_table[factorial8][19]; -int epfb_transition_table[factorial8][19]; -int epose_transition_table[binom12on4][19]; -int eposs_transition_table[binom12on4][19]; -int eposm_transition_table[binom12on4][19]; -int epe_transition_table[factorial4][19]; -int eps_transition_table[factorial4][19]; -int epm_transition_table[factorial4][19]; -int emslices_transition_table[binom12on4*binom8on4][19]; -int cp_transition_table[factorial8][19]; - -/***/ -/* Functions for permuting pieces (given in array format) */ -/***/ - -void apply_move_ep_array(int move, int ep[12]) { - int aux[12]; - for (int i = 0; i < 12; i++) - aux[i] = ep[i]; - for (int i = 0; i < 12; i++) - ep[i] = aux[edge_cycle[move][i]]; -} - -void apply_move_cp_array(int move, int cp[8]) { - int aux[8]; - for (int i = 0; i < 8; i++) - aux[i] = cp[i]; - for (int i = 0; i < 8; i++) - cp[i] = aux[corner_cycle[move][i]]; -} - -/***/ -/* Functions for permuting pieces (given in integer format) */ -/***/ - -int apply_move_ep_int(int move, int ep) { - int a[12]; - ep_int_to_array(ep, a); - apply_move_ep_array(move, a); - return ep_array_to_int(a); -} - -int apply_move_epud_int(int move, int ep) { - int a[12]; - epud_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epud_array_to_int(a); -} - -int apply_move_eprl_int(int move, int ep) { - int a[12]; - eprl_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eprl_array_to_int(a); -} - -int apply_move_epfb_int(int move, int ep) { - int a[12]; - epfb_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epfb_array_to_int(a); -} - -int apply_move_epose_int(int move, int ep) { - int a[12]; - epose_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epose_array_to_int(a); -} - -int apply_move_eposs_int(int move, int ep) { - int a[12]; - eposs_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eposs_array_to_int(a); -} - -int apply_move_eposm_int(int move, int ep) { - int a[12]; - eposm_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eposm_array_to_int(a); -} - -int apply_move_epe_int(int move, int ep) { - int a[12]; - epe_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epe_array_to_int(a); -} - -int apply_move_eps_int(int move, int ep) { - int a[12]; - eps_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eps_array_to_int(a); -} - -int apply_move_epm_int(int move, int ep) { - int a[12]; - epm_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epm_array_to_int(a); -} - -int apply_move_emslices_int(int move, int e) { - int a[12]; - emslices_int_to_array(e, a); - apply_move_ep_array(move, a); - return emslices_array_to_int(a); -} - -int apply_move_cp_int(int move, int cp) { - int a[8]; - cp_int_to_array(cp, a); - apply_move_cp_array(move, a); - return cp_array_to_int(a); -} - -int apply_move_eofb_int(int move, int eo) { - int a[12]; - eo_11bits_to_array(eo, a); - apply_move_ep_array(move, a); - /* Change edge orientation */ - if (move == F || move == F3) { - a[UF] = 1 - a[UF]; - a[DF] = 1 - a[DF]; - a[FR] = 1 - a[FR]; - a[FL] = 1 - a[FL]; - } - if (move == B || move == B3) { - a[UB] = 1 - a[UB]; - a[DB] = 1 - a[DB]; - a[BL] = 1 - a[BL]; - a[BR] = 1 - a[BR]; - } - return eo_array_to_11bits(a); -} - -int apply_move_eorl_int(int move, int eo) { - int a[12]; - eo_11bits_to_array(eo, a); - apply_move_ep_array(move, a); - /* Change edge orientation */ - if (move == R || move == R3) { - a[UR] = 1 - a[UR]; - a[DR] = 1 - a[DR]; - a[FR] = 1 - a[FR]; - a[BR] = 1 - a[BR]; - } - if (move == L || move == L3) { - a[UL] = 1 - a[UL]; - a[DL] = 1 - a[DL]; - a[FL] = 1 - a[FL]; - a[BL] = 1 - a[BL]; - } - return eo_array_to_11bits(a); -} - -int apply_move_eoud_int(int move, int eo) { - int a[12]; - eo_11bits_to_array(eo, a); - apply_move_ep_array(move, a); - /* Change edge orientation */ - if (move == U || move == U3) { - a[UF] = 1 - a[UF]; - a[UL] = 1 - a[UL]; - a[UB] = 1 - a[UB]; - a[UR] = 1 - a[UR]; - } - if (move == D || move == D3) { - a[DF] = 1 - a[DF]; - a[DL] = 1 - a[DL]; - a[DB] = 1 - a[DB]; - a[DR] = 1 - a[DR]; - } - return eo_array_to_11bits(a); -} - -int apply_move_coud_int(int move, int co) { - int a[8]; - co_7trits_to_array(co, a); - apply_move_cp_array(move, a); - /* Change corner orientation */ - if (move == R || move == R3) { - a[UFR] = (a[UFR] + 2) % 3; - a[UBR] = (a[UBR] + 1) % 3; - a[DBR] = (a[DBR] + 2) % 3; - a[DFR] = (a[DFR] + 1) % 3; - } - if (move == L || move == L3) { - a[UBL] = (a[UBL] + 2) % 3; - a[UFL] = (a[UFL] + 1) % 3; - a[DFL] = (a[DFL] + 2) % 3; - a[DBL] = (a[DBL] + 1) % 3; - } - if (move == F || move == F3) { - a[UFL] = (a[UFL] + 2) % 3; - a[UFR] = (a[UFR] + 1) % 3; - a[DFR] = (a[DFR] + 2) % 3; - a[DFL] = (a[DFL] + 1) % 3; - } - if (move == B || move == B3) { - a[UBR] = (a[UBR] + 2) % 3; - a[UBL] = (a[UBL] + 1) % 3; - a[DBL] = (a[DBL] + 2) % 3; - a[DBR] = (a[DBR] + 1) % 3; - } - return co_array_to_7trits(a); -} - -int apply_move_cofb_int(int move, int co) { - int a[8]; - co_7trits_to_array(co, a); - apply_move_cp_array(move, a); - /* Change corner orientation */ - if (move == R || move == R3) { - a[UFR] = (a[UFR] + 1) % 3; - a[UBR] = (a[UBR] + 2) % 3; - a[DBR] = (a[DBR] + 1) % 3; - a[DFR] = (a[DFR] + 2) % 3; - } - if (move == L || move == L3) { - a[UBL] = (a[UBL] + 1) % 3; - a[UFL] = (a[UFL] + 2) % 3; - a[DFL] = (a[DFL] + 1) % 3; - a[DBL] = (a[DBL] + 2) % 3; - } - if (move == U || move == U3) { - a[UFL] = (a[UFL] + 1) % 3; - a[UFR] = (a[UFR] + 2) % 3; - a[UBL] = (a[UBL] + 2) % 3; - a[UBR] = (a[UBR] + 1) % 3; - } - if (move == D || move == D3) { - a[DFL] = (a[DFL] + 2) % 3; - a[DFR] = (a[DFR] + 1) % 3; - a[DBL] = (a[DBL] + 1) % 3; - a[DBR] = (a[DBR] + 2) % 3; - } - return co_array_to_7trits(a); -} - -int apply_move_corl_int(int move, int co) { - int a[8]; - co_7trits_to_array(co, a); - apply_move_cp_array(move, a); - /* Change corner orientation */ - if (move == F || move == F3) { - a[UFR] = (a[UFR] + 2) % 3; - a[UFL] = (a[UFL] + 1) % 3; - a[DFL] = (a[DFL] + 2) % 3; - a[DFR] = (a[DFR] + 1) % 3; - } - if (move == B || move == B3) { - a[UBL] = (a[UBL] + 2) % 3; - a[UBR] = (a[UBR] + 1) % 3; - a[DBR] = (a[DBR] + 2) % 3; - a[DBL] = (a[DBL] + 1) % 3; - } - if (move == U || move == U3) { - a[UFL] = (a[UFL] + 2) % 3; - a[UFR] = (a[UFR] + 1) % 3; - a[UBL] = (a[UBL] + 1) % 3; - a[UBR] = (a[UBR] + 2) % 3; - } - if (move == D || move == D3) { - a[DFL] = (a[DFL] + 1) % 3; - a[DFR] = (a[DFR] + 2) % 3; - a[DBL] = (a[DBL] + 2) % 3; - a[DBR] = (a[DBR] + 1) % 3; - } - return co_array_to_7trits(a); -} - - - -/* Initialize transition tables */ - -void init_epud_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - if (move_mask_drud & (1 << j)) - epud_transition_table[i][j] = apply_move_epud_int(j, i); -} - -void init_eprl_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - if (move_mask_drrl & (1 << j)) - eprl_transition_table[i][j] = apply_move_eprl_int(j, i); -} - -void init_epfb_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - if (move_mask_drfb & (1 << j)) - epfb_transition_table[i][j] = apply_move_epfb_int(j, i); -} - -void init_epose_transition_table() { - for (int i = 0; i < binom12on4; i++) - for (int j = 0; j < 19; j++) - epose_transition_table[i][j] = apply_move_epose_int(j, i); -} - -void init_eposs_transition_table() { - for (int i = 0; i < binom12on4; i++) - for (int j = 0; j < 19; j++) - eposs_transition_table[i][j] = apply_move_eposs_int(j, i); -} - -void init_eposm_transition_table() { - for (int i = 0; i < binom12on4; i++) - for (int j = 0; j < 19; j++) - eposm_transition_table[i][j] = apply_move_eposm_int(j, i); -} - -void init_epe_transition_table() { - for (int i = 0; i < factorial4; i++) { - for (int j = 0; j < 19; j++) - if (move_mask_drud & (1 << j)) - epe_transition_table[i][j] = apply_move_epe_int(j, i); - } -} - -void init_eps_transition_table() { - for (int i = 0; i < factorial4; i++) { - for (int j = 0; j < 19; j++) - if (move_mask_drfb & (1 << j)) - eps_transition_table[i][j] = apply_move_eps_int(j, i); - } -} - -void init_epm_transition_table() { - for (int i = 0; i < factorial4; i++) { - for (int j = 0; j < 19; j++) - if (move_mask_drrl & (1 << j)) - epm_transition_table[i][j] = apply_move_epm_int(j, i); - } -} - -void init_emslices_transition_table() { - for (int i = 0; i < binom12on4*binom8on4; i++) { - for (int j = 0; j < 19; j++) - emslices_transition_table[i][j] = apply_move_emslices_int(j, i); - } -} +static int center_cycle[NMOVES][6] = +{ + [x] = { F_center, B_center, R_center, L_center, D_center, U_center }, + [y] = { U_center, D_center, B_center, F_center, R_center, L_center } +}; -void init_cp_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - cp_transition_table[i][j] = apply_move_cp_int(j, i); -} +static int eofb_flipped[NMOVES][12] = { + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; -void init_eofb_transition_table() { - for (int i = 0; i < pow2to11; i++) - for (int j = 0; j < 19; j++) - eofb_transition_table[i][j] = apply_move_eofb_int(j, i); -} +static int eorl_flipped[NMOVES][12] = { + [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; -void init_eorl_transition_table() { - for (int i = 0; i < pow2to11; i++) - for (int j = 0; j < 19; j++) - eorl_transition_table[i][j] = apply_move_eorl_int(j, i); -} +static int eoud_flipped[NMOVES][12] = { + [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } +}; -void init_eoud_transition_table() { - for (int i = 0; i < pow2to11; i++) - for (int j = 0; j < 19; j++) - eoud_transition_table[i][j] = apply_move_eoud_int(j, i); -} +static int coud_flipped[NMOVES][8] = { + [x] = { + [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, + [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 + } +}; -void init_coud_transition_table() { - for (int i = 0; i < pow3to7; i++) - for (int j = 0; j < 19; j++ ) - coud_transition_table[i][j] = apply_move_coud_int(j, i); -} +static int corl_flipped[NMOVES][8] = { + [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + } +}; -void init_cofb_transition_table() { - for (int i = 0; i < pow3to7; i++) - for (int j = 0; j < 19; j++ ) - cofb_transition_table[i][j] = apply_move_cofb_int(j, i); -} +static int cofb_flipped[NMOVES][8] = { + [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + }, + [y] = { + [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1, + [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2 + } +}; -void init_corl_transition_table() { - for (int i = 0; i < pow3to7; i++) - for (int j = 0; j < 19; j++ ) - corl_transition_table[i][j] = apply_move_corl_int(j, i); -} +static char equiv_alg_string[100][NMOVES] = { + [NULLMOVE] = "", + + [U] = " U ", + [U2] = " UU ", + [U3] = " UUU ", + [D] = " xx U xx ", + [D2] = " xx UU xx ", + [D3] = " xx UUU xx ", + [R] = " yx U xxxyyy ", + [R2] = " yx UU xxxyyy ", + [R3] = " yx UUU xxxyyy ", + [L] = " yyyx U xxxy ", + [L2] = " yyyx UU xxxy ", + [L3] = " yyyx UUU xxxy ", + [F] = " x U xxx ", + [F2] = " x UU xxx ", + [F3] = " x UUU xxx ", + [B] = " xxx U x ", + [B2] = " xxx UU x ", + [B3] = " xxx UUU x ", + + [Uw] = " xx U xx y ", + [Uw2] = " xx UU xx yy ", + [Uw3] = " xx UUU xx yyy ", + [Dw] = " U yyy ", + [Dw2] = " UU yy ", + [Dw3] = " UUU y ", + [Rw] = " yyyx U xxxy x ", + [Rw2] = " yyyx UU xxxy xx ", + [Rw3] = " yyyx UUU xxxy xxx ", + [Lw] = " yx U xxxyyy xxx ", + [Lw2] = " yx UU xxxyyy xx ", + [Lw3] = " yx UUU xxxyyy x ", + [Fw] = " xxx U x yxxxyyy ", + [Fw2] = " xxx UU x yxxyyy ", + [Fw3] = " xxx UUU x yxyyy ", + [Bw] = " x U xxx yxyyy ", + [Bw2] = " x UU xxx yxxyyy ", + [Bw3] = " x UUU xxx yxxxyyy ", + + [M] = " yx U xx UUU yxyyy ", + [M2] = " yx UU xx UU xxxy ", + [M3] = " yx UUU xx U yxxxy ", + [S] = " x UUU xx U yyyx ", + [S2] = " x UU xx UU yyx ", + [S3] = " x U xx UUU yx ", + [E] = " U xx UUU xxyyy ", + [E2] = " UU xx UU xxyy ", + [E3] = " UUU xx U xxy ", + + [x] = " x ", + [x2] = " xx ", + [x3] = " xxx ", + [y] = " y ", + [y2] = " yy ", + [y3] = " yyy ", + [z] = " yyy x y ", + [z2] = " yy xx ", + [z3] = " y x yyy " +}; -void init_transition_table() { - init_epud_transition_table(); - init_eprl_transition_table(); - init_epfb_transition_table(); - init_epose_transition_table(); - init_eposs_transition_table(); - init_eposm_transition_table(); - init_epe_transition_table(); - init_eps_transition_table(); - init_epm_transition_table(); - init_emslices_transition_table(); - init_cp_transition_table(); - init_eofb_transition_table(); - init_eorl_transition_table(); - init_eoud_transition_table(); - init_coud_transition_table(); - init_cofb_transition_table(); - init_corl_transition_table(); +/* Transition tables, to be loaded up at the beginning */ +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + + +/* Local functions implementation ********************************************/ + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + /*init_moves();*/ + + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +/* Public functions **********************************************************/ + +Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + /*init_moves();*/ + + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + +static bool +read_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +bool +commute(Move m1, Move m2) +{ + static bool initialized = false; + static bool commute_aux[NMOVES][NMOVES]; + + if (!initialized) { + Cube c1, c2; + int i, j; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute_aux[i][j] = equal(c1, c2) && i && j; + } + } + + initialized = true; + } + + return commute_aux[m1][m2]; +} + +bool +possible_next(Move m1, Move m2, Move m3) +{ + static bool initialized = false; + static bool paux[NMOVES][NMOVES][NMOVES]; + + if (!initialized) { + int i, j, k; + bool p, q, c; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p = j && base_move(j) == base_move(k); + q = i && base_move(i) == base_move(k); + c = commute(i, j); + paux[i][j][k] = !(p || (c && q)); + } + } + } + + initialized = true; + } + + return paux[m1][m2][m3]; +} + +void +init_moves() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + Alg *equiv_alg[NMOVES]; + + for (i = 0; i < NMOVES; i++) + equiv_alg[i] = new_alg(equiv_alg_string[i]); + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); + + for (i = 0; i < NMOVES; i++) + free_alg(equiv_alg[i]); } diff --git a/src/moves.h b/src/moves.h @@ -1,83 +1,16 @@ -#include "utils.h" +#ifndef MOVES_H +#define MOVES_H -/* Bitmask that define certain movesets. */ -#define move_mask_all 524287 /* Reverse 1111111111111111111 */ -#define move_mask_eofb 155647 /* Reverse 1111111111111010010 */ -#define move_mask_eorl 518527 /* Reverse 1111111010101111111 */ -#define move_mask_eoud 524197 /* Reverse 1010010111111111111 */ -#define move_mask_drud 149887 /* Reverse 1111111010010010010 */ -#define move_mask_drfb 518437 /* Reverse 1010010010010111111 */ -#define move_mask_drrl 155557 /* Reverse 1010010111111010010 */ -#define move_mask_htr 149797 /* Reverse 1010010010010010010 */ +#include "alg.h" +#include "cube.h" +#include "env.h" -extern int possible_next[19][19]; +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +Cube apply_move(Move m, Cube cube); +bool commute(Move m1, Move m2); +bool possible_next(Move m1, Move m2, Move m3); -int parallel(int m1, int m2); -void init_possible_next(); - -/* Transition tables */ -extern int eofb_transition_table[pow2to11][19]; -extern int eorl_transition_table[pow2to11][19]; -extern int eoud_transition_table[pow2to11][19]; -extern int coud_transition_table[pow3to7][19]; -extern int cofb_transition_table[pow3to7][19]; -extern int corl_transition_table[pow3to7][19]; -extern int epud_transition_table[factorial8][19]; -extern int epfb_transition_table[factorial8][19]; -extern int eprl_transition_table[factorial8][19]; -extern int epose_transition_table[binom12on4][19]; -extern int eposs_transition_table[binom12on4][19]; -extern int eposm_transition_table[binom12on4][19]; -extern int epe_transition_table[factorial4][19]; -extern int eps_transition_table[factorial4][19]; -extern int epm_transition_table[factorial4][19]; -extern int emslices_transition_table[binom12on4*binom8on4][19]; -extern int cp_transition_table[factorial8][19]; - - -/* Functions for permuting pieces (given in array format) */ - -void apply_move_ep_array(int move, int ep[12]); -void apply_move_cp_array(int move, int cp[8]); - -/* Functions for permuting pieces (given in integer format) */ - -int apply_move_ep_int(int move, int ep); -int apply_move_epud_int(int move, int ep); -int apply_move_epfb_int(int move, int ep); -int apply_move_eprl_int(int move, int ep); -int apply_move_epose_int(int move, int ep); -int apply_move_eposs_int(int move, int ep); -int apply_move_eposm_int(int move, int ep); -int apply_move_epe_int(int move, int ep); -int apply_move_eps_int(int move, int ep); -int apply_move_epm_int(int move, int ep); -int apply_move_cp_int(int move, int cp); -int apply_move_eofb_int(int move, int eo); -int apply_move_eorl_int(int move, int eo); -int apply_move_eoud_int(int move, int eo); -int apply_move_coud_int(int move, int co); -int apply_move_cofb_int(int move, int co); -int apply_move_corl_int(int move, int co); - -/* Initialize transition tables */ - -void init_epud_transition_table(); -void init_epfb_transition_table(); -void init_eprl_transition_table(); -void init_epose_transition_table(); -void init_eposs_transition_table(); -void init_eposm_transition_table(); -void init_epe_transition_table(); -void init_eps_transition_table(); -void init_epm_transition_table(); -void init_cp_transition_table(); -void init_eofb_transition_table(); -void init_eorl_transition_table(); -void init_eoud_transition_table(); -void init_coud_transition_table(); -void init_cofb_transition_table(); -void init_corl_transition_table(); - -void init_transition_table(); +void init_moves(); +#endif diff --git a/src/pf.c b/src/pf.c @@ -0,0 +1,80 @@ +#include "pf.h" + +PieceFilter +pf_all = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .eorl = true, + .eoud = true, + .cp = true, + .cofb = true, + .corl = true, + .coud = true, + .cpos = true +}; + +PieceFilter +pf_4val = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .coud = true, + .cp = true +}; + +PieceFilter +pf_epcp = { + .epose = true, + .eposs = true, + .eposm = true, + .cp = true +}; + +PieceFilter +pf_cpos = { + .cpos = true +}; + +PieceFilter +pf_cp = { + .cp = true +}; + +PieceFilter +pf_ep = { + .epose = true, + .eposs = true, + .eposm = true +}; + +PieceFilter +pf_e = { + .epose = true +}; + +PieceFilter +pf_s = { + .eposs = true +}; + +PieceFilter +pf_m = { + .eposm = true +}; + +PieceFilter +pf_eo = { + .eofb = true, + .eorl = true, + .eoud = true +}; + +PieceFilter +pf_co = { + .cofb = true, + .corl = true, + .coud = true +}; diff --git a/src/pf.h b/src/pf.h @@ -0,0 +1,18 @@ +#ifndef PF_H +#define PF_H + +#include "cubetypes.h" + +extern PieceFilter pf_all; +extern PieceFilter pf_4val; +extern PieceFilter pf_epcp; +extern PieceFilter pf_cpos; +extern PieceFilter pf_cp; +extern PieceFilter pf_ep; +extern PieceFilter pf_e; +extern PieceFilter pf_s; +extern PieceFilter pf_m; +extern PieceFilter pf_eo; +extern PieceFilter pf_co; + +#endif diff --git a/src/pruning.c b/src/pruning.c @@ -0,0 +1,271 @@ +#include "pruning.h" + +static void genptable_bfs(PruneData *pd, int d, Move *ms); +static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void ptable_update_index(PruneData *pd, uint64_t ind, int m); +static int ptableval_index(PruneData *pd, uint64_t ind); +static bool read_ptable_file(PruneData *pd); +static bool write_ptable_file(PruneData *pd); + +PruneData +pd_eofb_HTM = { + .filename = "pt_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM, +}; + +PruneData +pd_coud_HTM = { + .filename = "pt_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM, +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "pt_cornershtr_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM, +}; + +PruneData +pd_corners_HTM = { + .filename = "pt_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM, +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "pt_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, +}; + +PruneData +pd_drud_eofb = { + .filename = "pt_drud_eofb", + .coord = &coord_drud_eofb, + .moveset = moveset_eofb, +}; + +PruneData +pd_drudfin_noE_sym16_drud = { + .filename = "pt_drudfin_noE_sym16_drud", + .coord = &coord_drudfin_noE_sym16, + .moveset = moveset_drud, +}; + +PruneData +pd_htr_drud = { + .filename = "pt_htr_drud", + .coord = &coord_htr_drud, + .moveset = moveset_drud, +}; + +PruneData +pd_htrfin_htr = { + .filename = "pt_htrfin_htr", + .coord = &coord_htrfin, + .moveset = moveset_htr, +}; + +PruneData +pd_khuge_HTM = { + .filename = "pt_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM, +}; + +void +genptable(PruneData *pd) +{ + Move ms[NMOVES]; + int d; + uint64_t j, oldn; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe exit gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + pd->generated = true; + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + moveset_to_list(pd->moveset, ms); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update_index(pd, j, 15); + + for (j = 0; j < pd->coord->max; j++) + if (ptableval_index(pd, j) != 15) { + printf("Error, non-max value at index %lu!\n", j); + break; + } + printf("Table set, ready to start\n"); + + ptable_update(pd, (Cube){0}, 0); + pd->n = 1; + oldn = 0; + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + 0, pd->n - oldn, pd->n, pd->coord->max); + oldn = 1; + for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { + genptable_bfs(pd, d, ms); + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + d+1, pd->n - oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); +} + +static void +genptable_bfs(PruneData *pd, int d, Move *ms) +{ + uint64_t i; + + for (i = 0; i < pd->coord->max; i++) + if (ptableval_index(pd, i) == d) + genptable_branch(pd, i, d, ms); +} + +static void +genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms) +{ + int i, j; + Cube ci, cc, c; + + /* + * This is the only line of the whole program where we REALLY need an + * anti-indexer function. We could get rid of it if only we could save + * a cube object for each index value as we go, but then we would need + * an incredible amount of memory to generate each ptable: assuming + * fields in struct cube are 32 bit ints that would take 88 times the + * memory of the table to be generated, more than 120Gb for + * ptable_khuge for example! + */ + ci = pd->coord->cube(ind); + + for (i = 0; i < pd->coord->ntrans; i++) { + /* For simplicity trans[] is NULL when ntrans = 1 */ + c = i == 0 ? ci : + apply_trans(pd->coord->trans[i], ci); + for (j = 0; ms[j] != NULLMOVE; j++) { + cc = apply_move(ms[j], c); + if (ptableval(pd, cc) > d+1) + ptable_update(pd, cc, d+1); + } + } +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval_index(pd, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + ptable_update_index(pd, ind, n); +} + +static void +ptable_update_index(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + return ptableval_index(pd, pd->coord->index(cube)); +} + +static int +ptableval_index(PruneData *pd, uint64_t ind) +{ + if (!pd->generated) + genptable(pd); + + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + +static bool +read_ptable_file(PruneData *pd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +write_ptable_file(PruneData *pd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + diff --git a/src/pruning.h b/src/pruning.h @@ -0,0 +1,23 @@ +#ifndef PRUNING_H +#define PRUNING_H + +#include "symcoord.h" + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_drud_sym16_HTM; +extern PruneData pd_drud_eofb; +extern PruneData pd_drudfin_noE_sym16_drud; +extern PruneData pd_htr_drud; +extern PruneData pd_htrfin_htr; +extern PruneData pd_khuge_HTM; + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +#endif + diff --git a/src/pruning_tables.c b/src/pruning_tables.c @@ -1,483 +0,0 @@ -#include <stdint.h> -#include "pruning_tables.h" -#include "moves.h" - -/* The data contained in e.g. eofb pruning table is the same that is contained - * in eolr pruning table and so on. For small tables the memory wasted is not - * too much and it makes things easier. I may change this when I implement - * bigger tables. */ -int eofb_pruning_table[pow2to11]; -int eorl_pruning_table[pow2to11]; -int eoud_pruning_table[pow2to11]; -int coud_pruning_table[pow3to7]; -int cofb_pruning_table[pow3to7]; -int corl_pruning_table[pow3to7]; -int cp_pruning_table[factorial8]; - -int eorl_from_eofb_pruning_table[pow2to11]; -int eoud_from_eofb_pruning_table[pow2to11]; -int eoud_from_eorl_pruning_table[pow2to11]; -int eofb_from_eorl_pruning_table[pow2to11]; -int eofb_from_eoud_pruning_table[pow2to11]; -int eorl_from_eoud_pruning_table[pow2to11]; - -int coud_from_eofb_pruning_table[pow3to7]; -int coud_from_eorl_pruning_table[pow3to7]; -int cofb_from_eorl_pruning_table[pow3to7]; -int cofb_from_eoud_pruning_table[pow3to7]; -int corl_from_eoud_pruning_table[pow3to7]; -int corl_from_eofb_pruning_table[pow3to7]; - -int cp_drud_pruning_table[factorial8]; -int cp_drfb_pruning_table[factorial8]; -int cp_drrl_pruning_table[factorial8]; -int epud_pruning_table[factorial8]; -int epfb_pruning_table[factorial8]; -int eprl_pruning_table[factorial8]; - -int cp_htr_pruning_table[factorial8]; - -int cpud_to_htr_pruning_table[factorial8]; -int cpfb_to_htr_pruning_table[factorial8]; -int cprl_to_htr_pruning_table[factorial8]; - - -/* About 1Mb each */ -int8_t eofb_epose_pruning_table[pow2to11][binom12on4]; -int8_t eorl_eposs_pruning_table[pow2to11][binom12on4]; -int8_t eoud_eposm_pruning_table[pow2to11][binom12on4]; - -/* About 4.5Mb each */ -int8_t eofb_coud_pruning_table[pow2to11][pow3to7]; -int8_t eofb_corl_pruning_table[pow2to11][pow3to7]; -int8_t eorl_coud_pruning_table[pow2to11][pow3to7]; -int8_t eorl_cofb_pruning_table[pow2to11][pow3to7]; -int8_t eoud_cofb_pruning_table[pow2to11][pow3to7]; -int8_t eoud_corl_pruning_table[pow2to11][pow3to7]; - -/* About 1Mb each */ -int8_t coud_epose_from_eofb_pruning_table[pow3to7][binom12on4]; -int8_t cofb_eposs_from_eorl_pruning_table[pow3to7][binom12on4]; -int8_t corl_eposm_from_eoud_pruning_table[pow3to7][binom12on4]; -int8_t coud_epose_from_eorl_pruning_table[pow3to7][binom12on4]; -int8_t cofb_eposs_from_eoud_pruning_table[pow3to7][binom12on4]; -int8_t corl_eposm_from_eofb_pruning_table[pow3to7][binom12on4]; - - -/* Firs one is 88Mb, second one is 71Mb */ -int8_t cp_co_pruning_table[factorial8][pow3to7]; -int8_t triple_eo_pruning_table[pow2to11][binom12on4*binom8on4]; - -int initialized_small = 0; -int initialized_directdr = 0; -int initialized_drfromeo = 0; -int initialized_huge = 0; - -void init_single_table(int n, int t_tab[][19], int p_tab[n], int mask) { - int state[n]; - state[0] = 0; /* 0 should always be the solved state. */ - p_tab[0] = 0; - int state_count = 1; - for (int i = 0; i < state_count; i++) { - for (int m = 1; m < 19; m++) { - int next = t_tab[state[i]][m]; - if (mask & (1<<m) && !p_tab[next] && next) { - p_tab[next] = p_tab[state[i]] + 1; - state[state_count++] = next; - } - } - } -} - -/* Similar to single table, but specific to "cp to htr". - * The idea is that we are considering the distance not necessarily to the - * solved state, but to any state that is either solved or reachable from - * cp_pruning_table. */ -void init_cptohtr_table(int n, int t_tab[][19], int p_tab[n], int mask) { - - for (int i = 0; i < n; i++) - p_tab[i] = 21; - - /* List of htr states */ - int good[n]; good[0] = 0; - int good_count = 1; - for (int i = 0; i < n; i++) - if (cp_htr_pruning_table[i]) - good[good_count++] = i; - - /* Init pruning table starting from each possible state */ - int state[n]; - for (int j = 0; j < good_count; j++) { - state[0] = good[j]; - p_tab[state[0]] = 0; - int state_count = 1; - for (int i = 0; i < state_count; i++) { - for (int m = 1; m < 19; m++) { - int next = t_tab[state[i]][m]; - if (mask & (1<<m) && (p_tab[next] > p_tab[state[i]] + 1) && next) { - p_tab[next] = p_tab[state[i]] + 1; - state[state_count++] = next; - } - } - } - } -} - -void init_double_table(int n1, int n2, - int t_table1[n1][19], int t_table2[n2][19], - int8_t p_table[n1][n2], int mask) { - static int state1[factorial8*pow3to7], state2[factorial8*pow3to7]; - state1[0] = 0; - state2[0] = 0; - p_table[0][0] = 0; - int state_count = 1; - for (int i = 0; i < state_count; i++) { - for (int m = 1; m < 19; m++) { - int next1 = t_table1[state1[i]][m]; - int next2 = t_table2[state2[i]][m]; - if (mask & (1<<m) && !p_table[next1][next2] && (next1 || next2)) { - p_table[next1][next2] = p_table[state1[i]][state2[i]] + 1; - state1[state_count] = next1; - state2[state_count] = next2; - state_count++; - } - } - } -} - -void init_eofb_pruning_table() { - init_single_table(pow2to11, eofb_transition_table, eofb_pruning_table, - move_mask_all); -} - -void init_eorl_pruning_table() { - init_single_table(pow2to11, eorl_transition_table, eorl_pruning_table, - move_mask_all); -} - -void init_eoud_pruning_table() { - init_single_table(pow2to11, eoud_transition_table, eoud_pruning_table, - move_mask_all); -} - -void init_coud_pruning_table() { - init_single_table(pow3to7, coud_transition_table, coud_pruning_table, - move_mask_all); -} - -void init_cofb_pruning_table() { - init_single_table(pow3to7, cofb_transition_table, cofb_pruning_table, - move_mask_all); -} - -void init_corl_pruning_table() { - init_single_table(pow3to7, corl_transition_table, corl_pruning_table, - move_mask_all); -} - -void init_cp_pruning_table() { - init_single_table(factorial8, cp_transition_table, cp_pruning_table, - move_mask_all); -} - -/* The following tables use the eo moveset */ -void init_eorl_from_eofb_pruning_table() { - init_single_table(pow2to11, eorl_transition_table, - eorl_from_eofb_pruning_table, move_mask_eofb); -} - -void init_eoud_from_eofb_pruning_table() { - init_single_table(pow2to11, eoud_transition_table, - eoud_from_eofb_pruning_table, move_mask_eofb); -} - -void init_eoud_from_eorl_pruning_table() { - init_single_table(pow2to11, eoud_transition_table, - eoud_from_eorl_pruning_table, move_mask_eorl); -} - -void init_eofb_from_eorl_pruning_table() { - init_single_table(pow2to11, eofb_transition_table, - eofb_from_eorl_pruning_table, move_mask_eorl); -} - -void init_eofb_from_eoud_pruning_table() { - init_single_table(pow2to11, eofb_transition_table, - eofb_from_eoud_pruning_table, move_mask_eoud); -} - -void init_eorl_from_eoud_pruning_table() { - init_single_table(pow2to11, eorl_transition_table, - eorl_from_eoud_pruning_table, move_mask_eoud); -} - -void init_coud_from_eofb_pruning_table() { - init_single_table(pow3to7, coud_transition_table, - coud_from_eofb_pruning_table, move_mask_eofb); -} - -void init_corl_from_eofb_pruning_table() { - init_single_table(pow3to7, corl_transition_table, - corl_from_eofb_pruning_table, move_mask_eofb); -} - -void init_coud_from_eorl_pruning_table() { - init_single_table(pow3to7, coud_transition_table, - coud_from_eorl_pruning_table, move_mask_eorl); -} - -void init_cofb_from_eorl_pruning_table() { - init_single_table(pow3to7, cofb_transition_table, - cofb_from_eorl_pruning_table, move_mask_eorl); -} - -void init_corl_from_eoud_pruning_table() { - init_single_table(pow3to7, corl_transition_table, - corl_from_eoud_pruning_table, move_mask_eoud); -} - -void init_cofb_from_eoud_pruning_table() { - init_single_table(pow3to7, cofb_transition_table, - cofb_from_eoud_pruning_table, move_mask_eoud); -} - -/* The following tables always use DR moveset */ -void init_epud_pruning_table() { - init_single_table(factorial8, epud_transition_table, epud_pruning_table, - move_mask_drud); -} - -void init_epfb_pruning_table() { - init_single_table(factorial8, epfb_transition_table, epfb_pruning_table, - move_mask_drfb); -} - -void init_eprl_pruning_table() { - init_single_table(factorial8, eprl_transition_table, eprl_pruning_table, - move_mask_drrl); -} - -void init_cp_drud_pruning_table() { - init_single_table(factorial8, cp_transition_table, cp_drud_pruning_table, - move_mask_drud); -} - -void init_cp_drfb_pruning_table() { - init_single_table(factorial8, cp_transition_table, cp_drfb_pruning_table, - move_mask_drfb); -} - -void init_cp_drrl_pruning_table() { - init_single_table(factorial8, cp_transition_table, cp_drrl_pruning_table, - move_mask_drrl); -} - -void init_cp_htr_table() { - init_single_table(factorial8, cp_transition_table, cp_htr_pruning_table, - move_mask_htr); -} - -void init_cpud_to_htr_table() { - init_cptohtr_table(factorial8, cp_transition_table, - cpud_to_htr_pruning_table, move_mask_drud); -} - -void init_cpfb_to_htr_table() { - init_cptohtr_table(factorial8, cp_transition_table, - cpfb_to_htr_pruning_table, move_mask_drfb); -} - -void init_cprl_to_htr_table() { - init_cptohtr_table(factorial8, cp_transition_table, - cprl_to_htr_pruning_table, move_mask_drrl); -} - - -void init_eofb_epose_pruning_table() { - init_double_table(pow2to11, binom12on4, - eofb_transition_table, epose_transition_table, - eofb_epose_pruning_table, move_mask_all); -} - -void init_eorl_eposs_pruning_table() { - init_double_table(pow2to11, binom12on4, - eorl_transition_table, eposs_transition_table, - eorl_eposs_pruning_table, move_mask_all); -} - -void init_eoud_eposm_pruning_table() { - init_double_table(pow2to11, binom12on4, - eoud_transition_table, eposm_transition_table, - eoud_eposm_pruning_table, move_mask_all); -} - - - -void init_eofb_coud_pruning_table() { - init_double_table(pow2to11, pow3to7, - eofb_transition_table, coud_transition_table, - eofb_coud_pruning_table, move_mask_all); -} - -void init_eofb_corl_pruning_table() { - init_double_table(pow2to11, pow3to7, - eofb_transition_table, corl_transition_table, - eofb_corl_pruning_table, move_mask_all); -} - -void init_eorl_coud_pruning_table() { - init_double_table(pow2to11, pow3to7, - eorl_transition_table, coud_transition_table, - eorl_coud_pruning_table, move_mask_all); -} - -void init_eorl_cofb_pruning_table() { - init_double_table(pow2to11, pow3to7, - eorl_transition_table, cofb_transition_table, - eorl_cofb_pruning_table, move_mask_all); -} - -void init_eoud_corl_pruning_table() { - init_double_table(pow2to11, pow3to7, - eoud_transition_table, corl_transition_table, - eoud_corl_pruning_table, move_mask_all); -} - -void init_eoud_cofb_pruning_table() { - init_double_table(pow2to11, pow3to7, - eoud_transition_table, cofb_transition_table, - eoud_cofb_pruning_table, move_mask_all); -} - -void init_coud_epose_from_eofb_pruning_table() { - init_double_table(pow3to7, binom12on4, - coud_transition_table, epose_transition_table, - coud_epose_from_eofb_pruning_table, move_mask_eofb); -} - -void init_cofb_eposs_from_eorl_pruning_table() { - init_double_table(pow3to7, binom12on4, - cofb_transition_table, eposs_transition_table, - cofb_eposs_from_eorl_pruning_table, move_mask_eorl); -} - -void init_corl_eposm_from_eoud_pruning_table() { - init_double_table(pow3to7, binom12on4, - corl_transition_table, eposm_transition_table, - corl_eposm_from_eoud_pruning_table, move_mask_eoud); -} - -void init_coud_epose_from_eorl_pruning_table() { - init_double_table(pow3to7, binom12on4, - coud_transition_table, epose_transition_table, - coud_epose_from_eorl_pruning_table, move_mask_eorl); -} - -void init_cofb_eposs_from_eoud_pruning_table() { - init_double_table(pow3to7, binom12on4, - cofb_transition_table, eposs_transition_table, - cofb_eposs_from_eoud_pruning_table, move_mask_eoud); -} - -void init_corl_eposm_from_eofb_pruning_table() { - init_double_table(pow3to7, binom12on4, - corl_transition_table, eposm_transition_table, - corl_eposm_from_eofb_pruning_table, move_mask_eofb); -} - -void init_cp_co_pruning_table() { - init_double_table(factorial8, pow3to7, - cp_transition_table, coud_transition_table, - cp_co_pruning_table, move_mask_all); -} - -void init_triple_eo_pruning_table() { - init_double_table(pow2to11, binom12on4*binom8on4, - eofb_transition_table, emslices_transition_table, - triple_eo_pruning_table, move_mask_all); -} - - -void init_small_pruning_tables() { - if (initialized_small) - return; - - init_eofb_pruning_table(); - init_eorl_pruning_table(); - init_eoud_pruning_table(); - init_coud_pruning_table(); - init_cofb_pruning_table(); - init_corl_pruning_table(); - init_cp_pruning_table(); - - init_eorl_from_eofb_pruning_table(); - init_eoud_from_eofb_pruning_table(); - init_eoud_from_eorl_pruning_table(); - init_eofb_from_eorl_pruning_table(); - init_eofb_from_eoud_pruning_table(); - init_eorl_from_eoud_pruning_table(); - - init_coud_from_eofb_pruning_table(); - init_corl_from_eofb_pruning_table(); - init_coud_from_eorl_pruning_table(); - init_cofb_from_eorl_pruning_table(); - init_cofb_from_eoud_pruning_table(); - init_corl_from_eoud_pruning_table(); - - init_epud_pruning_table(); - init_epfb_pruning_table(); - init_eprl_pruning_table(); - init_cp_drud_pruning_table(); - init_cp_drfb_pruning_table(); - init_cp_drrl_pruning_table(); - - init_cp_htr_table(); - init_cpud_to_htr_table(); - init_cpfb_to_htr_table(); - init_cprl_to_htr_table(); - - initialized_small = 1; -} - -void init_directdr_pruning_tables() { - if (initialized_directdr) - return; - - init_eofb_epose_pruning_table(); - init_eorl_eposs_pruning_table(); - init_eoud_eposm_pruning_table(); - - init_eofb_coud_pruning_table(); - init_eofb_corl_pruning_table(); - init_eorl_coud_pruning_table(); - init_eorl_cofb_pruning_table(); - init_eoud_cofb_pruning_table(); - init_eoud_corl_pruning_table(); - - initialized_directdr = 1; -} - -void init_drfromeo_pruning_tables() { - if (initialized_drfromeo) - return; - - init_coud_epose_from_eofb_pruning_table(); - init_cofb_eposs_from_eorl_pruning_table(); - init_corl_eposm_from_eoud_pruning_table(); - init_coud_epose_from_eorl_pruning_table(); - init_cofb_eposs_from_eoud_pruning_table(); - init_corl_eposm_from_eofb_pruning_table(); - - initialized_drfromeo = 1; -} - -void init_huge_pruning_tables() { - if (initialized_huge) - return; - - init_cp_co_pruning_table(); - init_triple_eo_pruning_table(); - - initialized_huge = 1; -} - diff --git a/src/pruning_tables.h b/src/pruning_tables.h @@ -1,66 +0,0 @@ -#include <stdint.h> -#include "utils.h" - -extern int eofb_pruning_table[pow2to11]; -extern int eorl_pruning_table[pow2to11]; -extern int eoud_pruning_table[pow2to11]; -extern int coud_pruning_table[pow3to7]; -extern int cofb_pruning_table[pow3to7]; -extern int corl_pruning_table[pow3to7]; -extern int cp_pruning_table[factorial8]; - -extern int eorl_from_eofb_pruning_table[pow2to11]; -extern int eoud_from_eofb_pruning_table[pow2to11]; -extern int eoud_from_eorl_pruning_table[pow2to11]; -extern int eofb_from_eorl_pruning_table[pow2to11]; -extern int eofb_from_eoud_pruning_table[pow2to11]; -extern int eorl_from_eoud_pruning_table[pow2to11]; - -extern int coud_from_eofb_pruning_table[pow3to7]; -extern int coud_from_eorl_pruning_table[pow3to7]; -extern int cofb_from_eorl_pruning_table[pow3to7]; -extern int cofb_from_eoud_pruning_table[pow3to7]; -extern int corl_from_eoud_pruning_table[pow3to7]; -extern int corl_from_eofb_pruning_table[pow3to7]; - -extern int cp_drud_pruning_table[factorial8]; -extern int cp_drfb_pruning_table[factorial8]; -extern int cp_drrl_pruning_table[factorial8]; -extern int epud_pruning_table[factorial8]; -extern int epfb_pruning_table[factorial8]; -extern int eprl_pruning_table[factorial8]; - -extern int cp_htr_pruning_table[factorial8]; -extern int cpud_to_htr_pruning_table[factorial8]; -extern int cpfb_to_htr_pruning_table[factorial8]; -extern int cprl_to_htr_pruning_table[factorial8]; - -/* About 1Mb each */ -extern int8_t eofb_epose_pruning_table[pow2to11][binom12on4]; -extern int8_t eorl_eposs_pruning_table[pow2to11][binom12on4]; -extern int8_t eoud_eposm_pruning_table[pow2to11][binom12on4]; - -/* About 4.5Mb each */ -extern int8_t eofb_coud_pruning_table[pow2to11][pow3to7]; -extern int8_t eofb_corl_pruning_table[pow2to11][pow3to7]; -extern int8_t eorl_coud_pruning_table[pow2to11][pow3to7]; -extern int8_t eorl_cofb_pruning_table[pow2to11][pow3to7]; -extern int8_t eoud_cofb_pruning_table[pow2to11][pow3to7]; -extern int8_t eoud_corl_pruning_table[pow2to11][pow3to7]; - -/* About 1Mb each */ -extern int8_t coud_epose_from_eofb_pruning_table[pow3to7][binom12on4]; -extern int8_t cofb_eposs_from_eorl_pruning_table[pow3to7][binom12on4]; -extern int8_t corl_eposm_from_eoud_pruning_table[pow3to7][binom12on4]; -extern int8_t coud_epose_from_eorl_pruning_table[pow3to7][binom12on4]; -extern int8_t cofb_eposs_from_eoud_pruning_table[pow3to7][binom12on4]; -extern int8_t corl_eposm_from_eofb_pruning_table[pow3to7][binom12on4]; - -/* First one 88Mb, second one 21Mb */ -extern int8_t cp_co_pruning_table[factorial8][pow3to7]; -extern int8_t triple_eo_pruning_table[pow2to11][binom12on4*binom8on4]; - -void init_small_pruning_tables(); -void init_directdr_pruning_tables(); -void init_drfromeo_pruning_tables(); -void init_huge_pruning_tables(); diff --git a/src/shell.c b/src/shell.c @@ -0,0 +1,93 @@ +#include "shell.h" + +static void cleanwhitespaces(char *line); +static int parseline(char *line, char **v); + +static void +cleanwhitespaces(char *line) +{ + char *i; + + for (i = line; *i != 0; i++) + if (*i == '\t' || *i == '\n') + *i = ' '; +} + +/* This function assumes that **v is large enough */ +static int +parseline(char *line, char **v) +{ + char *t; + int n = 0; + + cleanwhitespaces(line); + + for (t = strtok(line, " "); t != NULL; t = strtok(NULL, " ")) + strcpy(v[n++], t); + + return n; +} + +void +exec_args(int c, char **v) +{ + int i; + Command *cmd = NULL; + CommandArgs *args; + + for (i = 0; i < NCOMMANDS; i++) + if (commands[i] != NULL && !strcmp(v[0], commands[i]->name)) + cmd = commands[i]; + + if (cmd == NULL) { + fprintf(stderr, "%s: command not found\n", v[0]); + return; + } + + args = cmd->parse_args(c-1, &v[1]); + if (!args->success) { + fprintf(stderr, "usage: %s\n", cmd->usage); + return; + } + + cmd->exec(args); + free_args(args); +} + +void +launch() +{ + int i, shell_argc; + char line[MAXLINELEN], **shell_argv; + + shell_argv = malloc(MAXNTOKENS * sizeof(char *)); + for (i = 0; i < MAXNTOKENS; i++) + shell_argv[i] = malloc((MAXTOKENLEN+1) * sizeof(char)); + + fprintf(stderr, "Welcome to Nissy "VERSION".\n"); + fprintf(stderr, "Type 'help' for a list.\n"); + + while (true) { + fprintf(stderr, "nissy-# "); + if (fgets(line, MAXLINELEN, stdin) == NULL) + break; + shell_argc = parseline(line, shell_argv); + if (shell_argc > 0) + exec_args(shell_argc, shell_argv); + } + + for (i = 0; i < MAXNTOKENS; i++) + free(shell_argv[i]); + free(shell_argv); +} + +int +main(int argc, char *argv[]) +{ + if (argc > 1) + exec_args(argc-1, &argv[1]); + else + launch(); + + return 0; +} diff --git a/src/shell.h b/src/shell.h @@ -0,0 +1,13 @@ +#ifndef SHELL_H +#define SHELL_H + +#include "commands.h" + +#define MAXLINELEN 10000 +#define MAXTOKENLEN 255 +#define MAXNTOKENS 255 + +void exec_args(int c, char **v); +void launch(); + +#endif diff --git a/src/solve.c b/src/solve.c @@ -0,0 +1,173 @@ +#include "solve.h" + +/* Local functions ***********************************************************/ + +static bool allowed_next(Move move, DfsData *dd); +static void dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step *s, SolveOptions *os, DfsData *dd); +static bool dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd); + +/* Local functions ***********************************************************/ + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next(dd->last2, dd->last1, move)) + return false; + + if (commute(dd->last1, move)) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(s, opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + if (s->is_valid(dd->current_alg) || opts->all) + append_alg(dd->sols, dd->current_alg); + + if (opts->verbose) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s->estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s->estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +/* Public functions **********************************************************/ + +AlgList * +solve(Cube cube, Step *step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c; + + if (step->detect != NULL) + step->pre_trans = step->detect(cube); + c = apply_trans(step->pre_trans, cube); + + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step->ready != NULL && !step->ready(c)) { + fprintf(stderr, "Cube not ready for solving step: "); + fprintf(stderr, "%s\n", step->ready_msg); + return sols; + } + + moveset_to_list(step->moveset, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && + !(sols->len && opts->optimal_only) && + sols->len < opts->max_solutions; + dd.d++) { + if (opts->verbose) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(c, step, opts, &dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(step->pre_trans), node->alg); + + free_alg(dd.current_alg); + return sols; +} diff --git a/src/solve.h b/src/solve.h @@ -0,0 +1,9 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include "moves.h" +#include "trans.h" + +AlgList * solve(Cube cube, Step *step, SolveOptions *opts); + +#endif diff --git a/src/solver.c b/src/solver.c @@ -1,1058 +0,0 @@ -#include <stdint.h> -#include <stdio.h> - -#include "utils.h" -#include "coordinates.h" -#include "moves.h" -#include "io.h" -#include "pruning_tables.h" - -/* Applies inverse of moves, inverse of prev_moves and then inverse of scramble - * and returns a coordinate determined by t_table. */ -int premoves_inverse(int moves[30], int scramble[], int prev_moves[30], - int t_table[][19]) { - int nprevmoves, nmoves, nscramble, coord = 0; - - for (nmoves = 0; moves[nmoves]; nmoves++); - for (nprevmoves = 0; prev_moves[nprevmoves]; nprevmoves++); - for (nscramble = 0; scramble[nscramble]; nscramble++); - - for (int i = nmoves - 1; i >= 0; i--) - coord = t_table[coord][inverse_move[moves[i]]]; - for (int i = nprevmoves - 1; i >= 0; i--) - coord = t_table[coord][inverse_move[prev_moves[i]]]; - for (int i = nscramble - 1; i >= 0; i--) - coord = t_table[coord][inverse_move[scramble[i]]]; - - return coord; -} - - -/******/ -/* EO */ -/******/ -void niss_eo_dfs(int eo, int scramble[], int eo_list[][30], int *eo_count, - int t_table[pow2to11][19], int p_table[pow2to11], - int last1, int last2, int moves, int m, int d, int niss, - int can_use_niss, int hide) { - - - if (*eo_count >= m || moves > d || - ((!can_use_niss || niss) && moves + p_table[eo] > d)) - return; - - eo_list[*eo_count][moves] = 0; - - if (eo == 0) { - /* If an early EO is found, or if "case F2 B", or if hide is on. */ - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*eo_count < m - 1) - copy_moves(eo_list[*eo_count], eo_list[(*eo_count)+1]); - (*eo_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - eo_list[*eo_count][moves] = niss ? -i : i; - niss_eo_dfs(t_table[eo][i], scramble, eo_list, eo_count, t_table, - p_table, i, last1, moves+1, m, d, niss, - can_use_niss, hide); - } - } - - if (*eo_count >= m) - return; - eo_list[*eo_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was F/F' etc, and if I am allowed to niss, try niss! */ - if (!niss && (last1 == 0 || t_table[0][last1] != 0) && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) { - int aux[] = {0,0}; - niss_eo_dfs(premoves_inverse(eo_list[*eo_count], scramble, aux, t_table), - scramble, eo_list, eo_count, t_table, p_table, - 0, 0, moves, m, d, 1, can_use_niss, hide); - } -} - -int eo_scram_spam(int scram[], int eo_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h) { - - init_small_pruning_tables(); - - int n = 0, eofb = 0, eorl = 0, eoud = 0; - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - } - for (int i = 0; i <= b; i++) { - if (fb) - niss_eo_dfs(eofb, scram, eo_list, &n, eofb_transition_table, - eofb_pruning_table, 0, 0, 0, m, i, 0, niss, h); - if (rl) - niss_eo_dfs(eorl, scram, eo_list, &n, eorl_transition_table, - eorl_pruning_table, 0, 0, 0, m, i, 0, niss, h); - if (ud) - niss_eo_dfs(eoud, scram, eo_list, &n, eoud_transition_table, - eoud_pruning_table, 0, 0, 0, m, i, 0, niss, h); - } - return n; -} - - -/******/ -/* CO */ -/******/ -void niss_co_dfs(int co, int scramble[], int co_list[][30], int *co_count, - int t_table[pow3to7][19], int p_table[pow3to7], - int last1, int last2, int moves, int m, int d, int niss, - int can_use_niss, int hide, int ignore) { - - - if (*co_count >= m || moves > d || - ((!can_use_niss || niss) && ((!ignore && moves + p_table[co] > d) || - ( ignore && moves + p_table[co] - 2 > d)))) - return; - - co_list[*co_count][moves] = 0; - - if (co == 0 || (ignore && ( t_table[t_table[co][F]][B] == 0 || - t_table[t_table[co][R]][L] == 0 || - t_table[t_table[co][U]][D] == 0 ))) { - /* If an early CO is found, or if "case F2 B", or if hide is on. */ - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*co_count < m - 1) - copy_moves(co_list[*co_count], co_list[(*co_count)+1]); - (*co_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - co_list[*co_count][moves] = niss ? -i : i; - niss_co_dfs(t_table[co][i], scramble, co_list, co_count, t_table, - p_table, i, last1, moves+1, m, d, niss, - can_use_niss, hide, ignore); - } - } - - if (*co_count >= m) - return; - co_list[*co_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was F/F' etc, and if I am allowed to niss, try niss! */ - if (!niss && (last1 == 0 || t_table[0][last1] != 0) && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) { - int aux[] = {0,0}; - niss_co_dfs(premoves_inverse(co_list[*co_count], scramble, aux, t_table), - scramble, co_list, co_count, t_table, p_table, - 0, 0, moves, m, d, 1, can_use_niss, hide, ignore); - } -} - -int co_scram_spam(int scram[], int co_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h, int ignore) { - - init_small_pruning_tables(); - - int n = 0, cofb = 0, corl = 0, coud = 0; - for (int i = 0; scram[i]; i++) { - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - } - for (int i = 0; i <= b; i++) { - if (fb) - niss_co_dfs(cofb, scram, co_list, &n, cofb_transition_table, - cofb_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore); - if (rl) - niss_co_dfs(corl, scram, co_list, &n, corl_transition_table, - corl_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore); - if (ud) - niss_co_dfs(coud, scram, co_list, &n, coud_transition_table, - coud_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore); - } - return n; -} - - -/**************/ -/* DR from EO */ -/**************/ - - -/* Scramble includes premoves for previous EO */ -void niss_dr_from_eo_dfs(int co, int epos, int scramble[], int eo_moves[30], - int dr_list[][30], int *dr_count, - int co_t_table[pow3to7][19], - int epos_t_table[binom12on4][19], - int8_t p_table[pow3to7][binom12on4], int mask, - int last1, int last2, int last1_inv, int last2_inv, - int moves, int m, int d, int niss, - int can_use_niss, int hide) { - - if (*dr_count >= m || moves > d || - ((!can_use_niss || niss) && moves + p_table[co][epos] > d)) - return; - - dr_list[*dr_count][moves] = 0; - - if (co == 0 && epos == 0) { - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*dr_count < m - 1) - copy_moves(dr_list[*dr_count], dr_list[(*dr_count)+1]); - (*dr_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - dr_list[*dr_count][moves] = niss ? -i : i; - niss_dr_from_eo_dfs(co_t_table[co][i], epos_t_table[epos][i], - scramble, eo_moves, dr_list, dr_count, - co_t_table, epos_t_table, p_table, mask, - i, last1, last1_inv, last2_inv, - moves+1, m, d, niss, can_use_niss, hide); - } - } - - if (*dr_count >= m) - return; - dr_list[*dr_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was F/F' etc and I am allowed to niss, try niss! */ - if (!niss && (last1 == 0 || co_t_table[0][last1] != 0) && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - niss_dr_from_eo_dfs(premoves_inverse(dr_list[*dr_count], scramble, - eo_moves, co_t_table), - premoves_inverse(dr_list[*dr_count], scramble, - eo_moves, epos_t_table), - scramble, eo_moves, dr_list, dr_count, - co_t_table, epos_t_table, p_table, - mask, last1_inv, last2_inv, 0, 0, - moves, m, d, 1, can_use_niss, hide); -} - -int drfrom_scram_spam(int scram[], int dr_list[][30], int from, int fb, - int rl, int ud, int m, int b, int niss, int hide) { - - init_drfromeo_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int epose = 0, eposm = 0, eposs = 0; - int coud = 0, corl = 0, cofb = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - epose = epose_transition_table[epose][scram[i]]; - eposm = eposm_transition_table[eposm][scram[i]]; - eposs = eposs_transition_table[eposs][scram[i]]; - } - - int fake_eom[2] = {0, 0}; /* Fake EO moves */ - - if (from == 1) { - if (eofb) - return -1; - for (int i = 0; i <= b; i++) { - if (ud) - niss_dr_from_eo_dfs(coud, epose, scram, fake_eom, dr_list, &n, - coud_transition_table, epose_transition_table, - coud_epose_from_eofb_pruning_table, move_mask_eofb, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - if (rl) - niss_dr_from_eo_dfs(corl, eposm, scram, fake_eom, dr_list, &n, - corl_transition_table, eposm_transition_table, - corl_eposm_from_eofb_pruning_table, move_mask_eofb, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if (from == 2) { - if (eorl) - return -1; - for (int i = 0; i <= b; i++) { - if (fb) - niss_dr_from_eo_dfs(cofb, eposs, scram, fake_eom, dr_list, &n, - cofb_transition_table, eposs_transition_table, - cofb_eposs_from_eorl_pruning_table, move_mask_eorl, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - if (ud) - niss_dr_from_eo_dfs(coud, epose, scram, fake_eom, dr_list, &n, - coud_transition_table, epose_transition_table, - coud_epose_from_eorl_pruning_table, move_mask_eorl, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if (from == 3) { - if (eoud) - return -1; - for (int i = 0; i <= b; i++) { - if (rl) - niss_dr_from_eo_dfs(corl, eposm, scram, fake_eom, dr_list, &n, - corl_transition_table, eposm_transition_table, - corl_eposm_from_eoud_pruning_table, move_mask_eoud, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - if (fb) - niss_dr_from_eo_dfs(cofb, eposs, scram, fake_eom, dr_list, &n, - cofb_transition_table, eposs_transition_table, - cofb_eposs_from_eoud_pruning_table, move_mask_eoud, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else { - return -1; - } - return n; -} - - -/***************/ -/* HTR from DR */ -/***************/ - -/* Scramble includes premoves for previous DR */ -void niss_htr_from_dr_dfs(int cp, int eo3, int scramble[], int eodr_moves[30], - int htr_list[][30], int *htr_count, - int eo3_t_table[pow2to11][19], - int cp_to_htr_pruning_table[factorial8], - int cp_htr_pruning_table[factorial8], - int cp_finish_pruning_table[factorial8], - int mask, int last1, int last2, - int last1_inv, int last2_inv, int moves, - int m, int d, int niss, - int can_use_niss, int hide) { - - if (*htr_count >= m || moves > d || - ((!can_use_niss || niss) && moves + cp_to_htr_pruning_table[cp] > d) || - moves + cp_finish_pruning_table[cp] - 4 > d) - return; - - htr_list[*htr_count][moves] = 0; - - if ((cp == 0 || cp_htr_pruning_table[cp]) && eo3 == 0) { - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*htr_count < m - 1) - copy_moves(htr_list[*htr_count], htr_list[(*htr_count)+1]); - (*htr_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - htr_list[*htr_count][moves] = niss ? -i : i; - niss_htr_from_dr_dfs(cp_transition_table[cp][i], eo3_t_table[eo3][i], - scramble, eodr_moves, htr_list, htr_count, - eo3_t_table, cp_to_htr_pruning_table, - cp_htr_pruning_table, cp_finish_pruning_table, - mask, i, last1, last1_inv, last2_inv, - moves+1, m, d, niss, can_use_niss, hide); - } - } - - if (*htr_count >= m) - return; - htr_list[*htr_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was a quarter turn and I am allowed to niss, try niss! */ - if (!niss && last1 % 3 != 2 && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - niss_htr_from_dr_dfs(premoves_inverse(htr_list[*htr_count], scramble, - eodr_moves, cp_transition_table), - premoves_inverse(htr_list[*htr_count], scramble, - eodr_moves, eo3_t_table), - scramble, eodr_moves, htr_list, htr_count, - eo3_t_table, cp_to_htr_pruning_table, - cp_htr_pruning_table, cp_finish_pruning_table, - mask, last1_inv, last2_inv, - 0, 0, moves, m, d, 1, can_use_niss, hide); -} - -int htr_scram_spam(int scram[], int htr_list[][30], int from, - int m, int b, int niss, int hide) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, corl = 0, cofb = 0; - int cp = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - } - - int fake_drm[2] = {0, 0}; /* Fake DR moves */ - - if ((from == 1 || from == 0) && (!eofb && !eorl && !coud)) { - for (int i = 0; i <= b; i++) { - niss_htr_from_dr_dfs(cp, eoud, scram, fake_drm, htr_list, &n, - eoud_transition_table, cpud_to_htr_pruning_table, - cp_htr_pruning_table, cp_drud_pruning_table, - move_mask_drud, 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if ((from == 2 || from == 0) && (!eorl && !eoud && !cofb)) { - for (int i = 0; i <= b; i++) { - niss_htr_from_dr_dfs(cp, eofb, scram, fake_drm, htr_list, &n, - eofb_transition_table, cpfb_to_htr_pruning_table, - cp_htr_pruning_table, cp_drfb_pruning_table, - move_mask_drfb, 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if ((from == 3 || from == 0) && (!eoud && !eofb && !corl)) { - for (int i = 0; i <= b; i++) { - niss_htr_from_dr_dfs(cp, eorl, scram, fake_drm, htr_list, &n, - eorl_transition_table, cprl_to_htr_pruning_table, - cp_htr_pruning_table, cp_drrl_pruning_table, - move_mask_drrl, 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else { - return -1; - } - return n; -} - - -/***********************/ -/* Direct DR (no NISS) */ -/***********************/ -void dr_dfs(int eo, int eo2, int eslice, int co, - int dr_list[][30], int *dr_count, - int eo_t_table[pow2to11][19], int eo2_t_table[pow2to11][19], - int eslice_t_table[binom12on4][19], int co_t_table[pow3to7][19], - int8_t eo_eslice_p_table[pow2to11][binom12on4], - int8_t eo_co_p_table[pow2to11][pow3to7], - int8_t eo2_co_p_table[pow2to11][pow3to7], - int last1, int last2, int moves, int max_sol, - int depth, int hide) { - if (*dr_count >= max_sol || moves + eo_eslice_p_table[eo][eslice] > depth || - moves + eo_co_p_table[eo][co] > depth || - moves + eo2_co_p_table[eo2][co] > depth) - return; - - dr_list[*dr_count][moves] = 0; - - if (eo == 0 && eslice == 0 && co == 0) { - /* If an early DR is found, or if "case R2 L". */ - if (moves != depth || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*dr_count < max_sol - 1) - copy_moves(dr_list[*dr_count], dr_list[(*dr_count)+1]); - (*dr_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - dr_list[*dr_count][moves] = i; - dr_dfs(eo_t_table[eo][i], eo2_t_table[eo2][i], - eslice_t_table[eslice][i], co_t_table[co][i], - dr_list, dr_count, - eo_t_table, eo2_t_table, eslice_t_table, co_t_table, - eo_eslice_p_table, eo_co_p_table, eo2_co_p_table, - i, last1, moves+1, max_sol, depth, hide); - } - } -} - -int dr_scram_spam(int scram[], int dr_list[][30], int fb, int rl, int ud, - int m, int b, int h) { - - init_directdr_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int epose = 0, eposm = 0, eposs = 0; - int coud = 0, corl = 0, cofb = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - epose = epose_transition_table[epose][scram[i]]; - eposm = eposm_transition_table[eposm][scram[i]]; - eposs = eposs_transition_table[eposs][scram[i]]; - } - - for (int i = 0; i <= b; i++) { - if (ud) - dr_dfs(eofb, eorl, epose, coud, dr_list, &n, - eofb_transition_table, eorl_transition_table, - epose_transition_table, coud_transition_table, - eofb_epose_pruning_table, eofb_coud_pruning_table, - eorl_coud_pruning_table, 0, 0, 0, m, i, h); - if (fb) - dr_dfs(eorl, eoud, eposs, cofb, dr_list, &n, - eorl_transition_table, eoud_transition_table, - eposs_transition_table, cofb_transition_table, - eorl_eposs_pruning_table, eorl_cofb_pruning_table, - eoud_cofb_pruning_table, 0, 0, 0, m, i, h); - if (rl) - dr_dfs(eoud, eofb, eposm, corl, dr_list, &n, - eoud_transition_table, eofb_transition_table, - eposm_transition_table, corl_transition_table, - eoud_eposm_pruning_table, eoud_corl_pruning_table, - eofb_corl_pruning_table, 0, 0, 0, m, i, h); - } - return n; -} - - -/*************/ -/* DR finish */ -/*************/ -void dr_finish_dfs(int cp, int ep8, int ep4, int sol[][30], int *sol_count, - int ep8_t_table[factorial8][19], - int ep4_t_table[factorial4][19], - int cp_p_table[factorial8], - int ep8_p_table[factorial8], - int mask, int last1, int last2, int moves, int m, int d) { - - - if (*sol_count >= m || moves + cp_p_table[cp] > d || - moves + ep8_p_table[ep8] > d) - return; - - sol[*sol_count][moves] = 0; - - if (cp == 0 && ep8 == 0 && ep4 == 0) { - if (moves != d) - return; - /* Copy moves for the next solution */ - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - sol[*sol_count][moves] = i; - dr_finish_dfs(cp_transition_table[cp][i], ep8_t_table[ep8][i], - ep4_t_table[ep4][i], sol, sol_count, - ep8_t_table, ep4_t_table, - cp_p_table, ep8_p_table, - mask, i, last1, moves+1, m, d); - } - } - - return; -} - -int dr_finish_scram_spam(int scram[], int sol[][30], int from, int m, int b) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, corl = 0, cofb = 0; - int cp = 0; - int ep[12]; - ep_int_to_array(0, ep); - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - apply_move_ep_array(scram[i], ep); - } - - if ((from == 1 && (eofb || eorl || coud)) || - (from == 2 && (eorl || eoud || cofb)) || - (from == 3 && (eoud || eofb || corl)) || - ((eofb || eorl || coud) && (eorl || eoud ||cofb) && (eoud ||eofb || corl))) - return -1; - - for (int i = 0; i <= b; i++) { - if ((from == 1 || from == 0) && (!eofb && !eorl && !coud)) - dr_finish_dfs(cp, epud_array_to_int(ep), epe_array_to_int(ep), - sol, &n, epud_transition_table, epe_transition_table, - cp_drud_pruning_table, epud_pruning_table, - move_mask_drud, 0, 0, 0, m, i); - if ((from == 2 || from == 0) && (!eorl && !eoud && !cofb)) - dr_finish_dfs(cp, epfb_array_to_int(ep), eps_array_to_int(ep), - sol, &n, epfb_transition_table, eps_transition_table, - cp_drfb_pruning_table, epfb_pruning_table, - move_mask_drfb, 0, 0, 0, m, i); - if ((from == 3 || from == 0) && (!eoud && !eofb && !corl)) - dr_finish_dfs(cp, eprl_array_to_int(ep), epm_array_to_int(ep), - sol, &n, eprl_transition_table, epm_transition_table, - cp_drrl_pruning_table, eprl_pruning_table, - move_mask_drrl, 0, 0, 0, m, i); - } - - return n; -} - -int htr_finish_scram_spam(int scram[], int sol[][30], int m, int b) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, cp = 0; - int ep[12]; - ep_int_to_array(0, ep); - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - apply_move_ep_array(scram[i], ep); - } - - if (eofb || eorl || eoud || coud || cpud_to_htr_pruning_table[cp] != 0) - return -1; - - for (int i = 0; i <= b; i++) - dr_finish_dfs(cp, epud_array_to_int(ep), epe_array_to_int(ep), - sol, &n, epud_transition_table, epe_transition_table, - cp_drud_pruning_table, epud_pruning_table, - move_mask_htr, 0, 0, 0, m, i); - - return n; -} - - -/**************/ -/* DR corners */ -/**************/ -void dr_corners_dfs(int cp, int sol[][30], int *sol_count, - int cp_p_table[factorial8], int mask, int last1, int last2, - int moves, int m, int d, int ignore) { - - if (*sol_count >= m || (!ignore && moves + cp_p_table[cp] > d) || - (ignore && moves + cp_p_table[cp] - 2 > d)) - return; - - - sol[*sol_count][moves] = 0; - - if (cp == 0 || - (ignore && mask == move_mask_drud && - (cp_transition_table[cp_transition_table[cp][U]][D3] == 0 || - cp_transition_table[cp_transition_table[cp][U2]][D2] == 0 || - cp_transition_table[cp_transition_table[cp][U3]][D] == 0 )) || - (ignore && mask == move_mask_drfb && - (cp_transition_table[cp_transition_table[cp][F]][B3] == 0 || - cp_transition_table[cp_transition_table[cp][F2]][B2] == 0 || - cp_transition_table[cp_transition_table[cp][F3]][B] == 0 )) || - (ignore && mask == move_mask_drrl && - (cp_transition_table[cp_transition_table[cp][R]][L3] == 0 || - cp_transition_table[cp_transition_table[cp][R2]][L2] == 0 || - cp_transition_table[cp_transition_table[cp][R3]][L] == 0 )) - ) { - if (moves != d) - return; - /* Copy moves for the next solution */ - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - sol[*sol_count][moves] = i; - dr_corners_dfs(cp_transition_table[cp][i], sol, sol_count, - cp_p_table, mask, i, last1, moves+1, m, d, ignore); - } - } -} - -int dr_corners_scram_spam(int scram[], int sol[][30], int from, int m, int b, - int ignore) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, corl = 0, cofb = 0; - int cp = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - } - - if ((from == 1 && coud) || (from == 2 && cofb) || (from == 3 && corl) || - (coud && cofb && corl)) - return -1; - - for (int i = 0; i <= b; i++) { - if ((from == 1 || from == 0) && !coud) - dr_corners_dfs(cp, sol, &n, cp_drud_pruning_table, move_mask_drud, - 0, 0, 0, m, i, ignore); - if ((from == 2 || from == 0) && !cofb) - dr_corners_dfs(cp, sol, &n, cp_drfb_pruning_table, move_mask_drfb, - 0, 0, 0, m, i, ignore); - if ((from == 3 || from == 0) && !corl) - dr_corners_dfs(cp, sol, &n, cp_drrl_pruning_table, move_mask_drrl, - 0, 0, 0, m, i, ignore); - } - - return n; -} - -/***************/ -/* Full solver */ -/***************/ - -int is_ep_solved(int ep, int moves[30]) { - int ep_arr[12]; - ep_int_to_array(ep, ep_arr); - for (int i = 0; moves[i]; i++) - apply_move_ep_array(moves[i], ep_arr); - return !ep_array_to_int(ep_arr); -} - -/* Solves directly using only small tables. Suitable for short solutions. */ -void small_optimal_dfs(int eofb, int eorl, int eoud, int ep, - int coud, int cofb, int corl, int cp, - int sol[][30], int *sol_count, int last1, int last2, - int moves, int m, int d) { - if (moves + eofb_pruning_table[eofb] > d || - moves + eorl_pruning_table[eorl] > d || - moves + eoud_pruning_table[eoud] > d || - moves + coud_pruning_table[coud] > d || - moves + cofb_pruning_table[cofb] > d || - moves + corl_pruning_table[corl] > d || - moves + cp_pruning_table[cp] > d || - *sol_count >= m) - return; - - sol[*sol_count][moves] = 0; - - if (eofb == 0 && coud == 0 && cp == 0) { - if (is_ep_solved(ep, sol[*sol_count])) { - if (moves != d) - return; - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - sol[*sol_count][moves] = i; - small_optimal_dfs(eofb_transition_table[eofb][i], - eorl_transition_table[eorl][i], - eoud_transition_table[eoud][i], ep, - coud_transition_table[coud][i], - cofb_transition_table[cofb][i], - corl_transition_table[corl][i], - cp_transition_table[cp][i], - sol, sol_count, i, last1, moves+1, m, d); - } - } -} - -/* Solves directly using only medium tables. Suitable for short solutions. -void medium_optimal_dfs(int eofb, int eorl, int eoud, - int epose, int eposs, int eposm, int ep, - int coud, int cofb, int corl, int cp, - int sol[][30], int *sol_count, int last1, int last2, - int moves, int m, int d) { - if (moves + eofb_epose_pruning_table[eofb][epose] > d || - moves + eorl_eposs_pruning_table[eorl][eposs] > d || - moves + eoud_eposm_pruning_table[eoud][eposm] > d || - moves + eofb_coud_pruning_table[eofb][coud] > d || - moves + eofb_corl_pruning_table[eofb][corl] > d || - moves + eorl_coud_pruning_table[eorl][coud] > d || - moves + eorl_cofb_pruning_table[eorl][cofb] > d || - moves + eoud_cofb_pruning_table[eoud][cofb] > d || - moves + eoud_corl_pruning_table[eoud][corl] > d || - moves + cp_pruning_table[cp] > d || - *sol_count >= m) - return; - - sol[*sol_count][moves] = 0; - - if (eofb == 0 && coud == 0 && cp == 0) { - if (is_ep_solved(ep, sol[*sol_count])) { - if (moves != d) - return; - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - sol[*sol_count][moves] = i; - medium_optimal_dfs(eofb_transition_table[eofb][i], - eorl_transition_table[eorl][i], - eoud_transition_table[eoud][i], - epose_transition_table[epose][i], - eposs_transition_table[eposs][i], - eposm_transition_table[eposm][i], ep, - coud_transition_table[coud][i], - cofb_transition_table[cofb][i], - corl_transition_table[corl][i], - cp_transition_table[cp][i], - sol, sol_count, i, last1, moves+1, m, d); - } - } -} -*/ - -/* Uses huge tables */ -int optimal_dfs(int ep, int cp, int eo, int co, int emslices, - int sol[][30], int last1, int last2, int moves, int d) { - if (moves + cp_co_pruning_table[cp][co] > d || - moves + triple_eo_pruning_table[eo][emslices] > d) - return 0; - - sol[0][moves] = 0; - - /* If solved, no need to check the depth */ - if (cp == 0 && co == 0 && eo == 0 && emslices == 0) - if (is_ep_solved(ep, sol[0])) - return 1; - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - sol[0][moves] = i; - if (optimal_dfs(ep, cp_transition_table[cp][i], - eofb_transition_table[eo][i], - coud_transition_table[co][i], - emslices_transition_table[emslices][i], - sol, i, last1, moves+1, d)) - return 1; - } - } - return 0; -} - -int solve_scram(int scram[], int sol[][30], int m, int b, int optimal) { - - /* Initialize pieces. */ - int eofb = 0, eorl = 0, eoud = 0, ep = 0; - int epose = 0, eposs = 0, eposm = 0; - int coud = 0, cofb = 0, corl = 0, cp = 0; - int emslices = 0; - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - epose = epose_transition_table[epose][scram[i]]; - eposs = eposs_transition_table[eposs][scram[i]]; - eposm = eposm_transition_table[eposm][scram[i]]; - - ep = apply_move_ep_int(scram[i], ep); - - coud = coud_transition_table[coud][scram[i]]; - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - cp = cp_transition_table[cp][scram[i]]; - - emslices = emslices_transition_table[emslices][scram[i]]; - } - - /* First we check if there are solutions of up to max_small moves. */ - int max_small = 10; - int n = 0; - init_small_pruning_tables(); - for (int i = 0; i <= min(b, max_small); i++) { - small_optimal_dfs(eofb, eorl, eoud, ep, coud, cofb, corl, cp, - sol, &n, 0, 0, 0, m, i); - if (n > 0 && optimal) - b = min(b, len(sol[0])); - } - - - if (n >= m || b <= 10) - return n; - - - /* If we found at least a solution, we return */ - if (n > 0) - return n; - - /* Then we try a 2-step solver */ - int max_step1 = 100; - int db = 12; - int step1[max_step1+10][30]; - int ss[300], step2[2][30]; - int best = b+1; - - /* TODO maybe: for now, multiple solutions can be found only using the - * short solver. */ - - int n_step1 = dr_scram_spam(scram, step1, 1, 1, 1, max_step1, min(b, db), 0); - for (int i = 0; i < n_step1; i++) { - copy_moves(scram, ss); - append_moves(step1[i], ss); - if (dr_finish_scram_spam(ss, step2, 0, 1, min(best-1,b) - len(step1[i]))) { - copy_moves(step1[i], sol[0]); - append_moves(step2[0], sol[0]); - best = len(sol[0]); - } - } - - /* If optimal solving was not required, or we have already found an optimal - * solution, we return. */ - if (best <= len(step1[n_step1-1]) || !optimal) - return best > b ? 0 : 1; - - /* Otherwise, we go on with the optimal solver. */ - int searched = len(step1[n_step1-1])-1; - - printf("Searched up to %d moves, no solution found.\n", searched); - printf("Using huge pruning tables, if not loaded it might take a while.\n"); - init_huge_pruning_tables(); - - for (int i = searched+1; i <= min(b, best-1); i++) { - if (i >= 10) - printf("Searching at depth %d.\n", i); - if (optimal_dfs(ep, cp, eofb, coud, emslices, sol, 0, 0, 0, i)) { - return 1; - } - } - return best > b ? 0 : 1; -} - -/* Given eofb, coud, ep and cp it finds a scramble that reaches that state. - * It uses a simple 3-step solver to find a preliminary "solution", and then - * gives this solutions as a scramble to a better solver (see above). */ -int reach_state(int eofb, int coud, int ep, int cp, int sol[][30]) { - - int fake_count = 0, fake_scram[30]; - int eo_list[2][30], dr_list[2][30], finish_list[2][30]; - - /* Convert ep to array */ - int ep_arr[12]; - ep_int_to_array(ep, ep_arr); - - /* Find EO */ - init_small_pruning_tables(); - for (int d = 0; d < 10; d++) { - niss_eo_dfs(eofb, fake_scram, eo_list, &fake_count, eofb_transition_table, - eofb_pruning_table, 0, 0, 0, 1, d, 0, 0, 0); - if (fake_count) { - fake_count = 0; - break; - } - } - - /* Apply moves found, find epose */ - for (int i = 0; eo_list[0][i]; i++) { - coud = coud_transition_table[coud][eo_list[0][i]]; - cp = cp_transition_table[cp][eo_list[0][i]]; - apply_move_ep_array(eo_list[0][i], ep_arr); - } - int epose = epose_array_to_int(ep_arr); - - /* Find DR */ - init_drfromeo_pruning_tables(); - for (int d = 0; d < 16; d++) { - niss_dr_from_eo_dfs(coud, epose, fake_scram, fake_scram, dr_list, - &fake_count, coud_transition_table, - epose_transition_table, - coud_epose_from_eofb_pruning_table, move_mask_eofb, - 0, 0, 0, 0, 0, 1, d, 0, 0, 0); - if (fake_count) { - fake_count = 0; - break; - } - } - - /* Apply moves found, find epud and epe */ - for (int i = 0; dr_list[0][i]; i++) { - cp = cp_transition_table[cp][dr_list[0][i]]; - apply_move_ep_array(dr_list[0][i], ep_arr); - } - int epud = epud_array_to_int(ep_arr); - int epe = epe_array_to_int(ep_arr); - - /* Find finish */ - init_small_pruning_tables(); - for (int d = 0; d < 16; d++) { - dr_finish_dfs(cp, epud, epe, finish_list, &fake_count, - epud_transition_table, epe_transition_table, - cp_drud_pruning_table, epud_pruning_table, move_mask_drud, - 0, 0, 0, 1, d); - if (fake_count) { - fake_count = 0; - break; - } - } - - int scram[50]; - - /* Debug */ - /*print_moves(eo_list[0]); printf("\n"); - print_moves(dr_list[0]); printf("\n"); - print_moves(finish_list[0]); printf("\n");*/ - - copy_moves(eo_list[0], scram); - append_moves(dr_list[0], scram); - append_moves(finish_list[0], scram); - return solve_scram(scram, sol, 1, 25, 0); -} diff --git a/src/solver.h b/src/solver.h @@ -1,16 +0,0 @@ -int eo_scram_spam(int scram[], int eo_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h); -int co_scram_spam(int scram[], int co_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h, int i); -int dr_scram_spam(int scram[], int dr_list[][30], int fb, int rl, int ud, - int m, int b, int h); -int drfrom_scram_spam(int scram[], int dr_list[][30], int from, int fb, - int rl, int ud, int m, int b, int niss, int hide); -int htr_scram_spam(int scram[], int htr_list[][30], int from, - int m, int b, int niss, int hide); -int dr_corners_scram_spam(int scram[], int sol[][30], int from, int m, int b, - int ignore); -int dr_finish_scram_spam(int scram[], int sol[][30], int from, int m, int b); -int htr_finish_scram_spam(int scram[], int sol[][30], int m, int b); -int solve_scram(int scram[], int sol[][30], int m, int b, int optimal); -int reach_state(int eofb, int coud, int ep, int cp, int sol[][30]); diff --git a/src/steps.c b/src/steps.c @@ -0,0 +1,941 @@ +#include "steps.h" + +/* Checkers, estimators and validators ***************************************/ + +static bool check_centers(Cube cube); +static bool check_eofb(Cube cube); +static bool check_drud(Cube cube); +static bool check_htr(Cube cube); + +static int estimate_eoany_HTM(CubeTarget ct); +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coany_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coany_URF(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drany_HTM(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_drud_eofb(CubeTarget ct); +static int estimate_dr_eofb(CubeTarget ct); +static int estimate_drudfin_drud(CubeTarget ct); +static int estimate_htr_drud(CubeTarget ct); +static int estimate_htrfin_htr(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +static bool always_valid(Alg *alg); +static bool validate_singlecw_ending(Alg *alg); + +/* Pre-transformation detectors **********************************************/ + +static Trans detect_pretrans_eofb(Cube cube); +static Trans detect_pretrans_drud(Cube cube); + +/* Messages for when cube is not ready ***************************************/ + +static char check_centers_msg[100] = "cube must be oriented (centers solved)"; +static char check_eo_msg[100] = "EO must be solved on given axis"; +static char check_dr_msg[100] = "DR must be solved on given axis"; +static char check_htr_msg[100] = "HTR must be solved"; +static char check_drany_msg[100] = "DR must be solved on at least one axis"; + +/* Steps *********************************************************************/ + +Step +optimal_HTM = { + .shortname = "optimal", + .name = "Optimal solve (in HTM)", + + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = always_valid, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +/* EO steps **************************/ +Step +eoany_HTM = { + .shortname = "eo", + .name = "EO on any axis", + + .estimate = estimate_eoany_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +eofb_HTM = { + .shortname = "eofb", + .name = "EO on F/B", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +eorl_HTM = { + .shortname = "eorl", + .name = "EO on R/L", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = ur, +}; + +Step +eoud_HTM = { + .shortname = "eoud", + .name = "EO on U/D", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +/* CO steps **************************/ +Step +coany_HTM = { + .shortname = "co", + .name = "CO on any axis", + + .estimate = estimate_coany_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +coud_HTM = { + .shortname = "coud", + .name = "CO on U/D", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +corl_HTM = { + .shortname = "corl", + .name = "CO on R/L", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = rf, +}; + +Step +cofb_HTM = { + .shortname = "cofb", + .name = "CO on F/B", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +Step +coany_URF = { + .shortname = "co-URF", + .name = "CO any axis (URF moveset)", + + .estimate = estimate_coany_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +coud_URF = { + .shortname = "coud-URF", + .name = "CO on U/D (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +corl_URF = { + .shortname = "corl-URF", + .name = "CO on R/L (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = rf, +}; + +Step +cofb_URF = { + .shortname = "cofb-URF", + .name = "CO on F/B (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = fd, +}; + +/* Misc corner steps *****************/ +Step +cornershtr_HTM = { + .shortname = "chtr", + .name = "Solve corners to HTR state", + + .estimate = estimate_cornershtr_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +cornershtr_URF = { + .shortname = "chtr-URF", + .name = "Solve corners to HTR state (URF moveset)", + + .estimate = estimate_cornershtr_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +corners_HTM = { + .shortname = "corners", + .name = "Solve corners", + + .estimate = estimate_corners_HTM, + .ready = NULL, + .is_valid = always_valid, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +corners_URF = { + .shortname = "corners-URF", + .name = "Solve corners (URF moveset)", + + .estimate = estimate_corners_URF, + .ready = NULL, + .is_valid = always_valid, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +/* DR steps **************************/ +Step +drany_HTM = { + .shortname = "dr", + .name = "DR on any axis", + + .estimate = estimate_drany_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +drud_HTM = { + .shortname = "drud", + .name = "DR on U/D", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +drrl_HTM = { + .shortname = "drrl", + .name = "DR on R/L", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = rf, +}; + +Step +drfb_HTM = { + .shortname = "drfb", + .name = "DR on F/B", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +/* DR from EO */ +Step +dr_eo = { + .shortname = "dr-eo", + .name = "DR without breaking EO (automatically detected)", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .detect = detect_pretrans_eofb, +}; + +Step +dr_eofb = { + .shortname = "dr-eofb", + .name = "DR on U/D or R/L without breaking EO on F/B", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = uf, +}; + +Step +dr_eorl = { + .shortname = "dr-eorl", + .name = "DR on U/D or F/B without breaking EO on R/L", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = ur, +}; + +Step +dr_eoud = { + .shortname = "dr-eoud", + .name = "DR on R/L or F/B without breaking EO on U/R", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fd, +}; + +Step +drud_eofb = { + .shortname = "drud-eofb", + .name = "DR on U/D without breaking EO on F/B", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = uf, +}; + +Step +drrl_eofb = { + .shortname = "drrl-eofb", + .name = "DR on R/L without breaking EO on F/B", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = rf, +}; + +Step +drud_eorl = { + .shortname = "drud-eorl", + .name = "DR on U/D without breaking EO on R/L", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = ur, +}; + +Step +drfb_eorl = { + .shortname = "drfb-eorl", + .name = "DR on F/B without breaking EO on R/L", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fr, +}; + +Step +drfb_eoud = { + .shortname = "drfb-eoud", + .name = "DR on F/B without breaking EO on U/D", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fd, +}; + +Step +drrl_eoud = { + .shortname = "drrl-eoud", + .name = "DR on R/L without breaking EO on U/D", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = rd, +}; + +/* DR finish steps */ +Step +dranyfin_DR = { + .shortname = "drfin", + .name = "DR finish on any axis without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .detect = detect_pretrans_drud, +}; + +Step +drudfin_drud = { + .shortname = "drudfin", + .name = "DR finish on U/D without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = uf, +}; + +Step +drrlfin_drrl = { + .shortname = "drrlfin", + .name = "DR finish on R/L without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = rf, +}; + +Step +drfbfin_drfb = { + .shortname = "drfbfin", + .name = "DR finish on F/B without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = fd, +}; + +/* HTR from DR */ +Step +htr_any = { + .shortname = "htr", + .name = "HTR from DR", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .detect = detect_pretrans_drud, +}; + +Step +htr_drud = { + .shortname = "htr-drud", + .name = "HTR from DR on U/D", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = uf, +}; + +Step +htr_drrl = { + .shortname = "htr-drrl", + .name = "HTR from DR on R/L", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = rf, +}; + +Step +htr_drfb = { + .shortname = "htr-drfb", + .name = "HTR from DR on F/B", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = fd, +}; + +/* HTR finish */ +Step +htrfin_htr = { + .shortname = "htrfin", + .name = "HTR finish without breaking HTR", + + .estimate = estimate_htrfin_htr, + .ready = check_htr, + .ready_msg = check_htr_msg, + .is_valid = always_valid, + .moveset = moveset_htr, + + .pre_trans = uf, +}; + +Step *steps[NSTEPS] = { + &optimal_HTM, /* first is default */ + + &eoany_HTM, + &eofb_HTM, + &eorl_HTM, + &eoud_HTM, + + &coany_HTM, + &coud_HTM, + &corl_HTM, + &cofb_HTM, + + &coany_URF, + &coud_URF, + &corl_URF, + &cofb_URF, + + &drany_HTM, + &drud_HTM, + &drrl_HTM, + &drfb_HTM, + + &dr_eo, + &dr_eofb, + &dr_eorl, + &dr_eoud, + &drud_eofb, + &drrl_eofb, + &drud_eorl, + &drfb_eorl, + &drfb_eoud, + &drrl_eoud, + + &dranyfin_DR, + &drudfin_drud, + &drrlfin_drrl, + &drfbfin_drfb, + + &htr_any, + &htr_drud, + &htr_drrl, + &htr_drfb, + + &htrfin_htr, + + &cornershtr_HTM, + &cornershtr_URF, + &corners_HTM, + &corners_URF, +}; + +/* Checkers, estimators and validators ***************************************/ + +static bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +static bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +static bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +static bool +check_htr(Cube cube) +{ + return check_drud(cube) && coord_htr_drud.index(cube) == 0; +} + +static int +estimate_eoany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_eofb_HTM, ct.cube); + r2 = ptableval(&pd_eofb_HTM, apply_trans(ur, ct.cube)); + r3 = ptableval(&pd_eofb_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_coud_HTM, ct.cube); + r2 = ptableval(&pd_coud_HTM, apply_trans(rf, ct.cube)); + r3 = ptableval(&pd_coud_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coany_URF(CubeTarget ct) +{ + int r1, r2, r3; + CubeTarget ct2, ct3; + + ct2.cube = apply_trans(rf, ct.cube); + ct2.target = ct.target; + + ct3.cube = apply_trans(fd, ct.cube); + ct3.target = ct.target; + + r1 = estimate_coud_URF(ct); + r2 = estimate_coud_URF(ct2); + r3 = estimate_coud_URF(ct3); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_drud_sym16_HTM, ct.cube); + r2 = ptableval(&pd_drud_sym16_HTM, apply_trans(rf, ct.cube)); + r3 = ptableval(&pd_drud_sym16_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +static int +estimate_drud_eofb(CubeTarget ct) +{ + return ptableval(&pd_drud_eofb, ct.cube); +} + +static int +estimate_dr_eofb(CubeTarget ct) +{ + int r1, r2; + + r1 = ptableval(&pd_drud_eofb, ct.cube); + r2 = ptableval(&pd_drud_eofb, apply_trans(rf, ct.cube)); + + return MIN(r1, r2); +} + +static int +estimate_drudfin_drud(CubeTarget ct) +{ + int val = ptableval(&pd_drudfin_noE_sym16_drud, ct.cube); + + if (val != 0) + return val; + + return ct.cube.epose % 24 == 0 ? 0 : 1; +} + +static int +estimate_htr_drud(CubeTarget ct) +{ + return ptableval(&pd_htr_drud, ct.cube); +} + +static int +estimate_htrfin_htr(CubeTarget ct) +{ + return ptableval(&pd_htrfin_htr, ct.cube); +} + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(rf, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(fd, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + /* Michiel de Bondt's trick */ + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + return MAX(ret, dr3); +} + +static bool +always_valid(Alg *alg) +{ + return true; +} + +static bool +validate_singlecw_ending(Alg *alg) +{ + int i; + bool nor, inv; + Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE; + + for (i = 0; i < alg->len; i++) { + if (alg->inv[i]) { + l2i = l1i; + l1i = alg->move[i]; + } else { + l2 = l1; + l1 = alg->move[i]; + } + } + + nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2)); + inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i)); + + return nor && inv; +} + +/* Pre-transformation detectors **********************************************/ + +static Trans +detect_pretrans_eofb(Cube cube) +{ + Trans i; + + for (i = 0; i < NROTATIONS; i++) + if (check_eofb(apply_trans(i, cube))) + return i; + + return 0; +} + +static Trans +detect_pretrans_drud(Cube cube) +{ + Trans i; + + for (i = 0; i < NROTATIONS; i++) + if (check_drud(apply_trans(i, cube))) + return i; + + return 0; +} diff --git a/src/steps.h b/src/steps.h @@ -0,0 +1,10 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "pruning.h" + +#define NSTEPS 50 + +extern Step * steps[NSTEPS]; + +#endif diff --git a/src/symcoord.c b/src/symcoord.c @@ -0,0 +1,355 @@ +#include "symcoord.h" + +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_cp_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_drudfin_noE_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_cp_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_drudfin_noE_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static void gensym(SymData *sd); +static bool read_symdata_file(SymData *sd); +static bool write_symdata_file(SymData *sd); + +/* Transformation groups and symmetry data ***********************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +static SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static SymData +sd_cp_16 = { + .filename = "sd_cp_16", + .coord = &coord_cp, + .sym_coord = &coord_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int nsymdata = 3; +static SymData * all_sd[] = { + &sd_coud_16, + &sd_cp_16, + &sd_eofbepos_16, +}; + + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_cp_sym16 = { + .index = index_cp_sym16, + .cube = antindex_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .max = POW3TO7 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drudfin_noE_sym16 = { + .index = index_drudfin_noE_sym16, + .cube = antindex_drudfin_noE_sym16, + .max = FACTORIAL8 * 2768, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +/* Functions *****************************************************************/ + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_cp_sym16(uint64_t ind) +{ + return sd_cp_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = antindex_eofbepos_sym16(ind/POW3TO7); + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_drudfin_noE_sym16(uint64_t ind) +{ + Cube c1, c2; + + c1 = coord_epud.cube(ind % FACTORIAL8); + c2 = antindex_cp_sym16(ind/FACTORIAL8); + c1.cp = c2.cp; + + return c1; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = antindex_eofbepos_sym16(ind/(FACTORIAL4*POW3TO7)); + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[coord_coud.index(cube)]; +} + +static uint64_t +index_cp_sym16(Cube cube) +{ + return sd_cp_16.class[coord_cp.index(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_drudfin_noE_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_cp_16.transtorep[coord_cp.index(cube)]; + c = apply_trans(t, cube); + + return index_cp_sym16(c) * FACTORIAL8 + coord_epud.index(c); +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[coord_eofbepos.index(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + +/* Other functions ***********************************************************/ + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static bool +read_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +void +init_symcoord() +{ + int i; + + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_coord(); + + for (i = 0; i < nsymdata; i++) + gensym(all_sd[i]); +} + diff --git a/src/symcoord.h b/src/symcoord.h @@ -0,0 +1,15 @@ +#ifndef SYMCOORD_H +#define SYMCOORD_H + +#include "coord.h" + +extern Coordinate coord_coud_sym16; +extern Coordinate coord_cp_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_drudfin_noE_sym16; +extern Coordinate coord_khuge; + +void init_symcoord(); + +#endif diff --git a/src/trans.c b/src/trans.c @@ -0,0 +1,375 @@ +#include "trans.h" + +/* Local functions ***********************************************************/ + +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static bool write_ttables_file(); + +/* Tables and other data *****************************************************/ + +static int ep_mirror[12] = { + [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL, + [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL, + [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL +}; + +static int cp_mirror[8] = { + [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL, + [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL +}; + +static int cpos_mirror[6] = { + [U_center] = U_center, [D_center] = D_center, + [R_center] = L_center, [L_center] = R_center, + [F_center] = F_center, [B_center] = B_center +}; + +/* TODO Is there a more elegant way? */ +static char rotation_alg_string[100][NROTATIONS] = { + [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3", + [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2", + [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3", + [lf] = "z", [ld] = "z y3", [lb] = "z y2", [lu] = "z y", + [fu] = "x y2", [fr] = "x y", [fd] = "x", [fl] = "x y3", + [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3", +}; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +/* Local functions implementation ********************************************/ + +static bool +read_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fread(co_ttable[m], b, me[5], f) == me[5]; + r = r && fread(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fread(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_alg(r)); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_alg(r), ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static bool +write_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fwrite(co_ttable[m], b, me[5], f) == me[5]; + r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +/* Public functions **********************************************************/ + +Cube +apply_trans(Trans t, Cube cube) +{ + /*init_trans();*/ + + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Trans +inverse_trans(Trans t) +{ + /* TODO is there a more elegant way? */ + static Trans inverse_trans_aux[NTRANS] = { + [uf] = uf, [ur] = ul, [ul] = ur, [ub] = ub, + [df] = df, [dr] = dr, [dl] = dl, [db] = db, + [rf] = lf, [rd] = bl, [rb] = rb, [ru] = fr, + [lf] = rf, [ld] = br, [lb] = lb, [lu] = fl, + [fu] = fu, [fr] = ru, [fd] = bu, [fl] = lu, + [bu] = fd, [br] = ld, [bd] = bd, [bl] = rd, + + [uf_mirror] = uf_mirror, [ur_mirror] = ur_mirror, + [ul_mirror] = ul_mirror, [ub_mirror] = ub_mirror, + [df_mirror] = df_mirror, [dr_mirror] = dl_mirror, + [dl_mirror] = dr_mirror, [db_mirror] = db_mirror, + [rf_mirror] = rf_mirror, [rd_mirror] = br_mirror, + [rb_mirror] = lb_mirror, [ru_mirror] = fl_mirror, + [lf_mirror] = lf_mirror, [ld_mirror] = bl_mirror, + [lb_mirror] = rb_mirror, [lu_mirror] = fr_mirror, + [fu_mirror] = fu_mirror, [fr_mirror] = lu_mirror, + [fd_mirror] = bu_mirror, [fl_mirror] = ru_mirror, + [bu_mirror] = fd_mirror, [br_mirror] = rd_mirror, + [bd_mirror] = bd_mirror, [bl_mirror] = ld_mirror + }; + + return inverse_trans_aux[t]; +} + +Alg * +rotation_alg(Trans t) +{ + int i; + + static Alg *rotation_alg_arr[NROTATIONS]; + static bool initialized = false; + + if (!initialized) { + for (i = 0; i < NROTATIONS; i++) + rotation_alg_arr[i] = new_alg(rotation_alg_string[i]); + + initialized = true; + } + + return rotation_alg_arr[t % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + /*init_trans();*/ + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +void +init_trans() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_moves(); + + Cube aux, cube, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_alg(i), (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_alg(m), (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + /* Old version: + * + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube) || is_solved(mirr)) + moves_ttable[m][mi] = move; + } + */ + + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + if (is_solved(cube)) { + moves_ttable[m][mi] = move; + break; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + diff --git a/src/trans.h b/src/trans.h @@ -0,0 +1,13 @@ +#ifndef TRANS_H +#define TRANS_H + +#include "moves.h" + +Cube apply_trans(Trans t, Cube cube); +Trans inverse_trans(Trans t); +Alg * rotation_alg(Trans i); +void transform_alg(Trans i, Alg *alg); + +void init_trans(); + +#endif diff --git a/src/utils.c b/src/utils.c @@ -1,132 +1,274 @@ #include "utils.h" -/* Hardcoded factorial of small numbers (n<=12). */ -int factorial[13] = { - 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600 -}; - -/* swaps two integers */ -void swap(int *a, int *b) { - int aux = *a; - *a = *b; - *b = aux; -} - -/* Converts the integer a to its representation in base b (first n digits - * only) and saves the result in r. */ -void int_to_digit_array(int a, int b, int n, int *r) { - for (int i = 0; i < n; i++) { - r[i] = a % b; - a /= b; - } -} - -/* Converts the array of n digits a to a integer using base b. */ -int digit_array_to_int(int *a, int n, int b) { - int ret = 0, p = 1; - for (int i = 0; i < n; i++) { - ret += a[i] * p; - p *= b; - } - return ret; -} - -/* Converts a permutation on [0..(n-1)] into the integer i which is the index - * of the permutation in the sorted list of all n! such permutations. - * Only works for n<=12. */ -int perm_to_index(int *a, int n) { - int ret = 0; - for (int i = 0; i < n; i++) { - int c = 0; - for (int j = i+1; j < n; j++) - if (a[i] > a[j]) - c++; - ret += factorial[n-i-1] * c; - } - return ret; -} - -/* Converts a permutation index to the actual permutation as an array - * (see perm_to_index) and saves the result to r. */ -void index_to_perm(int p, int n, int *r) { - int a[n]; - for (int j = 0; j < n; j++) - a[j] = 0; /* picked elements */ - for (int i = 0; i < n; i++) { - int c = 0, j = 0; - while (c <= p / factorial[n-i-1]) { - if (!a[j]) - c++; - j++; - } - r[i] = j-1; - a[j-1] = 1; - p %= factorial[n-i-1]; - } -} - - -int perm_sign_array(int a[], int n) { - int ret = 0; - for (int i = 0; i < n; i++) - for (int j = i+1; j < n; j++) - if (a[i]>a[j]) - ret++; - return ret % 2; -} - -int perm_sign_int(int p, int n) { - int a[n]; - index_to_perm(p, n, a); - return perm_sign_array(a, n); -} - - -/* Converts a k-element subset of a set with an element from an array of n - * elements, of which k are 1 (or just non-zero) and n-k are 0, to its index - * in the sorted list of all such subsets. - * Works only for n <= 12. */ -int subset_to_index(int *a, int n, int k) { - int ret = 0; - for (int i = 0; i < n; i++) { - if (k == n-i) - return ret; - if (a[i]) { - ret += factorial[n-i-1] / (factorial[k] * factorial[n-i-1-k]); - k--; - } - } - return ret; -} - -/* Inverse of the above */ -void index_to_subset(int s, int n, int k, int *r) { - for (int i = 0; i < n; i++) { - if (k == n-i) { - for (int j = i; j < n; j++) - r[j] = 1; - return; - } - int v = factorial[n-i-1] / (factorial[k] * factorial[n-i-1-k]); - if (s >= v) { - r[i] = 1; - k--; - s -= v; - } else { - r[i] = 0; - } - } -} - -/* Converts the first n-1 digits of a number to an array a of digits in base b; - * then adds one element to the array, so that the sum of the elements of a is - * zero modulo b. - * This is used for determing the edge orientation from an 11-bits integer or - * the corner orientation from a 7-trits integer. */ -void int_to_sum_zero_array(int x, int b, int n, int *a) { - int_to_digit_array(x, b, n-1, a); - int s = 0; - for (int i = 0; i < n - 1; i++) s = (s + a[i]) % b; - a[n-1] = (b - s) % b; +void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; } diff --git a/src/utils.h b/src/utils.h @@ -1,58 +1,41 @@ -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#define abs(a) (((a) > 0) ? (a) : (-(a))) - -/* Some useful constants */ -#define pow2to11 2048 -#define pow2to12 4096 -#define pow3to7 2187 -#define pow3to8 6561 -#define pow12to4 20736 -#define factorial4 24 -#define factorial6 720 -#define factorial8 40320 -#define factorial12 479001600 -#define binom12on4 495 -#define binom8on4 70 - -void swap(int *a, int *b); - -/* Hardcoded factorial of small numbers (n<=12). */ -extern int factorial[13]; - -/* Converts the integer a to its representation in base b (first n digits - * only) and saves the result in r. */ -void int_to_digit_array(int a, int b, int n, int *r); - -/* Converts the array of n digits a to a integer using base b. */ -int digit_array_to_int(int *a, int n, int b); - -/* Converts a permutation on [0..(n-1)] into the integer i which is the index - * of the permutation in the sorted list of all n! such permutations. - * Only works for n<=12. */ -int perm_to_index(int *a, int n); - -/* Converts a permutation index to the actual permutation as an array - * (see perm_to_index) and saves the result to r. */ -void index_to_perm(int p, int n, int *r); - -/* Determine the sign of a permutation, either in integer or array format. */ -int perm_sign_array(int a[], int n); -int perm_sign_int(int p, int n); - -/* Converts a k-element subset of a set with an element from an array of n - * elements, of which k are 1 and n-k are 0, to its index in the sorted list - * of all such subsets. - * Works only for n <= 12. */ -int subset_to_index(int *a, int n, int k); - -/* Inverse of the above */ -void index_to_subset(int s, int n, int k, int *r); - -/* Converts the first n-1 digits of a number to an array a of digits in base b; - * then adds one element to the array, so that the sum of the elements of a is - * zero modulo b. - * This is used for determing the edge orientation from an 11-bits integer or - * the corner orientation from a 7-trits integer. */ -void int_to_sum_zero_array(int x, int b, int n, int *a); - +#ifndef UTILS_H +#define UTILS_H + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +void apply_permutation(int *perm, int *set, int n); +int binomial(int n, int k); +int digit_array_to_int(int *a, int n, int b); +int factorial(int n); +void index_to_perm(int p, int n, int *r); +void index_to_subset(int s, int n, int k, int *r); +void int_to_digit_array(int a, int b, int n, int *r); +void int_to_sum_zero_array(int x, int b, int n, int *a); +int invert_digits(int a, int b, int n); +bool is_perm(int *a, int n); +bool is_subset(int *a, int n, int k); +int perm_sign(int *a, int n); +int perm_to_index(int *a, int n); +int powint(int a, int b); +int subset_to_index(int *a, int n, int k); +void sum_arrays_mod(int *src, int *dst, int n, int m); +void swap(int *a, int *b); + +#endif