nissy-classic

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

commit 0e8d73bb3edcc8bdff6e3ded442b66f68265059a
parent 4e359b44ce111b04cc4d2b28033fba4ab4e6e989
Author: Sebastiano Tronto <sebastiano.tronto@gmail.com>
Date:   Sun, 21 Jun 2020 23:01:57 +0200

First push

Diffstat:
A.gitignore | 2++
MREADME.md | 39++++++++++++++++++++++++++++++++++++++-
ATODO | 13+++++++++++++
Adocs/add.txt | 18++++++++++++++++++
Adocs/change.txt | 21+++++++++++++++++++++
Adocs/dr.txt | 45+++++++++++++++++++++++++++++++++++++++++++++
Adocs/drcorners.txt | 34++++++++++++++++++++++++++++++++++
Adocs/drfinish.txt | 29+++++++++++++++++++++++++++++
Adocs/eo.txt | 38++++++++++++++++++++++++++++++++++++++
Adocs/exit.txt | 9+++++++++
Adocs/help.txt | 15+++++++++++++++
Adocs/htr.txt | 30++++++++++++++++++++++++++++++
Adocs/htrfinish.txt | 23+++++++++++++++++++++++
Adocs/invert.txt | 14++++++++++++++
Adocs/nissy.txt | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/pic.txt | 20++++++++++++++++++++
Adocs/pre.txt | 18++++++++++++++++++
Adocs/print.txt | 19+++++++++++++++++++
Adocs/quit.txt | 9+++++++++
Adocs/replace.txt | 22++++++++++++++++++++++
Adocs/save.txt | 22++++++++++++++++++++++
Adocs/solve.txt | 33+++++++++++++++++++++++++++++++++
Adocs/unniss.txt | 14++++++++++++++
Anissy | 0
Asrc/compile.sh | 1+
Asrc/coordinates.c | 288+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/coordinates.h | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/io.c | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/io.h | 21+++++++++++++++++++++
Asrc/main.c | 801+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/moves.c | 526+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/moves.h | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pruning_tables.c | 483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pruning_tables.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/solver.c | 893+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/solver.h | 13+++++++++++++
Asrc/utils.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/utils.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
38 files changed, 4242 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +backup +src/dbg_compile.sh diff --git a/README.md b/README.md @@ -1,2 +1,39 @@ # nissy -A Rubik's cube solver and FMC assistant +A Rubik's cube solver and FMC assistant. + +## 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. + +## 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. + +## How to use it +Check out the help pages in the docs folder. They are also available from +within nissy with the command "help". + +I will add more examples and maybe screenshots when I feel like. + +## 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/src +gcc -O3 -std=c99 -o ../nissy *.c +cd .. +./nissy +``` + +You can also use the script compile.sh in the src folder, 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! diff --git a/TODO b/TODO @@ -0,0 +1,13 @@ +NEXT VERSION: +- solver: improve logic. check after each dr? +- add checks for sequences >255 moves +- cat! +- niss for direct DR + - prune for CO using CO+CP state +- Option: exactly N moves (or lower bound); exactly optimal? +- CO (also from EO), direct corners solve +- scramble shortner (solve + invert) +- RUF-ify + +FUTURE IDEAS: +- sort before branching dfs diff --git a/docs/add.txt b/docs/add.txt @@ -0,0 +1,18 @@ + +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 @@ -0,0 +1,21 @@ + +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/dr.txt b/docs/dr.txt @@ -0,0 +1,45 @@ + +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 @@ -0,0 +1,34 @@ + +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 @@ -0,0 +1,29 @@ + +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 @@ -0,0 +1,38 @@ + +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 @@ -0,0 +1,9 @@ + +HELP PAGE FOR COMMAND exit + +SYNTAX +exit + +DESCRIPTION +Exits nissy. + diff --git a/docs/help.txt b/docs/help.txt @@ -0,0 +1,15 @@ + +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 @@ -0,0 +1,30 @@ + +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 @@ -0,0 +1,23 @@ + +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 @@ -0,0 +1,14 @@ + +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 @@ -0,0 +1,88 @@ + +******************************************************************************* +********************* 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 @@ -0,0 +1,20 @@ + +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/pre.txt b/docs/pre.txt @@ -0,0 +1,18 @@ + +HELP PAGE FOR COMMAND pre + +SYNTAX +pre [MOVES|$ID1|@ID1] $ID2 + +DESCRIPTION +Prepends either MOVES, the scramble memorized under $ID1 or the output sequence +memorized under @ID1 at the beginning 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: "pre(pend) x to y" or just "pre(pend) to y". + +EXAMPLES +add $1 + The user is required to type the moves that will be prepended to $1. +add F R B $1 + Prepends the moves F R B to scramble $1. Now scramble $1 begins with F R B. diff --git a/docs/print.txt b/docs/print.txt @@ -0,0 +1,19 @@ + +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 @@ -0,0 +1,9 @@ + +HELP PAGE FOR COMMAND quit + +SYNTAX +quit + +DESCRIPTION +Exits nissy. + diff --git a/docs/replace.txt b/docs/replace.txt @@ -0,0 +1,22 @@ + +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 @@ -0,0 +1,22 @@ + +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/solve.txt b/docs/solve.txt @@ -0,0 +1,33 @@ + +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 @@ -0,0 +1,14 @@ + +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/nissy b/nissy Binary files differ. diff --git a/src/compile.sh b/src/compile.sh @@ -0,0 +1 @@ +gcc -Wall -Wextra -O3 -std=c99 -o ../nissy -g *.c diff --git a/src/coordinates.c b/src/coordinates.c @@ -0,0 +1,288 @@ +/* blabla */ + +#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 @@ -0,0 +1,92 @@ +/* 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/io.c b/src/io.c @@ -0,0 +1,232 @@ +#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 @@ -0,0 +1,21 @@ +#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 @@ -0,0 +1,801 @@ +/* blabla */ +#include <stdio.h> +#include <stdlib.h> +#include "utils.h" +#include "coordinates.h" +#include "io.h" +#include "moves.h" +#include "solver.h" +#include "string.h" + +char *commands[][10] = { + {"help", "[COMMAND]", + "Print this help, or a help page for COMMAND."}, + {"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."}, + {"eo", "[MOVES|$ID|@ID]", + "Solves EO."}, + {"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."}, + {"", "", ""} +}; + +/* 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[][21]) { + 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 help_cmd(int n, char cmdtok[][100]) { + if (n == 1) { + printf("\n"); + for (int i = 0; commands[i][0][0]; i++) + printf("%-10s%-20s%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) { + char fname[255], line[255] = ""; + FILE *file; + sprintf(fname, "docs/%s.txt", cmdtok[1]); + file = fopen(fname, "r"); + if (file == NULL) { + printf("No help file for %s.\n", cmdtok[1]); + return; + } + while (fgets(line, 255, file) != NULL) + printf("%s", line); + } else { + printf("help: wrong syntax.\n"); + } +} + +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 = 20, 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][21]; + 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][21], aux3[21]; + 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 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][21]; + int neo = eo_scram_spam(scram_unnissed, eo_list, fb, rl, ud, m, b, niss, + hide); + print_results(neo, eo_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][21], 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][21], 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][21], 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][21], 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][21], 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]); +} + +void (*cmd_list[])(int n, char cmdtok[][100]) = { + help_cmd, save_cmd, change_cmd, print_cmd, + add_cmd, invert_cmd, unniss_cmd, pic_cmd, + solve_cmd, replace_cmd, + eo_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]); +} + +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 @@ -0,0 +1,526 @@ +/* This is a simple program to solve the Rubik's Cube. + * No idea how many features I am going to implement. + * Open source license and whatnot. + * I am trying to follow the C99 standard. */ + +/* 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]; + +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; + + int mask = move_mask_all ^ (7 << last1); + + 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); + + 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' */ +}; + +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' */ +}; + +/* 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); + } +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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(); +} + diff --git a/src/moves.h b/src/moves.h @@ -0,0 +1,83 @@ +#include "utils.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 */ + +extern int possible_next[19][19]; + +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(); + diff --git a/src/pruning_tables.c b/src/pruning_tables.c @@ -0,0 +1,483 @@ +#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 @@ -0,0 +1,66 @@ +#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/solver.c b/src/solver.c @@ -0,0 +1,893 @@ +#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[21], int scramble[], int prev_moves[21], + 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[][21], 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; + } + + if (moves + p_table[eo] <= d) { + 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); + } + } + } + + 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[][21], 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; +} + + +/**************/ +/* DR from EO */ +/**************/ + + +/* Scramble includes premoves for previous EO */ +void niss_dr_from_eo_dfs(int co, int epos, int scramble[], int eo_moves[21], + int dr_list[][21], 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; + } + + if (moves + p_table[co][epos] <= d) { + 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); + } + } + } + + 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[][21], 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[21], + int htr_list[][21], 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; + } + + if (moves + cp_htr_pruning_table[cp] <= d) { + 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); + } + } + } + + 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[][21], 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[][21], 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[][21], 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[][21], 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[][21], 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[][21], 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[][21], 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 && + (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 ))) { + 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[][21], 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[21]) { + 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[][21], 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[][21], 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[][21], 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[][21], 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; + + /* Then we try a slightly larger optimal solver */ + /* + int max_medium = 11; + init_directdr_pruning_tables(); + for (int i = 0; i <= min(b, max_medium); i++) + medium_optimal_dfs(eofb, eorl, eoud, epose, eposs, eposm, ep, + coud, cofb, corl, cp, sol, &n, 0, 0, 0, m, i); + */ + + /* If we found at least a solution, we return */ + if (n > 0) + return n; + + /* Then we try a 2-step solver */ + int max_step1 = 5000; + int db = 14; + int step1[max_step1+10][21]; + int ss[300], step2[2][21]; + 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; +} diff --git a/src/solver.h b/src/solver.h @@ -0,0 +1,13 @@ +int eo_scram_spam(int scram[], int eo_list[][21], int fb, int rl, int ud, + int m, int b, int niss, int h); +int dr_scram_spam(int scram[], int dr_list[][21], int fb, int rl, int ud, + int m, int b, int h); +int drfrom_scram_spam(int scram[], int dr_list[][21], 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[][21], int from, + int m, int b, int niss, int hide); +int dr_corners_scram_spam(int scram[], int sol[][21], int from, int m, int b, + int ignore); +int dr_finish_scram_spam(int scram[], int sol[][21], int from, int m, int b); +int htr_finish_scram_spam(int scram[], int sol[][21], int m, int b); +int solve_scram(int scram[], int sol[][21], int m, int b, int optimal); diff --git a/src/utils.c b/src/utils.c @@ -0,0 +1,115 @@ +#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]; + } +} + +/* 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; +} + diff --git a/src/utils.h b/src/utils.h @@ -0,0 +1,54 @@ +#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); + +/* 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); +