nissy-classic

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

commit 6f4313bc1ed5be794146e6ca2fd73f48c7906061
parent 1a5bfe9b08707b0aef748d7921a419ba4a046fba
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Mon,  1 May 2023 12:48:55 +0200

Reverted to 2.0.3

Diffstat:
M.gitignore | 1-
MINSTALL | 93+++++++++++++++++++++++++++++++------------------------------------------------
MMakefile | 21++++++++++-----------
DTODO/2.1.md | 51---------------------------------------------------
DTODO/build-options.md | 33---------------------------------
DTODO/documentation.md | 41-----------------------------------------
DTODO/easy.md | 16----------------
DTODO/installation.md | 14--------------
DTODO/new-feature-ideas.md | 32--------------------------------
DTODO/parser.md | 13-------------
DTODO/refactoring.md | 28----------------------------
DTODO/testing.md | 6------
DTODO/webapp.md | 19-------------------
Dold/maybe-useful-coord.c | 181-------------------------------------------------------------------------------
Dold/maybe-useful-steps.c | 1111-------------------------------------------------------------------------------
Dold/solve_old.c | 447-------------------------------------------------------------------------------
Dold/solve_old.h | 9---------
Msrc/alg.c | 301++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/alg.h | 16++++++++++++----
Msrc/commands.c | 358++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/commands.h | 199+------------------------------------------------------------------------------
Msrc/coord.c | 977++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/coord.h | 271++++++-------------------------------------------------------------------------
Msrc/cube.c | 932+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/cube.h | 59+++++++++++++++++++++++++++++++++++------------------------
Msrc/cubetypes.h | 240++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/env.c | 2--
Msrc/env.h | 4++--
Dsrc/fst.c | 401-------------------------------------------------------------------------------
Dsrc/fst.h | 13-------------
Msrc/moves.c | 419++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/moves.h | 23++++++++++++++++++-----
Dsrc/movesets.c | 194-------------------------------------------------------------------------------
Dsrc/movesets.h | 15---------------
Asrc/pf.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/pf.h | 20++++++++++++++++++++
Msrc/pruning.c | 371+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/pruning.h | 22++++++++++++++++------
Msrc/shell.c | 10+---------
Msrc/solve.c | 548++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/solve.h | 51++++-----------------------------------------------
Dsrc/solver_step.c | 306-------------------------------------------------------------------------------
Dsrc/solver_step.h | 12------------
Msrc/steps.c | 1494++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/steps.h | 243+++----------------------------------------------------------------------------
Asrc/symcoord.c | 687+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/symcoord.h | 17+++++++++++++++++
Dsrc/threader_eager.c | 163-------------------------------------------------------------------------------
Dsrc/threader_eager.h | 8--------
Dsrc/threader_single.c | 35-----------------------------------
Dsrc/threader_single.h | 8--------
Msrc/trans.c | 383++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/trans.h | 36+++++++++++++++---------------------
Msrc/utils.c | 14++++++--------
Dtests/alg_tests.c | 266-------------------------------------------------------------------------------
Dtests/alg_tests.h | 21---------------------
Dtests/coord_tests.c | 50--------------------------------------------------
Dtests/coord_tests.h | 13-------------
Dtests/fst_tests.c | 184-------------------------------------------------------------------------------
Dtests/fst_tests.h | 19-------------------
Dtests/test.c | 85-------------------------------------------------------------------------------
Dtests/test_common.h | 41-----------------------------------------
Dwww/download/index.html | 217-------------------------------------------------------------------------------
Dwww/examples/index.html | 49-------------------------------------------------
Dwww/favicon.png | 0
Dwww/index.html | 93-------------------------------------------------------------------------------
Dwww/screenshot.png | 0
Dwww/style-3.css | 105-------------------------------------------------------------------------------
68 files changed, 5571 insertions(+), 6635 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -2,4 +2,3 @@ doc/*.html doc/*.pdf nissy nissy-* -test diff --git a/INSTALL b/INSTALL @@ -4,78 +4,57 @@ Nissy is available at https://nissy.tronto.net # Requirements -A full installation of nissy requires about 3Gb of space, of which -2.3Gb are occupied by the huge pruning table for fast optimal solving, -and running it requires the same amount of RAM. One can choose to never -use this function and not to install the relative pruning table. There -is an alternative (slower) optimal solving function that uses about -500Mb of RAM. When generating the pruning tables automatically (see -the section Tables below), at least 5.3Gb or RAM are required. +A full installation of nissy requires about 3Gb of space, +of which 2.3Gb are occupied by the huge pruning table for fast optimal solving, +and running it requires the same amount of RAM. +One can choose to never use this function and not to install the relative +pruning table. There is an alternative (slower) +optimal solving function that uses about 500Mb of RAM. # Installation ## On Windows Try downloading and executing in a terminal the file nissy.exe, then -follow the instructions in the Tables section below for installing the -pruning tables. If nissy.exe does not work, you can try following the -UNIX instructions in WSL (Windows Subsystem for Linux) or in a similar -environment. +follow the instructions in the Tables section below for +installing the pruning tables. +If nissy.exe does not work, you can try following the UNIX instructions +in WSL (Windows Subsystem for Linux) or in a similar environment. -## On a UNIX system: - -Download the source archive (.tar.gz). Extract it with your favorite -archive program, for example with - - tar -xvzf nissy-VERSION.tar.gz - -Open a terminal in the directory just extracted. If you wish, edit the -Makefile to match your local configuration (this is usually not necessary, -but you may want to change the PREFIX variable to change the installation -path) and run - - make - -followed by +Sorry for the inconvenience, I don't have a Windows machine to test this on. - make install +## On a UNIX system: -Then follow the instructions below to install the pruning tables. +Edit the Makefile to match your local configuration (usually not necessary, but you +may want to change the PREFIX variable) and run make, followed by make install. +Follow the instructions below to install the pruning tables. ## Tables +Nissy needs to generate certain large tables to work. These tables are by default +generated the first time they are needed (e.g the first time you ask to solve a +certain step) and then saved to a file. Whenever these tables are needed again, +nissy simply loads the corresponding file from the hard disk. -Once you have installed nissy, run +The very large table for optimal solving can take some time to generate +(about 1.5 hours on my fairly old but decent laptop, using 8 CPU threads). +In order to generate it you need at least 5Gb of RAM. +All other tables are much faster. - nissy gen +You can ask nissy to generate all the tables it will ever need with the gen +command. It is recommended to use more than one thread, if your CPU has them. +For example, you can run: -to generate all the tables that Nissy will ever need. Running this -command requires around 5.3Gb of RAM, and it can take some time (about -40 minutes on my fairly old but decent laptop, with 8 CPU threads). +nissy gen -t 8 -Some unnecessary technical detail: by default this command is going to -use at most 64 threads. If you want you can choose to use more threads -(if your CPU is very powerful) or fewer threads (if you for example -want to run this command in the background while you do other stuff) -with the -t option, for example nissy gen -t 1. +to generate all tables using 8 threads. -Alternatively, you can download all the tables (1.7Gb) and copy them -into the correct folder (see manual page, ENVIRONMENT section). On UNIX -operating systems this folder is either .nissy/tables in the user's -home directory or $XDG_DATA_HOME/nissy/tables if the XDG variable -is configured. On Windows it is the same directory as the nissy.exe -executable file. +Alternatively, you can simply download all the tables and copy them into the +correct folder (see manual page, ENVIRONMENT section). On UNIX operating +systems this folder is either .nissy/tables in the user's home directory or +$XDG_DATA_HOME/nissy/tables if the XDG variable is configured. On Windows +it is the same directory as the nissy.exe executable file. You can downloads all the tables from the following link: - - https://nissy.tronto.net/nissy-tables-2.0.2.zip - -# Upgrading - -If you already have nissy installed and you want to upgrade to a more -recent version, you can simply repeat the installation process: -* On Windows: simply replace nissy.exe with the new file with the same name. -* On UNIX systems: download the new version of the source code, extract it - in a new folder and run make and make install again. - -Between each version new table files might have been added, or old ones -may be not used anymore. Nissy will deal with this automatically. + https://nissy.tronto.net/nissy-tables-2.0.zip +The version 2.0 at the end of the file name is only indicative. +Later versions will use the same tables, unless otherwise specified. diff --git a/Makefile b/Makefile @@ -1,18 +1,19 @@ # See LICENSE file for copyright and license details. -VERSION = post-2.0.2 +VERSION = 2.0.3 PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man -CPPFLAGS = -DVERSION=\"${VERSION}\" -CFLAGS = -std=c99 -pthread -pedantic -Wall -Wextra \ - -Wno-unused-parameter -O3 ${CPPFLAGS} -DBGFLAGS = -std=c99 -pthread -pedantic -Wall -Wextra \ - -Wno-unused-parameter -g ${CPPFLAGS} +CPPFLAGS = -DVERSION=\"${VERSION}\" +CFLAGS = -std=c99 -pthread -pedantic -Wall -Wextra \ + -Wno-unused-parameter -O3 ${CPPFLAGS} +DBGFLAGS = -std=c99 -pthread -pedantic -Wall -Wextra \ + -Wno-unused-parameter -g ${CPPFLAGS} CC = cc + all: nissy nissy: clean @@ -24,11 +25,8 @@ nissy.exe: debug: ${CC} ${DBGFLAGS} -o nissy src/*.c -test: - ${CC} ${DBGFLAGS} -DTEST -o test src/*.c tests/*.c - clean: - rm -rf nissy nissy*.exe nissy*.tar.gz doc/nissy.html doc/nissy.pdf + rm -rf nissy nissy*.exe nissy*.tar.gz dist: clean nissy.exe mkdir -p nissy-${VERSION} @@ -65,4 +63,5 @@ uninstall: rm -rf ${DESTDIR}${PREFIX}/bin/nissy ${DESTDIR}${MANPREFIX}/man1/nissy.1 for s in ${SCRIPTS}; do rm -rf ${DESTDIR}${PREFIX}/bin/$$s; done -.PHONY: all debug test clean dist install uninstall upload +.PHONY: all debug clean dist install uninstall upload + diff --git a/TODO/2.1.md b/TODO/2.1.md @@ -1,51 +0,0 @@ -# TODO-list for version 2.1 (or is it 3.0 at this point?) - -## Rework solver - -### 1. Implement minimum viable - -* Implement nxopt31 with fst_cube. Remember that the function - move_check_solved() should do one axis at the time, so that we don't move - everything before checking. -* test? - -### 2. Rework achitecture and file dependencies - -* solve.h depends only on moves(alg?) (dependency on step and trans is removed). -* Other modules have changed dependencies, might as well rework all. -* Make files smaller, do not include definition in .h, separate -data from abstract operations. -* remove cubetypes.h -* Create a module for multi-step (maybe wait?) -* Possible changes: in step solver, copy cube only if niss; add cleanup function -in solver (called by solve()) to free cube and perhaps pruning tables. -* see various TODO's in files - -### 4. More threading options - -* Lazy multithread: threads are as independent as possible and only -merged at the end. Ideal when all solutions of a certain length are requested. -* (Done) Eager multithread: current implementation, branches communicate the -number and list of solutions to stop as soon as possible. Good when only one -solution of a certain depth is required. - -## Simplify steps - -* Remove one type of rotation. -* Change steps to choicestep and stepalt to step (or was this already done?). - -## Add missing coordinates and steps - -* Check the old file for a list. Many are missing. -* Checkers in steps.c should use coordinates. - -## Missing and new commands - -* gen -* freemem -* twophase - -## Easy improvements - -* Solve should re-orient the cube if centers are off -* Solve: add options for -I (inverse only) and -L (linear = normal + inverse). diff --git a/TODO/build-options.md b/TODO/build-options.md @@ -1,33 +0,0 @@ -# Build options for memory and multithreading - -## Investigate - -* Check exactly how much memory is needed for everything. -* Take note of which parts use threading (solving, genptable, other?). - -## Prepare code - -* Use define / ifdef or similar to compile and build tables only for the - parts to be used. -* If threads = 1, use a much simpler version of the solve method. Remember - that checking if enough solutions have been found is the first thing to - do in singlethread (no locking). -* Do not include pthread if threads = 1. -* Only one optimal solver should be compiled. -* Some simple steps may also need alternatives with smaller tables - (e.g. for staying sub 1Gb). For example dr and drfin. -* If necessary, work out alternatives to "twophase" for low-resource versions. - -## Makefile - -* Figure out how to change these options via makefile. For example: one - variable for the maximum allowed ram and one for the number of threads. -* (Optional) use a configure script? -* (Optional) interactive installation script? - -## Automate - -* Scout for resources during installation and choose best configuration - automatically. -* How to do this in Linux / POSIX? -* How to do this in Windows? diff --git a/TODO/documentation.md b/TODO/documentation.md @@ -1,41 +0,0 @@ -# Documentation - -## Big documentation file on nissy's internals - -* Coordinates -* Symcoordinates -* Pruning tables -* Coordinate solving -* fst cube -* Optimized solver -* Multithreading -* Commands etc... -* Code architecture - -## examples.md - -* Example file for nissy's website and documentation folder -* With screenshots! - -## Random info - -Where to collect random information like this table? - -Table pt_nxopt31_HTM -Base value: 9 -0 1 -1 6 -2 29 -3 164 -4 1433 -5 16772 -6 205033 -7 2513871 -8 30329976 -9 342440769 -10 2815191126 -11 6147967200 -12 524918774 -13 3546 -14 0 -15 0 diff --git a/TODO/easy.md b/TODO/easy.md @@ -1,16 +0,0 @@ -# Easy things to improve or add - -## Improvements - -* Silent batch mode without >>> -* Solutions should be shown sorted: by length first, then by normal moves - (no niss) first, then it depends on the step (e.g. EO by axis). - -## Old commands and steps - -* drcorners (solve corners after DR) -* Search and improve suboptimal subsequences - -## New commands - -* notation: show valid moves diff --git a/TODO/installation.md b/TODO/installation.md @@ -1,14 +0,0 @@ -# Simplify and improve installation - -## Tables - -* Make install should generate tables, or add a "make tables" target to - generate tables. -* Make tables should also check for existing files and remove old ones - (maybe more for nissy's command than for makefile). - -## Correctness - -* Add checksum for all generated files. -* Hard-code results? Check for compatibility problems between different OSes - and filesystems - but there should not be any, since we use stdint.h. diff --git a/TODO/new-feature-ideas.md b/TODO/new-feature-ideas.md @@ -1,32 +0,0 @@ -# Possible new features and improvements - -This file contains non-refined ideas. Once an idea gets refined, it will -get its own file and more details. - -## Steps - -* QTM solver -* 5-side solver (for robots) -* Other steps (cross, blocks, LSE...) - -## UX features - -* Save algs as variables and edit them (like in old nissy) -* Use a logging system for previously run commands, info, results... - (e.g. when solving with -c solutions are not shown, they can be logged here) -* Configurability: add an "alias" command, run config file at startup -* Input cube state directly instead of moves (ugly from command line / file) - -## Improvements - -* Optimal solver: when asking only for one solution, scan for upper bound in - parallel using a non-optimal (but fast) solver (e.g. twophase). -* Optimal solver: up to a small bound, try with a small pruning table. -* Optimal solver: start at different depths in parallel -* Multi-step solver: make more general - -## New features - -* Allow user to specify moveset manually (see issue \#5 on github) -* EO analysis (and also DR and HTR analysis): group similar EOs (Jay) -* HTR "maze" analysis? diff --git a/TODO/parser.md b/TODO/parser.md @@ -1,13 +0,0 @@ -# Improve command parser - -First, expand this TODO file to be more precise. - -## Refactor - -* The syntax of a command's options should be described by data, not by a - parser function. -* A single parser function can then parse options for all commands. - -## Usability - -* Better error messages! diff --git a/TODO/refactoring.md b/TODO/refactoring.md @@ -1,28 +0,0 @@ -# Refactoring - -## Init functions - -* All .h files should have a single init function. -* This function should initialize everything that this module needs, including - calling the init functions of the modules it depends on. -* To avoid multiple initialization of the same module, each should have a - static bool initialized variable. -* Everything that a module needs should be initialized by init(), avoid - initializing stuff when solving. Exception: pruning tables, move tables. -* Most functions should generate some tables and save them to disk. -* Init functions should have a consistent structure (e.g. the way they check - if the tables are already generated should be the same). - -## Cube types - -* Get rid of cubetype.h, split type definitionss into the other modules. -* Every type definition should be in the most fundamental module that needs it. - -## Code style - -* Stop declaring all variables at the beginning of a function. -* Remove variable names from prototypes. -* Sort function implementations alphabetically, ignore static vs non static. -* Rename functions and variable to have a consistent naming scheme. -* Functions that copy data: swap src and dest, follow memcpy standard. -* Read style(9) and decide what to implement. diff --git a/TODO/testing.md b/TODO/testing.md @@ -1,6 +0,0 @@ -# Testing - -## Write tests - -* Write tests for each module. Some of might require refactoring (this is - a good thing!) diff --git a/TODO/webapp.md b/TODO/webapp.md @@ -1,19 +0,0 @@ -# Towards a nissy webapp - -## Architecture - -* Split in client / server. -* Server can load and keep in memory all the tables, client(s) send messages to - the server to run commands. -* Use UNIX sockets only first, maybe later try WinSock. - -## Simple webapp - -* Investigate how to use fastcgi, try simple program first. -* Decide what limits to put in terms of resources and write a "filter" script - to block big requests (maybe use a timeout). - -## Advanced webapp - -* Use cubing.js for nice graphics. -* Port it to a graphical desktop version too. diff --git a/old/maybe-useful-coord.c b/old/maybe-useful-coord.c @@ -1,181 +0,0 @@ -int -array_ep_to_epos(int *ep, int *ss) -{ - int epos[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - int eps[4]; - int i, j, is; - - for (i = 0, is = 0; i < 12; i++) { - for (j = 0; j < 4; j++) { - if (ep[i] == ss[j]) { - eps[is++] = j; - epos[i] = 1; - } - } - } - - for (i = 0; i < 4; i++) - swap(&epos[ss[i]], &epos[i+8]); - - return 24 * subset_to_index(epos, 12, 4) + perm_to_index(eps, 4); -} - -void -epos_to_compatible_ep(int epos, int *ep, int *ss) -{ - int i, j, k, other[8]; - bool flag; - - for (i = 0; i < 12; i++) - ep[i] = -1; - - epos_to_partial_ep(epos, ep, ss); - - for (i = 0, j = 0; i < 12; i++) { - flag = false; - for (k = 0; k < 4; k++) - flag = flag || (i == ss[k]); - if (!flag) - other[j++] = i; - } - - for (i = 0, j = 0; i < 12; i++) - if (ep[i] == -1) - ep[i] = other[j++]; -} - -void -epos_to_partial_ep(int epos, int *ep, int *ss) -{ - int i, is, eposs[12], eps[4]; - - index_to_perm(epos % FACTORIAL4, 4, eps); - index_to_subset(epos / FACTORIAL4, 12, 4, eposs); - - for (i = 0; i < 4; i++) - swap(&eposs[ss[i]], &eposs[i+8]); - - for (i = 0, is = 0; i < 12; i++) - if (eposs[i]) - ep[i] = ss[eps[is++]]; -} - -void -fix_eorleoud(CubeArray *arr) -{ - int i; - - for (i = 0; i < 12; i++) { - if ((edge_slice(i) == 0 && edge_slice(arr->ep[i]) != 0) || - (edge_slice(i) != 0 && edge_slice(arr->ep[i]) == 0)) { - arr->eorl[i] = 1 - arr->eofb[i]; - } else { - arr->eorl[i] = arr->eofb[i]; - } - - if ((edge_slice(i) == 2 && edge_slice(arr->ep[i]) != 2) || - (edge_slice(i) != 2 && edge_slice(arr->ep[i]) == 2)) { - arr->eoud[i] = 1 - arr->eofb[i]; - } else { - arr->eoud[i] = arr->eofb[i]; - } - } -} - -void -fix_cofbcorl(CubeArray *arr) -{ - int i; - - for (i = 0; i < 8; i++) { - if (i % 2 == arr->cp[i] % 2) { - arr->cofb[i] = arr->coud[i]; - arr->corl[i] = arr->coud[i]; - } else { - if (arr->cp[i] % 2 == 0) { - arr->cofb[i] = (arr->coud[i]+1)%3; - arr->corl[i] = (arr->coud[i]+2)%3; - } else { - arr->cofb[i] = (arr->coud[i]+2)%3; - arr->corl[i] = (arr->coud[i]+1)%3; - } - } - } -} - -Cube -admissible_ep(Cube cube, PieceFilter f) -{ - CubeArray *arr = new_cubearray(cube, f); - Cube ret; - bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - int i, j; - - for (i = 0; i < 12; i++) - if (arr->ep[i] != -1) - used[arr->ep[i]] = true; - - for (i = 0, j = 0; i < 12; i++) { - for ( ; j < 11 && used[j]; j++); - if (arr->ep[i] == -1) - arr->ep[i] = j++; - } - - ret = arrays_to_cube(arr, pf_ep); - free_cubearray(arr, f); - - return ret; -} - -int -edge_slice(Edge e) { - if (e < 0 || e > 11) - return -1; - - if (e == FR || e == FL || e == BL || e == BR) - return 0; - if (e == UR || e == UL || e == DR || e == DL) - return 1; - - return 2; -} - -int -piece_orientation(Cube cube, int piece, char *orientation) -{ - int arr[12], n, b, x; - - if (!strcmp(orientation, "eofb")) { - x = cube.eofb; - n = 12; - b = 2; - } else if (!strcmp(orientation, "eorl")) { - x = cube.eorl; - n = 12; - b = 2; - } else if (!strcmp(orientation, "eoud")) { - x = cube.eoud; - n = 12; - b = 2; - } else if (!strcmp(orientation, "coud")) { - x = cube.coud; - n = 8; - b = 3; - } else if (!strcmp(orientation, "corl")) { - x = cube.corl; - n = 8; - b = 3; - } else if (!strcmp(orientation, "cofb")) { - x = cube.cofb; - n = 8; - b = 3; - } else { - return -1; - } - - int_to_sum_zero_array(x, b, n, arr); - if (piece < n) - return arr[piece]; - - return -1; -} diff --git a/old/maybe-useful-steps.c b/old/maybe-useful-steps.c @@ -1,1111 +0,0 @@ -#include "steps.h" - -#define UPDATECHECKSTOP(a, b, c) if ((a=(MAX((a),(b))))>(c)) return (a); - -static int estimate_stepalt(StepAlt *a, uint64_t *ind); - -/* Checkers and validators ***************************************************/ - -/* TODO: these should be with Cube *cube (and all need to change) */ -/* Maybe move them to cube.c */ -static bool check_centers(Cube cube); -static bool check_coud_HTM(Cube cube); -static bool check_coud_URF(Cube cube); -static bool check_corners_HTM(Cube cube); -static bool check_corners_URF(Cube cube); -static bool check_cornershtr(Cube cube); -static bool check_eofb(Cube cube); -static bool check_drud(Cube cube); -static bool check_htr(Cube cube); - -static bool always_valid(Alg *alg); -static bool validate_singlecw_ending(Alg *alg); - -/* Messages for when cube is not ready ***************************************/ - -static char check_centers_msg[100] = "cube must be oriented (centers solved)"; -static char check_eo_msg[100] = "EO must be solved on given axis"; -static char check_dr_msg[100] = "DR must be solved on given axis"; -static char check_htr_msg[100] = "HTR must be solved"; -static char check_drany_msg[100] = "DR must be solved on at least one axis"; - -/* Steps *********************************************************************/ - -/* Optimal solvers *******************/ - -Step -optimal_HTM = { - .shortname = "optimal", - .name = "Optimal solve (in HTM)", - - .final = true, - .is_done = is_solved, - .estimate = estimate_nxopt31_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = always_valid, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, - .ntables = 2, -}; - -Step -optimal_light_HTM = { - .shortname = "light", - .name = "Optimal solve (in HTM), small table (500Mb RAM total)", - - .final = true, - .is_done = is_solved, - .estimate = estimate_light_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = always_valid, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_drud_sym16_HTM, &pd_corners_HTM}, - .ntables = 2, -}; - -/* Optimal after EO ******************/ - -Step -eofin_eo = { - .shortname = "eofin", - .name = "Optimal solve after EO without breaking EO (detected)", - - .final = true, - .is_done = is_solved, - .estimate = estimate_nxopt31_HTM, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = always_valid, - .moveset = &moveset_eofb, - - .detect = detect_pretrans_eofb, - - .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, - .ntables = 2, -}; - -Step -eofbfin_eofb = { - .shortname = "eofbfin", - .name = "Optimal after EO on F/B without breaking EO", - - .final = true, - .is_done = is_solved, - .estimate = estimate_nxopt31_HTM, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = always_valid, - .moveset = &moveset_eofb, - - .pre_trans = uf, - - .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, - .ntables = 2, -}; - -Step -eorlfin_eorl = { - .shortname = "eorlfin", - .name = "Optimal after EO on R/L without breaking EO", - - .final = true, - .is_done = is_solved, - .estimate = estimate_nxopt31_HTM, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = always_valid, - .moveset = &moveset_eofb, - - .pre_trans = ur, - - .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, - .ntables = 2, -}; - -Step -eoudfin_eoud = { - .shortname = "eoudfin", - .name = "Optimal after EO on U/D without breaking EO", - - .final = true, - .is_done = is_solved, - .estimate = estimate_nxopt31_HTM, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = always_valid, - .moveset = &moveset_eofb, - - .pre_trans = fd, - - .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, - .ntables = 2, -}; - -/* EO steps **************************/ -Step -eoany_HTM = { - .shortname = "eo", - .name = "EO on any axis", - - .final = false, - .is_done = check_eofb, - .estimate = estimate_eofb_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .detect = detect_pretrans_void_3axis, - - .tables = {&pd_eofb_HTM}, - .ntables = 1, -}; - -Step -eofb_HTM = { - .shortname = "eofb", - .name = "EO on F/B", - - .final = false, - .is_done = check_eofb, - .estimate = estimate_eofb_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_eofb_HTM}, - .ntables = 1, -}; - -Step -eorl_HTM = { - .shortname = "eorl", - .name = "EO on R/L", - - .final = false, - .is_done = check_eofb, - .estimate = estimate_eofb_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = ur, - - .tables = {&pd_eofb_HTM}, - .ntables = 1, -}; - -Step -eoud_HTM = { - .shortname = "eoud", - .name = "EO on U/D", - - .final = false, - .is_done = check_eofb, - .estimate = estimate_eofb_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = fd, - - .tables = {&pd_eofb_HTM}, - .ntables = 1, -}; - -/* CO steps **************************/ -Step -coany_HTM = { - .shortname = "co", - .name = "CO on any axis", - - .final = false, - .is_done = check_coud_HTM, - .estimate = estimate_coud_HTM, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .detect = detect_pretrans_void_3axis, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -coud_HTM = { - .shortname = "coud", - .name = "CO on U/D", - - .final = false, - .is_done = check_coud_HTM, - .estimate = estimate_coud_HTM, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -corl_HTM = { - .shortname = "corl", - .name = "CO on R/L", - - .final = false, - .is_done = check_coud_HTM, - .estimate = estimate_coud_HTM, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = rf, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -cofb_HTM = { - .shortname = "cofb", - .name = "CO on F/B", - - .final = false, - .is_done = check_coud_HTM, - .estimate = estimate_coud_HTM, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = fd, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -coany_URF = { - .shortname = "co-URF", - .name = "CO any axis (URF moveset)", - - .final = false, - .is_done = check_coud_URF, - .estimate = estimate_coud_URF, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_URF, - - .detect = detect_pretrans_void_3axis, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -coud_URF = { - .shortname = "coud-URF", - .name = "CO on U/D (URF moveset)", - - .final = false, - .is_done = check_coud_URF, - .estimate = estimate_coud_URF, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_URF, - - .pre_trans = uf, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -corl_URF = { - .shortname = "corl-URF", - .name = "CO on R/L (URF moveset)", - - .final = false, - .is_done = check_coud_URF, - .estimate = estimate_coud_URF, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_URF, - - .pre_trans = rf, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -Step -cofb_URF = { - .shortname = "cofb-URF", - .name = "CO on F/B (URF moveset)", - - .final = false, - .is_done = check_coud_URF, - .estimate = estimate_coud_URF, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_URF, - - .pre_trans = fd, - - .tables = {&pd_coud_HTM}, - .ntables = 1, -}; - -/* Misc corner steps *****************/ -Step -cornershtr_HTM = { - .shortname = "chtr", - .name = "Solve corners to HTR state", - - .final = false, - .is_done = check_cornershtr, - .estimate = estimate_cornershtr_HTM, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_cornershtr_HTM}, - .ntables = 1, -}; - -Step -cornershtr_URF = { - .shortname = "chtr-URF", - .name = "Solve corners to HTR state (URF moveset)", - - .final = false, - .is_done = check_cornershtr, - .estimate = estimate_cornershtr_URF, - .ready = NULL, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_URF, - - .pre_trans = uf, - - .tables = {&pd_cornershtr_HTM}, - .ntables = 1, -}; - -Step -corners_HTM = { - .shortname = "corners", - .name = "Solve corners", - - .final = true, - .is_done = check_corners_HTM, - .estimate = estimate_corners_HTM, - .ready = NULL, - .is_valid = always_valid, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_corners_HTM}, - .ntables = 1, -}; - -Step -corners_URF = { - .shortname = "corners-URF", - .name = "Solve corners (URF moveset)", - - .final = true, /* TODO: check if this works with reorient */ - .is_done = check_corners_URF, - .estimate = estimate_corners_URF, - .ready = NULL, - .is_valid = always_valid, - .moveset = &moveset_URF, - - .pre_trans = uf, - - .tables = {&pd_corners_HTM}, - .ntables = 1, -}; - -/* DR steps **************************/ -Step -drany_HTM = { - .shortname = "dr", - .name = "DR on any axis", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .detect = detect_pretrans_void_3axis, - - .tables = {&pd_drud_sym16_HTM}, - .ntables = 1, -}; - -Step -drud_HTM = { - .shortname = "drud", - .name = "DR on U/D", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = uf, - - .tables = {&pd_drud_sym16_HTM}, - .ntables = 1, -}; - -Step -drrl_HTM = { - .shortname = "drrl", - .name = "DR on R/L", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = rf, - - .tables = {&pd_drud_sym16_HTM}, - .ntables = 1, -}; - -Step -drfb_HTM = { - .shortname = "drfb", - .name = "DR on F/B", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_HTM, - .ready = check_centers, - .ready_msg = check_centers_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_HTM, - - .pre_trans = fd, - - .tables = {&pd_drud_sym16_HTM}, - .ntables = 1, -}; - -/* DR from EO */ -Step -dr_eo = { - .shortname = "dr-eo", - .name = "DR without breaking EO (automatically detected)", - - .final = false, - .is_done = check_drud, - .estimate = estimate_dr_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .detect = detect_pretrans_eofb, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -dr_eofb = { - .shortname = "dr-eofb", - .name = "DR on U/D or R/L without breaking EO on F/B", - - .final = false, - .is_done = check_drud, - .estimate = estimate_dr_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = uf, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -dr_eorl = { - .shortname = "dr-eorl", - .name = "DR on U/D or F/B without breaking EO on R/L", - - .final = false, - .is_done = check_drud, - .estimate = estimate_dr_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = ur, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -dr_eoud = { - .shortname = "dr-eoud", - .name = "DR on R/L or F/B without breaking EO on U/D", - - .final = false, - .is_done = check_drud, - .estimate = estimate_dr_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = fd, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -drud_eofb = { - .shortname = "drud-eofb", - .name = "DR on U/D without breaking EO on F/B", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = uf, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -drrl_eofb = { - .shortname = "drrl-eofb", - .name = "DR on R/L without breaking EO on F/B", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = rf, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -drud_eorl = { - .shortname = "drud-eorl", - .name = "DR on U/D without breaking EO on R/L", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = ur, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -drfb_eorl = { - .shortname = "drfb-eorl", - .name = "DR on F/B without breaking EO on R/L", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = fr, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -drfb_eoud = { - .shortname = "drfb-eoud", - .name = "DR on F/B without breaking EO on U/D", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = fd, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -Step -drrl_eoud = { - .shortname = "drrl-eoud", - .name = "DR on R/L without breaking EO on U/D", - - .final = false, - .is_done = check_drud, - .estimate = estimate_drud_eofb, - .ready = check_eofb, - .ready_msg = check_eo_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_eofb, - - .pre_trans = rd, - - .tables = {&pd_drud_eofb}, - .ntables = 1, -}; - -/* DR finish steps */ -Step -dranyfin_DR = { - .shortname = "drfin", - .name = "DR finish on any axis without breaking DR", - - .final = true, - .is_done = is_solved, - .estimate = estimate_drudfin_drud, - .ready = check_drud, - .ready_msg = check_drany_msg, - .is_valid = always_valid, - .moveset = &moveset_drud, - - .detect = detect_pretrans_drud, - - .tables = {&pd_drudfin_noE_sym16_drud}, - .ntables = 1, -}; - -Step -drudfin_drud = { - .shortname = "drudfin", - .name = "DR finish on U/D without breaking DR", - - .final = true, - .is_done = is_solved, - .estimate = estimate_drudfin_drud, - .ready = check_drud, - .ready_msg = check_dr_msg, - .is_valid = always_valid, - .moveset = &moveset_drud, - - .pre_trans = uf, - - .tables = {&pd_drudfin_noE_sym16_drud}, - .ntables = 1, -}; - -Step -drrlfin_drrl = { - .shortname = "drrlfin", - .name = "DR finish on R/L without breaking DR", - - .final = true, - .is_done = is_solved, - .estimate = estimate_drudfin_drud, - .ready = check_drud, - .ready_msg = check_dr_msg, - .is_valid = always_valid, - .moveset = &moveset_drud, - - .pre_trans = rf, - - .tables = {&pd_drudfin_noE_sym16_drud}, - .ntables = 1, -}; - -Step -drfbfin_drfb = { - .shortname = "drfbfin", - .name = "DR finish on F/B without breaking DR", - - .final = true, - .is_done = is_solved, - .estimate = estimate_drudfin_drud, - .ready = check_drud, - .ready_msg = check_dr_msg, - .is_valid = always_valid, - .moveset = &moveset_drud, - - .pre_trans = fd, - - .tables = {&pd_drudfin_noE_sym16_drud}, - .ntables = 1, -}; - -/* HTR from DR */ -Step -htr_any = { - .shortname = "htr", - .name = "HTR from DR", - - .final = false, - .is_done = check_htr, - .estimate = estimate_htr_drud, - .ready = check_drud, - .ready_msg = check_drany_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_drud, - - .detect = detect_pretrans_drud, - - .tables = {&pd_htr_drud}, - .ntables = 1, -}; - -Step -htr_drud = { - .shortname = "htr-drud", - .name = "HTR from DR on U/D", - - .final = false, - .is_done = check_htr, - .estimate = estimate_htr_drud, - .ready = check_drud, - .ready_msg = check_dr_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_drud, - - .pre_trans = uf, - - .tables = {&pd_htr_drud}, - .ntables = 1, -}; - -Step -htr_drrl = { - .shortname = "htr-drrl", - .name = "HTR from DR on R/L", - - .final = false, - .is_done = check_htr, - .estimate = estimate_htr_drud, - .ready = check_drud, - .ready_msg = check_dr_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_drud, - - .pre_trans = rf, - - .tables = {&pd_htr_drud}, - .ntables = 1, -}; - -Step -htr_drfb = { - .shortname = "htr-drfb", - .name = "HTR from DR on F/B", - - .final = false, - .is_done = check_htr, - .estimate = estimate_htr_drud, - .ready = check_drud, - .ready_msg = check_dr_msg, - .is_valid = validate_singlecw_ending, - .moveset = &moveset_drud, - - .pre_trans = fd, - - .tables = {&pd_htr_drud}, - .ntables = 1, -}; - -/* HTR finish */ -Step -htrfin_htr = { - .shortname = "htrfin", - .name = "HTR finish without breaking HTR", - - .final = true, - .is_done = is_solved, - .estimate = estimate_htrfin_htr, - .ready = check_htr, - .ready_msg = check_htr_msg, - .is_valid = always_valid, - .moveset = &moveset_htr, - - .pre_trans = uf, - - .tables = {&pd_htrfin_htr}, - .ntables = 1, -}; - -Step *steps[] = { - &optimal_HTM, /* first is default */ - &optimal_light_HTM, - - &eofin_eo, - &eofbfin_eofb, - &eorlfin_eorl, - &eoudfin_eoud, - - &eoany_HTM, - &eofb_HTM, - &eorl_HTM, - &eoud_HTM, - - &coany_HTM, - &coud_HTM, - &corl_HTM, - &cofb_HTM, - - &coany_URF, - &coud_URF, - &corl_URF, - &cofb_URF, - - &drany_HTM, - &drud_HTM, - &drrl_HTM, - &drfb_HTM, - - &dr_eo, - &dr_eofb, - &dr_eorl, - &dr_eoud, - &drud_eofb, - &drrl_eofb, - &drud_eorl, - &drfb_eorl, - &drfb_eoud, - &drrl_eoud, - - &dranyfin_DR, - &drudfin_drud, - &drrlfin_drrl, - &drfbfin_drfb, - - &htr_any, - &htr_drud, - &htr_drrl, - &htr_drfb, - - &htrfin_htr, - - &cornershtr_HTM, - &cornershtr_URF, - &corners_HTM, - &corners_URF, - - NULL -}; - -/* Checkers and validators ***************************************************/ - -static bool -check_centers(Cube cube) -{ - return cube.cpos == 0; -} - -static bool -check_coud_HTM(Cube cube) -{ - return cube.coud == 0; -} - -static bool -check_coud_URF(Cube cube) -{ - Cube c2, c3; - - c2 = apply_move(z, cube); - c3 = apply_move(x, cube); - - return cube.coud == 0 || c2.coud == 0 || c3.coud == 0; -} - -static bool -check_corners_URF(Cube cube) -{ - Cube c; - Trans i; - - for (i = 0; i < NROTATIONS; i++) { - c = apply_alg(rotation_alg(i), cube); - if (c.cp && c.coud) - return true; - } - - return false; -} - -static bool -check_corners_HTM(Cube cube) -{ - return cube.cp == 0 && cube.coud == 0; -} - -static bool -check_cornershtr(Cube cube) -{ - return coord_cornershtr.index(cube) == 0; -} - -static bool -check_eofb(Cube cube) -{ - return cube.eofb == 0; -} - -static bool -check_drud(Cube cube) -{ - return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; -} - -static bool -check_htr(Cube cube) -{ - return check_drud(cube) && coord_htr_drud.index(cube) == 0; -} - -static bool -always_valid(Alg *alg) -{ - return true; -} - -static bool -validate_singlecw_ending(Alg *alg) -{ - int i; - bool nor, inv; - Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE; - - for (i = 0; i < alg->len; i++) { - if (alg->inv[i]) { - l2i = l1i; - l1i = alg->move[i]; - } else { - l2 = l1; - l1 = alg->move[i]; - } - } - - nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2)); - inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i)); - - return nor && inv; -} - -/* Public functions **********************************************************/ - -void -compute_ind(StepAlt *a, Cube *cube, uint64_t *ind) -{ - -} - -int -estimate_stepalt(StepAlt *a, uint64_t *ind) -{ - int i, ret, est[a->n_coord]; - - for (i = 0; i < a->n_coord; i++) { - est[i] = ptableval(a->pd[i], ind[i]); - if (est[i] == 0 && a->compact_pd[i]) - est[i] = ptableval(a->fallback_pd, ind[i] / a->fbmod); - } - - for (i = 0; i < a->n_dbtrick; i++) - if (est[a->dbtrick[i][0]] == est[a->dbtrick[i][1]] && - est[a->dbtrick[i][0]] == est[a->dbtrick[i][2]]) - est[a->dbtrick[i][0]] += 1; - - for (i = 0, ret = -1; i < a->n_coord; i++) - ret = max(ret, est[i]); - - return ret; -} - -void -prepare_step(Step *step, SolveOptions *opts) -{ - int i, j; - PDGenData pdg; - StepAlt *a; - - init_moveset(step->moveset); - pdg.moveset = step->moveset; - - for (i = 0; step->alt[i] != NULL; i++) { - a = step->alt[i]; - for (j = 0; j < a->n_coord; j++) { - gen_coord(a->coord[j]); - - pdg.coord = a->coord[j]; - pdg.compact = a->compact[j]; - pdg.pd = NULL; - - a->pd[j] = genptable(&pdg, opts->nthreads); - - if (a->compact_pd[j]) { - gen_coord(a->fallback_coord[j]); - - pdg.coord = a->fallback_coord[j]; - pdg.compact = false; - pdg.pd = NULL; - - a->fallback_pd[j] = - step->genptable(&pdg, opts->nthreads); - } - } - } -} diff --git a/old/solve_old.c b/old/solve_old.c @@ -1,447 +0,0 @@ -#define SOLVE_C - -#include "solve.h" - -/* Local functions ***********************************************************/ - -static void copy_dfsarg(DfsArg *src, DfsArg *dst); -static void dfs(DfsArg *arg); -static void dfs_add_sol(DfsArg *arg); -static void dfs_niss(DfsArg *arg); -static bool dfs_move_checkstop(DfsArg *arg); -static void * instance_thread(void *arg); -static void multidfs(DfsArg *arg); -static bool niss_makes_sense(DfsArg *arg); -static bool solvestop(int d, int op, SolveOptions *opts, AlgList *sols); - -/* Local functions ***********************************************************/ - -static void -copy_dfsarg(DfsArg *src, DfsArg *dst) -{ - int i; - - dst->cube = src->cube; - dst->t = src->t; - dst->s = src->s; - dst->opts = src->opts; - dst->d = src->d; - dst->bound = src->bound; /* In theory not needed */ - dst->niss = src->niss; - dst->sols = src->sols; - dst->sols_mutex = src->sols_mutex; - dst->current_alg = src->current_alg; - - for (i = 0; i < src->s->n_coord; i++) { - dst->ind[i].val = src->ind[i].val; - dst->ind[i].t = src->ind[i].t; - } - - src->s->copy_extra(src, dst); -} - -static void -dfs(DfsArg *arg) -{ - int i; - Move m, l0, l1; - DfsArg newarg; - - if (dfs_move_checkstop(arg)) - return; - - if (arg->bound == 0) { - if (arg->current_alg->len == arg->d) - dfs_add_sol(arg); - return; - } - - for (i = 0; arg->s->moveset->sorted_moves[i] != NULLMOVE; i++) { - m = arg->s->moveset->sorted_moves[i]; - if (arg->s->moveset->can_append(arg->current_alg, m, arg->niss) - && compare_last(arg->current_alg, m, arg->niss) >= 0) { - copy_dfsarg(arg, &newarg); - append_move(arg->current_alg, m, newarg.niss); - dfs(&newarg); - remove_last_move(arg->current_alg); - } - } - - if (niss_makes_sense(arg)) - dfs_niss(arg); -} - -static void -dfs_add_sol(DfsArg *arg) -{ - bool valid, accepted, nisscanc; - - valid = arg->s->is_valid==NULL || arg->s->is_valid(arg->current_alg); - accepted = valid || arg->opts->all; - nisscanc = arg->s->final && - arg->s->moveset->cancel_niss(arg->current_alg); - - if (accepted && !nisscanc) { - pthread_mutex_lock(arg->sols_mutex); - - if (arg->sols->len < arg->opts->max_solutions) { - append_alg(arg->sols, arg->current_alg); - transform_alg( - inverse_trans(arg->t), arg->sols->last->alg); - if (arg->opts->verbose) - print_alg(arg->sols->last->alg, false); - } - - pthread_mutex_unlock(arg->sols_mutex); - } -} - -static void -dfs_niss(DfsArg *arg) -{ - DfsArg newarg; - Alg *inv; - Cube *c; - - copy_dfsarg(arg, &newarg); - - /* Invert current alg and scramble */ - newarg.cube = malloc(sizeof(Cube)); - inv = inverse_alg(arg->current_alg); - c = malloc(sizeof(Cube)); - make_solved(newarg.cube); - apply_alg(inv, newarg.cube); - copy_cube(arg->cube, c); - invert_cube(c); - compose(c, newarg.cube); - - /* New indexes */ - compute_ind(newarg.s, newarg.cube, newarg.ind); - - newarg.niss = !(arg->niss); - - dfs(&newarg); - - free_alg(inv); - free(c); - free(newarg.cube); -} - -static bool -dfs_move_checkstop(DfsArg *arg) -{ - int i, goal, nsols; - Move mm; - Trans tt = uf; /* Avoid uninitialized warning */ - - /* Moving and computing bound */ - arg->bound = 0; - goal = arg->d - arg->current_alg->len; - for (i = 0; i < arg->s->n_coord; i++) { - if (arg->last[0] != NULLMOVE) { - mm = transform_move(arg->ind[i].t, arg->last[0]); - arg->ind[i].val = move_coord(arg->s->coord[i], - mm, arg->ind[i].val, &tt); - arg->ind[i].t = transform_trans(tt, arg->ind[i].t); - } - - arg->bound = - MAX(arg->bound, ptableval(arg->s->pd[i], arg->ind[i].val)); - if (arg->opts->can_niss && !arg->niss) - arg->bound = MIN(1, arg->bound); - - if (arg->bound > goal) - return true; - } - - pthread_mutex_lock(arg->sols_mutex); - nsols = arg->sols->len; - pthread_mutex_unlock(arg->sols_mutex); - - return nsols >= arg->opts->max_solutions; -} - -static void * -instance_thread(void *arg) -{ - bool b, inv; - Cube c; - Move m; - ThreadDataSolve *td; - AlgListNode *node; - DfsArg darg; - - td = (ThreadDataSolve *)arg; - - while (1) { - b = false; - - pthread_mutex_lock(td->start_mutex); - if ((node = *(td->node)) == NULL) - b = true; - else - *(td->node) = (*(td->node))->next; - pthread_mutex_unlock(td->start_mutex); - - if (b) - break; - - inv = node->alg->inv[0]; - m = node->alg->move[0]; - - copy_cube(td->arg.cube, &c); - if (inv) - invert_cube(&c); - - copy_dfsarg(&td->arg, &darg); - compute_ind(td->arg.s, &c, darg.ind); - darg.cube = &c; - - darg.niss = inv; - darg.current_alg = new_alg(""); - append_move(darg.current_alg, m, inv); - - dfs(&darg); - - free_alg(darg.current_alg); - } - - return NULL; -} - -static void -multidfs(DfsArg *arg) -{ - int i; - Cube local_cube; - Alg *alg; - AlgList *start; - AlgListNode **node; - pthread_t t[arg->opts->nthreads]; - ThreadDataSolve td[arg->opts->nthreads]; - pthread_mutex_t *start_mutex, *sols_mutex; - - node = malloc(sizeof(AlgListNode *)); - start_mutex = malloc(sizeof(pthread_mutex_t)); - sols_mutex = malloc(sizeof(pthread_mutex_t)); - - start = new_alglist(); - pthread_mutex_init(start_mutex, NULL); - pthread_mutex_init(sols_mutex, NULL); - - for (i = 0; arg->s->moveset->sorted_moves[i] != NULLMOVE; i++) { - alg = new_alg(""); - append_move(alg, arg->s->moveset->sorted_moves[i], false); - append_alg(start, alg); - if (arg->opts->can_niss && !arg->s->final) { - alg->inv[0] = true; - append_alg(start, alg); - } - free_alg(alg); - } - *node = start->first; - - copy_cube(arg->cube, &local_cube); - - for (i = 0; i < arg->opts->nthreads; i++) { - copy_dfsarg(arg, &(td[i].arg)); - td[i].arg.cube = &local_cube; - td[i].arg.sols_mutex = sols_mutex; - - td[i].thid = i; - td[i].start = start; - td[i].node = node; - td[i].start_mutex = start_mutex; - - pthread_create(&t[i], NULL, instance_thread, &td[i]); - } - - for (i = 0; i < arg->opts->nthreads; i++) - pthread_join(t[i], NULL); - - free_alglist(start); - free(node); - free(start_mutex); - free(sols_mutex); -} - -static bool -niss_makes_sense(DfsArg *arg) -{ - Move m, mm; - uint64_t u; - int i; - - if (arg->s->final || arg->niss || !arg->opts->can_niss) - return false; - - if (arg->current_alg->len_normal == 0) - return true; - - m = inverse_move(arg->last[0]); - for (i = 0; i < arg->s->n_coord; i++) { - mm = transform_move(arg->ind[i].t, m); - u = move_coord(arg->s->coord[i], mm, 0, NULL); - if (ptableval(arg->s->pd[i], u) > 0) - return true; - } - - return false; -} - -static bool -solvestop(int d, int op, SolveOptions *opts, AlgList *sols) -{ - bool opt_done, max_moves_exceeded, max_sols_exceeded; - - opt_done = opts->optimal != -1 && op != -1 && d > opts->optimal + op; - max_moves_exceeded = d > opts->max_moves; - max_sols_exceeded = sols->len >= opts->max_solutions; - - return opt_done || max_moves_exceeded || max_sols_exceeded; -} - -/* Public functions **********************************************************/ - -AlgList * -solve(Cube *cube, ChoiceStep *cs, SolveOptions *opts) -{ - int i, j, d, op, est; - bool ready[99], one_ready, zerosol; - Movable ind[99][10]; - AlgList *s; - Cube *c[99]; - DfsArg arg[99]; - - prepare_cs(cs, opts); - s = new_alglist(); - - for (i = 0, one_ready = false; cs->step[i] != NULL; i++) { - c[i] = malloc(sizeof(Cube)); - copy_cube(cube, c[i]); - apply_trans(cs->t[i], c[i]); - - arg[i].cube = c[i]; - arg[i].t = cs->t[i]; - arg[i].s = cs->step[i]; - arg[i].opts = opts; - arg[i].sols = s; - - if ((ready[i] = cs->step[i]->ready(c[i]))) { - one_ready = true; - /* Only for local use for 0 moves solutions */ - compute_ind(cs->step[i], c[i], ind[i]); - } - } - if (!one_ready) { - fprintf(stderr, "Cube not ready for solving step: "); - fprintf(stderr, "%s\n", cs->ready_msg); - return s; - } - - /* If the empty moves sequence is a solution for one of the - * alternatives, all longer solutions will be discarded, so we may - * just set its ready[] value to false. If the solution is accepted - * we append it and start searching from d = 1. */ - for (i = 0, zerosol = false; cs->step[i] != NULL; i++) { - if (ready[i]) { - est = 0; - for (j = 0; j < cs->step[i]->n_coord; j++) - est = MAX(est, ptableval(cs->step[i]->pd[j], - ind[i][j].val)); - if (est == 0) { - ready[i] = false; - zerosol = true; - } - } - } - if (zerosol && opts->min_moves == 0) { - append_alg(s, new_alg("")); - opts->min_moves = 1; - if (opts->verbose) - printf("Step is already solved" - "(empty alg is a solution)\n"); - } - - for (d = opts->min_moves, op = -1; !solvestop(d, op, opts, s); d++) { - if (opts->verbose) - fprintf(stderr, "Searching depth %d\n", d); - - for (i=0; cs->step[i]!=NULL && !solvestop(d,op,opts,s); i++) { - if (!ready[i]) - continue; - - arg[i].d = d; - multidfs(&arg[i]); - - if (s->len > 0 && op == -1) - op = d; - } - } - - for (i = 0; cs->step[i] != NULL; i++) - free(c[i]); - - return s; -} - -/* TODO: make more general! */ -Alg * -solve_2phase(Cube *cube, int nthreads) -{ - int bestlen, newb; - Alg *bestalg, *ret; - AlgList *sols1, *sols2; - AlgListNode *i; - Cube c; - SolveOptions opts1, opts2; - - opts1.min_moves = 0; - opts1.max_moves = 13; - opts1.max_solutions = 20; - opts1.nthreads = nthreads; - opts1.optimal = 3; - opts1.can_niss = false; - opts1.verbose = false; - opts1.all = true; - - opts2.min_moves = 0; - opts2.max_moves = 19; - opts2.max_solutions = 1; - opts2.nthreads = nthreads; - opts2.can_niss = false; - opts2.verbose = false; - - /* We skip step1 if it is solved on U/D */ - if (check_drud(cube)) { - sols1 = new_alglist(); - append_alg(sols1, new_alg("")); - } else { - sols1 = solve(cube, &drud_HTM, &opts1); - } - bestalg = new_alg(""); - bestlen = 999; - for (i = sols1->first; i != NULL; i = i->next) { - copy_cube(cube, &c); - apply_alg(i->alg, &c); - sols2 = solve(&c, &dranyfin_DR, &opts2); - - if (sols2->len > 0) { - newb = i->alg->len + sols2->first->alg->len; - if (newb < bestlen) { - bestlen = newb; - copy_alg(i->alg, bestalg); - compose_alg(bestalg, sols2->first->alg); - } - } - - free_alglist(sols2); - } - - free_alglist(sols1); - - ret = cleanup(bestalg); - free_alg(bestalg); - - return ret; -} diff --git a/old/solve_old.h b/old/solve_old.h @@ -1,9 +0,0 @@ -#ifndef SOLVE_H -#define SOLVE_H - -#include "movesets.h" - -AlgList * solve(Cube *cube, ChoiceStep *cs, SolveOptions *opts); -Alg * solve_2phase(Cube *cube, int nthreads); - -#endif diff --git a/src/alg.c b/src/alg.c @@ -1,11 +1,112 @@ -#define ALG_C - #include "alg.h" +/* Local functions ***********************************************************/ + +static bool allowed_HTM(Move m); +static bool allowed_URF(Move m); +static bool allowed_eofb(Move m); +static bool allowed_drud(Move m); +static bool allowed_htr(Move m); +static bool allowed_next_HTM(Move l2, Move l1, Move m); static int axis(Move m); + static void free_alglistnode(AlgListNode *aln); static void realloc_alg(Alg *alg, int n); +/* Movesets ******************************************************************/ + +Moveset +moveset_HTM = { + .allowed = allowed_HTM, + .allowed_next = allowed_next_HTM, +}; + +Moveset +moveset_URF = { + .allowed = allowed_URF, + .allowed_next = allowed_next_HTM, +}; + +Moveset +moveset_eofb = { + .allowed = allowed_eofb, + .allowed_next = allowed_next_HTM, +}; + +Moveset +moveset_drud = { + .allowed = allowed_drud, + .allowed_next = allowed_next_HTM, +}; + +Moveset +moveset_htr = { + .allowed = allowed_htr, + .allowed_next = allowed_next_HTM, +}; + +static int nmoveset = 5; +static Moveset * all_ms[] = { + &moveset_HTM, + &moveset_URF, + &moveset_eofb, + &moveset_drud, + &moveset_htr, +}; + +/* Functions *****************************************************************/ + +static bool +allowed_HTM(Move m) +{ + return m >= U && m <= B3; +} + +static bool +allowed_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + +static bool +allowed_eofb(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || b == R || b == L || + ((b == F || b == B) && m == b+1); +} + +static bool +allowed_drud(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || + ((b == R || b == L || b == F || b == B) && m == b + 1); +} + +static bool +allowed_htr(Move m) +{ + Move b = base_move(m); + + return moveset_HTM.allowed(m) && m == b + 1; +} + +static bool +allowed_next_HTM(Move l2, Move l1, Move m) +{ + bool p, q; + + p = l1 != NULLMOVE && base_move(l1) == base_move(m); + q = l2 != NULLMOVE && base_move(l2) == base_move(m); + + return !(p || (commute(l1, l2) && q)); +} + void append_alg(AlgList *l, Alg *alg) { @@ -33,42 +134,30 @@ append_move(Alg *alg, Move m, bool inverse) alg->move[alg->len] = m; alg->inv [alg->len] = inverse; alg->len++; - - if (inverse) - alg->move_inverse[alg->len_inverse++] = m; - else - alg->move_normal[alg->len_normal++] = m; } static int axis(Move m) { - static int aux[] = { - [NULLMOVE] = 0, - - [U] = 1, [U2] = 1, [U3] = 1, - [D] = 1, [D2] = 1, [D3] = 1, - [Uw] = 1, [Uw2] = 1, [Uw3] = 1, - [Dw] = 1, [Dw2] = 1, [Dw3] = 1, - [E] = 1, [E2] = 1, [E3] = 1, - [y] = 1, [y2] = 1, [y3] = 1, - - [R] = 2, [R2] = 2, [R3] = 2, - [L] = 2, [L2] = 2, [L3] = 2, - [Rw] = 2, [Rw2] = 2, [Rw3] = 2, - [Lw] = 2, [Lw2] = 2, [Lw3] = 2, - [M] = 2, [M2] = 2, [M3] = 2, - [x] = 2, [x2] = 2, [x3] = 2, - - [F] = 3, [F2] = 3, [F3] = 3, - [B] = 3, [B2] = 3, [B3] = 3, - [Fw] = 3, [Fw2] = 3, [Fw3] = 3, - [Bw] = 3, [Bw2] = 3, [Bw3] = 3, - [S] = 3, [S2] = 3, [S3] = 3, - [z] = 3, [z2] = 3, [z3] = 3, - }; + if (m == NULLMOVE) + return 0; + + if (m >= U && m <= B3) + return (m-1)/6 + 1; + + if (m >= Uw && m <= Bw3) + return (m-1)/6 - 2; + + if (base_move(m) == E || base_move(m) == y) + return 1; + + if (base_move(m) == M || base_move(m) == x) + return 2; - return aux[m]; + if (base_move(m) == S || base_move(m) == z) + return 3; + + return -1; } Move @@ -86,32 +175,6 @@ commute(Move m1, Move m2) return axis(m1) == axis(m2); } -int -compare(Move m1, Move m2) -{ - if (!commute(m1, m2)) - return 0; - - return m1 < m2 ? 1 : -1; -} - -int -compare_last(Alg *alg, Move m, bool inverse) -{ - Move last; - int n; - - if (inverse) { - n = alg->len_inverse; - last = n > 0 ? alg->move_inverse[n-1] : NULLMOVE; - } else { - n = alg->len_normal; - last = n > 0 ? alg->move_normal[n-1] : NULLMOVE; - } - - return compare(last, m); -} - void compose_alg(Alg *alg1, Alg *alg2) { @@ -124,7 +187,7 @@ compose_alg(Alg *alg1, Alg *alg2) void copy_alg(Alg *src, Alg *dst) { - dst->len = dst->len_normal = dst->len_inverse = 0; + dst->len = 0; /* Overwrites */ compose_alg(dst, src); } @@ -156,6 +219,16 @@ free_alglistnode(AlgListNode *aln) free(aln); } +void +inplace(Alg * (*f)(Alg *), Alg *alg) +{ + Alg *aux; + + aux = f(alg); + copy_alg(aux, alg); + free(aux); +} + Alg * inverse_alg(Alg *alg) { @@ -211,14 +284,10 @@ new_alg(char *str) Move j, m; alg = malloc(sizeof(Alg)); - alg->allocated = 30; - alg->move = malloc(alg->allocated * sizeof(Move)); - alg->inv = malloc(alg->allocated * sizeof(bool)); - alg->move_normal = malloc(alg->allocated * sizeof(Move)); - alg->move_inverse = malloc(alg->allocated * sizeof(Move)); - alg->len = 0; - alg->len_normal = 0; - alg->len_inverse = 0; + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; niss = false; for (i = 0; str[i]; i++) { @@ -227,13 +296,13 @@ new_alg(char *str) if (str[i] == '(' && niss) { fprintf(stderr, "Error reading moves: nested ( )\n"); - alg->len = alg->len_normal = alg->len_inverse = 0; + alg->len = 0; return alg; } if (str[i] == ')' && !niss) { fprintf(stderr, "Error reading moves: unmatched )\n"); - alg->len = alg->len_normal = alg->len_inverse = 0; + alg->len = 0; return alg; } @@ -300,7 +369,7 @@ new_alg(char *str) if (niss) { fprintf(stderr, "Error reading moves: unmatched (\n"); - alg->len = alg->len_normal = alg->len_inverse = 0; + alg->len = 0; } return alg; @@ -385,25 +454,12 @@ realloc_alg(Alg *alg, int n) fprintf(stderr, "something might go wrong.\n"); } - alg->move = realloc(alg->move, n * sizeof(int)); - alg->inv = realloc(alg->inv, n * sizeof(int)); - alg->move_normal = realloc(alg->move_normal, n * sizeof(int)); - alg->move_inverse = realloc(alg->move_inverse, n * sizeof(int)); + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); alg->allocated = n; } void -remove_last_move(Alg *a) -{ - a->len--; - - if (a->inv[a->len]) - a->len_inverse--; - else - a->len_normal--; -} - -void swapmove(Move *m1, Move *m2) { Move aux; @@ -413,34 +469,6 @@ swapmove(Move *m1, Move *m2) *m2 = aux; } -char * -trans_string(Trans t) -{ - static char trans_string_aux[NTRANS][20] = { - [uf] = "uf", [ur] = "ur", [ub] = "ub", [ul] = "ul", - [df] = "df", [dr] = "dr", [db] = "db", [dl] = "dl", - [rf] = "rf", [rd] = "rd", [rb] = "rb", [ru] = "ru", - [lf] = "lf", [ld] = "ld", [lb] = "lb", [lu] = "lu", - [fu] = "fu", [fr] = "fr", [fd] = "fd", [fl] = "fl", - [bu] = "bu", [br] = "br", [bd] = "bd", [bl] = "bl", - - [uf_mirror] = "uf*", [ur_mirror] = "ur*", - [ub_mirror] = "ub*", [ul_mirror] = "ul*", - [df_mirror] = "df*", [dr_mirror] = "dr*", - [db_mirror] = "db*", [dl_mirror] = "dl*", - [rf_mirror] = "rf*", [rd_mirror] = "rd*", - [rb_mirror] = "rb*", [ru_mirror] = "ru*", - [lf_mirror] = "lf*", [ld_mirror] = "ld*", - [lb_mirror] = "lb*", [lu_mirror] = "lu*", - [fu_mirror] = "fu*", [fr_mirror] = "fr*", - [fd_mirror] = "fd*", [fl_mirror] = "fl*", - [bu_mirror] = "bu*", [br_mirror] = "br*", - [bd_mirror] = "bd*", [bl_mirror] = "bl*", - }; - - return trans_string_aux[t]; -} - Alg * unniss(Alg *alg) { @@ -449,11 +477,48 @@ unniss(Alg *alg) ret = new_alg(""); - for (i = 0; i < alg->len_normal; i++) - append_move(ret, alg->move_normal[i], false); + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + append_move(ret, alg->move[i], false); + + for (i = alg->len-1; i >= 0; i--) + if (alg->inv[i]) + append_move(ret, inverse_move(alg->move[i]), false); + + return ret; +} - for (i = 0; i < alg->len_inverse; i++) - append_move(ret, inverse_move(alg->move_inverse[i]), false); +void +init_moveset(Moveset *ms) +{ + int j; + uint64_t l, one; + Move m, l2, l1; + + one = 1; + + for (j = 0, m = U; m < NMOVES; m++) + if (ms->allowed(m)) + ms->sorted_moves[j++] = m; + ms->sorted_moves[j] = NULLMOVE; + + for (l1 = 0; l1 < NMOVES; l1++) { + for (l2 = 0; l2 < NMOVES; l2++) { + ms->mask[l2][l1] = 0; + for (l=0; ms->sorted_moves[l]!=NULLMOVE; l++) { + m = ms->sorted_moves[l]; + if (ms->allowed_next(l2, l1, m)) + ms->mask[l2][l1] |= (one<<m); + } + } + } +} - return ret; +void +init_all_movesets() +{ + int i; + + for (i = 0; i < nmoveset; i++) + init_moveset(all_ms[i]); } diff --git a/src/alg.h b/src/alg.h @@ -8,28 +8,36 @@ #include "cubetypes.h" #include "utils.h" +extern Moveset moveset_HTM; +extern Moveset moveset_URF; +extern Moveset moveset_eofb; +extern Moveset moveset_drud; +extern Moveset moveset_htr; + void append_alg(AlgList *l, Alg *alg); void append_move(Alg *alg, Move m, bool inverse); Move base_move(Move m); -int compare(Move m1, Move m2); /* Return 1 (m1<m2), 0 or -1 (m1>m2) */ -int compare_last(Alg *alg, Move m, bool inverse); void compose_alg(Alg *alg1, Alg *alg2); bool commute(Move m1, Move m2); void copy_alg(Alg *src, Alg *dst); void free_alg(Alg *alg); void free_alglist(AlgList *l); +void inplace(Alg * (*f)(Alg *), Alg *alg); Alg * inverse_alg(Alg *alg); Move inverse_move(Move m); char * move_string(Move m); +void movelist_to_position(Move *ml, int *pos); +void moveset_to_list(Moveset ms, Move *lst); Alg * new_alg(char *str); AlgList * new_alglist(); Alg * on_inverse(Alg *alg); void print_alg(Alg *alg, bool l); void print_alglist(AlgList *al, bool l); -void remove_last_move(Alg *alg); void swapmove(Move *m1, Move *m2); -char * trans_string(Trans t); /* Here because similar to move_string, move? */ Alg * unniss(Alg *alg); +void init_moveset(Moveset *ms); +void init_all_movesets(); + #endif diff --git a/src/commands.c b/src/commands.c @@ -1,11 +1,187 @@ -#define COMMANDS_C - #include "commands.h" -static bool read_cs(CommandArgs *args, char *str); +/* Arg parsing functions *****************************************************/ + +CommandArgs * gen_parse_args(int c, char **v); +CommandArgs * help_parse_args(int c, char **v); +CommandArgs * parse_only_scramble(int c, char **v); +CommandArgs * parse_no_arg(int c, char **v); +CommandArgs * solve_parse_args(int c, char **v); +CommandArgs * scramble_parse_args(int c, char **v); + +/* Exec functions ************************************************************/ + +static void gen_exec(CommandArgs *args); +static void cleanup_exec(CommandArgs *args); +static void invert_exec(CommandArgs *args); +static void solve_exec(CommandArgs *args); +static void scramble_exec(CommandArgs *args); +static void steps_exec(CommandArgs *args); +static void commands_exec(CommandArgs *args); +static void freemem_exec(CommandArgs *args); +static void print_exec(CommandArgs *args); +static void twophase_exec(CommandArgs *args); +static void help_exec(CommandArgs *args); +static void quit_exec(CommandArgs *args); +static void unniss_exec(CommandArgs *args); +static void version_exec(CommandArgs *args); + +/* Local functions ***********************************************************/ + +static bool read_step(CommandArgs *args, char *str); static bool read_scrtype(CommandArgs *args, char *str); static bool read_scramble(int c, char **v, CommandArgs *args); +/* Commands ******************************************************************/ + +Command +solve_cmd = { + .name = "solve", + .usage = "solve STEP [OPTIONS] SCRAMBLE", + .description = "Solve a step; see command steps for a list of steps", + .parse_args = solve_parse_args, + .exec = solve_exec +}; + +Command +scramble_cmd = { + .name = "scramble", + .usage = "scramble [TYPE] [-n N]", + .description = "Get a random-position scramble", + .parse_args = scramble_parse_args, + .exec = scramble_exec, +}; + +Command +gen_cmd = { + .name = "gen", + .usage = "gen [-t N]", + .description = "Generate all tables [using N threads]", + .parse_args = gen_parse_args, + .exec = gen_exec +}; + +Command +invert_cmd = { + .name = "invert", + .usage = "invert SCRAMBLE]", + .description = "Invert a scramble", + .parse_args = parse_only_scramble, + .exec = invert_exec, +}; + +Command +steps_cmd = { + .name = "steps", + .usage = "steps", + .description = "List available steps", + .parse_args = parse_no_arg, + .exec = steps_exec +}; + +Command +commands_cmd = { + .name = "commands", + .usage = "commands", + .description = "List available commands", + .parse_args = parse_no_arg, + .exec = commands_exec +}; + +Command +freemem_cmd = { + .name = "freemem", + .usage = "freemem", + .description = "free large tables from RAM", + .parse_args = parse_no_arg, + .exec = freemem_exec, +}; + +Command +print_cmd = { + .name = "print", + .usage = "print SCRAMBLE", + .description = "Print written description of the cube", + .parse_args = parse_only_scramble, + .exec = print_exec, +}; + +Command +help_cmd = { + .name = "help", + .usage = "help [COMMAND]", + .description = "Display nissy manual page or help on specific command", + .parse_args = help_parse_args, + .exec = help_exec, +}; + +Command +twophase_cmd = { + .name = "twophase", + .usage = "twophase", + .description = "Find a solution quickly using a 2-phase method", + .parse_args = parse_only_scramble, + .exec = twophase_exec, +}; + +Command +quit_cmd = { + .name = "quit", + .usage = "quit", + .description = "Quit nissy", + .parse_args = parse_no_arg, + .exec = quit_exec, +}; + +Command +cleanup_cmd = { + .name = "cleanup", + .usage = "cleanup SCRAMBLE", + .description = "Rewrite a scramble using only standard moves (HTM)", + .parse_args = parse_only_scramble, + .exec = cleanup_exec, +}; + +Command +unniss_cmd = { + .name = "unniss", + .usage = "unniss SCRAMBLE", + .description = "Rewrite a scramble without NISS", + .parse_args = parse_only_scramble, + .exec = unniss_exec, +}; + +Command +version_cmd = { + .name = "version", + .usage = "version", + .description = "print nissy version", + .parse_args = parse_no_arg, + .exec = version_exec, +}; + +Command *commands[] = { + &commands_cmd, + &freemem_cmd, + &gen_cmd, + &help_cmd, + &invert_cmd, + &print_cmd, + &quit_cmd, + &solve_cmd, + &scramble_cmd, + &steps_cmd, + &twophase_cmd, + &cleanup_cmd, + &unniss_cmd, + &version_cmd, + NULL +}; + +/* Other constants ***********************************************************/ + +char *scrtypes[20] = { "eo", "corners", "edges", "fmc", "dr", "htr", NULL }; + /* Arg parsing functions implementation **************************************/ CommandArgs * @@ -95,7 +271,7 @@ solve_parse_args(int c, char **v) a->opts->print_number = false; } else if (!strcmp(v[i], "-c")) { a->opts->count_only = true; - } else if (!read_cs(a, v[i])) { + } else if (!read_step(a, v[i])) { break; } } @@ -210,26 +386,17 @@ parse_no_arg(int c, char **v) /* Exec functions implementation *********************************************/ -void +static void solve_exec(CommandArgs *args) { Cube c; AlgList *sols; - Solver *solver[99]; - Threader *threader; - make_solved(&c); - apply_alg(args->scramble, &c); -/* TODO: adjust */ -/* threader = &threader_single;*/ - threader = &threader_eager; + init_all_movesets(); + init_symcoord(); -/* TODO: adjust */ - int i; - for (i = 0; args->cs->step[i] != NULL; i++) - solver[i] = new_stepsolver_lazy(args->cs->step[i]); - solver[i] = NULL; - sols = solve(&c, args->opts, solver, threader); + c = apply_alg(args->scramble, (Cube){0}); + sols = solve(c, args->step, args->opts); if (args->opts->count_only) printf("%d\n", sols->len); @@ -239,67 +406,85 @@ solve_exec(CommandArgs *args) free_alglist(sols); } -void +static void scramble_exec(CommandArgs *args) { Cube cube; + CubeArray *arr; Alg *scr, *ruf, *aux; - int i, j, eo, ep, co, cp; + int i, j, eo, ep, co, cp, a[12]; + int eparr[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 }; uint64_t ui, uj, uk; + init_all_movesets(); + init_symcoord(); + srand(time(NULL)); for (i = 0; i < args->n; i++) { if (!strcmp(args->scrtype, "dr")) { - ui = rand() % FACTORIAL8; - uj = rand() % FACTORIAL8; - uk = rand() % FACTORIAL4; - - make_solved(&cube); - index_to_perm(ui, 8, cube.cp); - index_to_perm(uj, 8, cube.ep); - index_to_perm(uk, 4, cube.ep + 8); - for (j = 8; j < 12; j++) - cube.ep[j] += 8; + /* Warning: cube is inconsistent because of side CO * + * and EO on U/D. But solve_2phase only solves drfin * + * in this case, so it should be ok. * + * TODO: check this properly * + * Moreover we again need to fix parity after * + * generating epose manually */ + do { + ui = rand() % FACTORIAL8; + uj = rand() % FACTORIAL8; + uk = rand() % FACTORIAL4; + + index_to_perm(ui, 8, eparr); + arr = malloc(sizeof(CubeArray)); + arr->ep = eparr; + cube = arrays_to_cube(arr, pf_ep); + free(arr); + + cube.cp = uj; + cube.epose = uk; + } while (!is_admissible(cube)); } else if (!strcmp(args->scrtype, "htr")) { - make_solved(&cube); - /* TODO */ + /* antindex_htrfin() returns a consistent * + * cube, except possibly for parity */ + do { + ui = rand() % (24*24/6); + cube = (Cube){0}; + cube.cp = cornershtrfin_ant[ui]; + cube.epose = rand() % 24; + cube.eposs = rand() % 24; + cube.eposm = rand() % 24; + } while (!is_admissible(cube)); } else { - ep = rand() % FACTORIAL12; - cp = rand() % FACTORIAL8; eo = rand() % POW2TO11; + ep = rand() % FACTORIAL12; co = rand() % POW3TO7; + cp = rand() % FACTORIAL8; if (!strcmp(args->scrtype, "eo")) { eo = 0; } else if (!strcmp(args->scrtype, "corners")) { eo = 0; ep = 0; + index_to_perm(cp, 8, a); + if (perm_sign(a, 8) == 1) { + swap(&a[0], &a[1]); + cp = perm_to_index(a, 8); + } } else if (!strcmp(args->scrtype, "edges")) { co = 0; cp = 0; + index_to_perm(ep, 12, a); + if (perm_sign(a, 12) == 1) { + swap(&a[0], &a[1]); + ep = perm_to_index(a, 12); + } } - - make_solved(&cube); - index_to_perm(ep, 12, cube.ep); - index_to_perm(cp, 8, cube.cp); - int_to_sum_zero_array(eo, 2, 12, cube.eo); - int_to_sum_zero_array(co, 3, 8, cube.co); - } - - if (!is_admissible(&cube)) { - if (!strcmp(args->scrtype, "corners")) - swap(&cube.cp[UFR], &cube.cp[UFL]); - else - swap(&cube.ep[UF], &cube.ep[UB]); + cube = fourval_to_cube(eo, ep, co, cp); } /* TODO: can be optimized for htr and dr using htrfin, drfin */ - /* - TODO: solve_2phase was removed - scr = solve_2phase(&cube, 1); - */ + scr = solve_2phase(cube, 1); if (!strcmp(args->scrtype, "fmc")) { aux = new_alg(""); @@ -328,22 +513,23 @@ scramble_exec(CommandArgs *args) } } -void +static void gen_exec(CommandArgs *args) { -/* TODO: int i; fprintf(stderr, "Generating coordinates...\n"); + init_all_movesets(); + init_symcoord(); + fprintf(stderr, "Generating pruning tables...\n"); for (i = 0; all_pd[i] != NULL; i++) genptable(all_pd[i], args->opts->nthreads); -*/ fprintf(stderr, "Done!\n"); } -void +static void invert_exec(CommandArgs *args) { Alg *inv; @@ -354,16 +540,16 @@ invert_exec(CommandArgs *args) free_alg(inv); } -void +static void steps_exec(CommandArgs *args) { int i; - for (i = 0; csteps[i] != NULL; i++) - printf("%-15s %s\n", csteps[i]->shortname, csteps[i]->name); + for (i = 0; steps[i] != NULL; i++) + printf("%-15s %s\n", steps[i]->shortname, steps[i]->name); } -void +static void commands_exec(CommandArgs *args) { int i; @@ -373,10 +559,9 @@ commands_exec(CommandArgs *args) } -void +static void freemem_exec(CommandArgs *args) { -/* TODO: int i; for (i = 0; all_pd[i] != NULL; i++) @@ -384,36 +569,35 @@ freemem_exec(CommandArgs *args) for (i = 0; all_sd[i] != NULL; i++) free_sd(all_sd[i]); -*/ + + /* TODO: invtables are also large, but for now they are * + * statically allocated. Consider releasing those too. */ } -void +static void print_exec(CommandArgs *args) { - Cube c; - - make_solved(&c); - apply_alg(args->scramble, &c); - print_cube(&c); + init_moves(); + print_cube(apply_alg(args->scramble, (Cube){0})); } -/* -void +static void twophase_exec(CommandArgs *args) { Cube c; Alg *sol; - make_solved(&c); - apply_alg(args->scramble, &c); - sol = solve_2phase(&c, 1); + init_all_movesets(); + init_symcoord(); + + c = apply_alg(args->scramble, (Cube){0}); + sol = solve_2phase(c, 1); print_alg(sol, false); free_alg(sol); } -*/ -void +static void help_exec(CommandArgs *args) { if (args->command == NULL) { @@ -427,7 +611,7 @@ help_exec(CommandArgs *args) " system (such as Linux or MacOS) or in pdf and html" " format in the docs folder.\n" "Nissy is available for free at " - "https://nissy.tronto.net\n" + "https://github.com/sebastianotronto/nissy\n" ); } else { printf("Command %s: %s\nusage: %s\n", args->command->name, @@ -435,24 +619,26 @@ help_exec(CommandArgs *args) } } -void +static void quit_exec(CommandArgs *args) { exit(0); } -void +static void cleanup_exec(CommandArgs *args) { Alg *alg; + init_moves(); + alg = cleanup(args->scramble); print_alg(alg, false); free_alg(alg); } -void +static void unniss_exec(CommandArgs *args) { Alg *aux; @@ -462,7 +648,7 @@ unniss_exec(CommandArgs *args) free(aux); } -void +static void version_exec(CommandArgs *args) { printf(VERSION"\n"); @@ -505,8 +691,6 @@ static bool read_scrtype(CommandArgs *args, char *str) { int i; - static char *scrtypes[20] = - { "eo", "corners", "edges", "fmc", "dr", "htr", NULL }; for (i = 0; scrtypes[i] != NULL; i++) { if (!strcmp(scrtypes[i], str)) { @@ -519,13 +703,13 @@ read_scrtype(CommandArgs *args, char *str) } static bool -read_cs(CommandArgs *args, char *str) +read_step(CommandArgs *args, char *str) { int i; - for (i = 0; csteps[i] != NULL; i++) { - if (!strcmp(csteps[i]->shortname, str)) { - args->cs = csteps[i]; + for (i = 0; steps[i] != NULL; i++) { + if (!strcmp(steps[i]->shortname, str)) { + args->step = steps[i]; return true; } } @@ -562,7 +746,7 @@ new_args() args->opts = malloc(sizeof(SolveOptions)); /* step and command are static */ - args->cs = csteps[0]; /* default: first step in list */ + args->step = steps[0]; /* default: first step in list */ args->command = NULL; return args; diff --git a/src/commands.h b/src/commands.h @@ -5,207 +5,10 @@ #include "solve.h" #include "steps.h" -#include "solver_step.h" -#include "threader_single.h" -#include "threader_eager.h" void free_args(CommandArgs *args); CommandArgs * new_args(); -/* Arg parsing functions *****************************************************/ - -CommandArgs * gen_parse_args(int c, char **v); -CommandArgs * help_parse_args(int c, char **v); -CommandArgs * parse_only_scramble(int c, char **v); -CommandArgs * parse_no_arg(int c, char **v); -CommandArgs * solve_parse_args(int c, char **v); -CommandArgs * scramble_parse_args(int c, char **v); - -/* Exec functions ************************************************************/ - -void gen_exec(CommandArgs *args); -void cleanup_exec(CommandArgs *args); -void invert_exec(CommandArgs *args); -void solve_exec(CommandArgs *args); -void scramble_exec(CommandArgs *args); -void steps_exec(CommandArgs *args); -void commands_exec(CommandArgs *args); -void freemem_exec(CommandArgs *args); -void print_exec(CommandArgs *args); -/*void twophase_exec(CommandArgs *args);*/ -void help_exec(CommandArgs *args); -void quit_exec(CommandArgs *args); -void unniss_exec(CommandArgs *args); -void version_exec(CommandArgs *args); - -/* Commands ******************************************************************/ - -#ifndef COMMANDS_C - -extern Command cleanup_cmd; -extern Command commands_cmd; -extern Command freemem_cmd; -extern Command gen_cmd; -extern Command help_cmd; -extern Command invert_cmd; -extern Command print_cmd; -extern Command quit_cmd; -extern Command scramble_cmd; -extern Command solve_cmd; -extern Command steps_cmd; -extern Command unniss_cmd; -extern Command version_cmd; - -extern Command *commands[]; - -#else - -Command -solve_cmd = { - .name = "solve", - .usage = "solve STEP [OPTIONS] SCRAMBLE", - .description = "Solve a step; see command steps for a list of steps", - .parse_args = solve_parse_args, - .exec = solve_exec -}; - -Command -scramble_cmd = { - .name = "scramble", - .usage = "scramble [TYPE] [-n N]", - .description = "Get a random-position scramble", - .parse_args = scramble_parse_args, - .exec = scramble_exec, -}; - -Command -gen_cmd = { - .name = "gen", - .usage = "gen [-t N]", - .description = "Generate all tables [using N threads]", - .parse_args = gen_parse_args, - .exec = gen_exec -}; - -Command -invert_cmd = { - .name = "invert", - .usage = "invert SCRAMBLE]", - .description = "Invert a scramble", - .parse_args = parse_only_scramble, - .exec = invert_exec, -}; - -Command -steps_cmd = { - .name = "steps", - .usage = "steps", - .description = "List available steps", - .parse_args = parse_no_arg, - .exec = steps_exec -}; - -Command -commands_cmd = { - .name = "commands", - .usage = "commands", - .description = "List available commands", - .parse_args = parse_no_arg, - .exec = commands_exec -}; - -Command -freemem_cmd = { - .name = "freemem", - .usage = "freemem", - .description = "free large tables from RAM", - .parse_args = parse_no_arg, - .exec = freemem_exec, -}; - -Command -print_cmd = { - .name = "print", - .usage = "print SCRAMBLE", - .description = "Print written description of the cube", - .parse_args = parse_only_scramble, - .exec = print_exec, -}; - -Command -help_cmd = { - .name = "help", - .usage = "help [COMMAND]", - .description = "Display nissy manual page or help on specific command", - .parse_args = help_parse_args, - .exec = help_exec, -}; - -/* -Command -twophase_cmd = { - .name = "twophase", - .usage = "twophase", - .description = "Find a solution quickly using a 2-phase method", - .parse_args = parse_only_scramble, - .exec = twophase_exec, -}; -*/ - -Command -quit_cmd = { - .name = "quit", - .usage = "quit", - .description = "Quit nissy", - .parse_args = parse_no_arg, - .exec = quit_exec, -}; - -Command -cleanup_cmd = { - .name = "cleanup", - .usage = "cleanup SCRAMBLE", - .description = "Rewrite a scramble using only standard moves (HTM)", - .parse_args = parse_only_scramble, - .exec = cleanup_exec, -}; - -Command -unniss_cmd = { - .name = "unniss", - .usage = "unniss SCRAMBLE", - .description = "Rewrite a scramble without NISS", - .parse_args = parse_only_scramble, - .exec = unniss_exec, -}; - -Command -version_cmd = { - .name = "version", - .usage = "version", - .description = "print nissy version", - .parse_args = parse_no_arg, - .exec = version_exec, -}; - -Command *commands[] = { - &commands_cmd, - &freemem_cmd, - &gen_cmd, - &help_cmd, - &invert_cmd, - &print_cmd, - &quit_cmd, - &solve_cmd, - &scramble_cmd, - &steps_cmd, -/* &twophase_cmd,*/ - &cleanup_cmd, - &unniss_cmd, - &version_cmd, - NULL -}; - -#endif +extern Command * commands[]; #endif diff --git a/src/coord.c b/src/coord.c @@ -1,654 +1,611 @@ -#define COORD_C - #include "coord.h" -static void gen_coord_comp(Coordinate *coord); -static void gen_coord_sym(Coordinate *coord); -static bool read_coord_mtable(Coordinate *coord); -static bool read_coord_sd(Coordinate *coord); -static bool read_coord_ttable(Coordinate *coord); -static bool write_coord_mtable(Coordinate *coord); -static bool write_coord_sd(Coordinate *coord); -static bool write_coord_ttable(Coordinate *coord); +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_epud(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cp(Cube cube); +static uint64_t index_cphtr(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_cornershtrfin(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_drud_eofb(Cube cube); +static uint64_t index_htr_drud(Cube cube); +static uint64_t index_htrfin(Cube cube); +static uint64_t index_cpud_separate(Cube cube); + +static uint64_t move_eofb(Move m, uint64_t ind); +static uint64_t move_eofbepos(Move m, uint64_t ind); +static uint64_t move_epud(Move m, uint64_t ind); +static uint64_t move_coud(Move m, uint64_t ind); +static uint64_t move_corners(Move m, uint64_t ind); +static uint64_t move_cp(Move m, uint64_t ind); +static uint64_t move_cphtr(Move m, uint64_t ind); +static uint64_t move_cornershtr(Move m, uint64_t ind); +static uint64_t move_cornershtrfin(Move m, uint64_t ind); +static uint64_t move_drud(Move m, uint64_t ind); +static uint64_t move_drud_eofb(Move m, uint64_t ind); +static uint64_t move_htr_drud(Move m, uint64_t ind); +static uint64_t move_htrfin(Move m, uint64_t ind); +static uint64_t move_cpud_separate(Move m, uint64_t ind); + +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_cpud_separate(); +static void init_cornershtrfin(); +static void init_htr_eposs(); + + +/* All sorts of useful costants and tables **********************************/ + +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +int cpud_separate_ind[FACTORIAL8]; +int cpud_separate_ant[BINOM8ON4]; +static int cornershtrfin_ind[FACTORIAL8]; +int cornershtrfin_ant[24*24/6]; +static int htr_eposs_ind[BINOM12ON4]; +static int htr_eposs_ant[BINOM8ON4]; + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofb = { + .index = index_eofb, + .max = POW2TO11, + .move = move_eofb, +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .max = POW2TO11 * BINOM12ON4, + .move = move_eofbepos, +}; + +Coordinate +coord_coud = { + .index = index_coud, + .max = POW3TO7, + .move = move_coud, +}; + +Coordinate +coord_corners = { + .index = index_corners, + .max = POW3TO7 * FACTORIAL8, + .move = move_corners, +}; + +Coordinate +coord_cp = { + .index = index_cp, + .max = FACTORIAL8, + .move = move_cp, +}; + +Coordinate +coord_cphtr = { + .index = index_cphtr, + .max = BINOM8ON4 * 6, + .move = move_cphtr, +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6, + .move = move_cornershtr, +}; + +Coordinate +coord_cornershtrfin = { + .index = index_cornershtrfin, + .max = 24*24/6, + .move = move_cornershtrfin, +}; + +Coordinate +coord_epud = { + .index = index_epud, + .max = FACTORIAL8, + .move = move_epud, +}; + +Coordinate +coord_drud = { + .index = index_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4, + .move = move_drud, +}; + +Coordinate +coord_htr_drud = { + .index = index_htr_drud, + .max = BINOM8ON4 * 6 * BINOM8ON4, + .move = move_htr_drud, +}; + +Coordinate +coord_htrfin = { + .index = index_htrfin, + .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */ + .move = move_htrfin, +}; + +Coordinate +coord_drud_eofb = { + .index = index_drud_eofb, + .max = POW3TO7 * BINOM12ON4, + .move = move_drud_eofb, +}; + +Coordinate +coord_cpud_separate = { + .index = index_cpud_separate, + .max = BINOM8ON4, + .move = move_cpud_separate, +}; /* Indexers ******************************************************************/ -uint64_t -index_eofb(Cube *cube) -{ - return (uint64_t)digit_array_to_int(cube->eo, 11, 2); -} - -uint64_t -index_coud(Cube *cube) +static uint64_t +index_eofb(Cube cube) { - return (uint64_t)digit_array_to_int(cube->co, 7, 3); + return cube.eofb; } -uint64_t -index_cp(Cube *cube) +static uint64_t +index_eofbepos(Cube cube) { - return (uint64_t)perm_to_index(cube->cp, 8); + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; } -uint64_t -index_cpudsep(Cube *cube) +static uint64_t +index_epud(Cube cube) { - int i, c[8]; + uint64_t ret; + CubeArray *arr = new_cubearray(cube, pf_ep); - for (i = 0; i < 8; i++) - c[i] = cube->cp[i] < 4 ? 0 : 1; + ret = perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); - return (uint64_t)subset_to_index(c, 8, 4); + return ret; } -uint64_t -index_epe(Cube *cube) +static uint64_t +index_coud(Cube cube) { - int i, e[4]; - - for (i = 0; i < 4; i++) - e[i] = cube->ep[i+8] - 8; - - return (uint64_t)perm_to_index(e, 4); + return cube.coud; } -uint64_t -index_epud(Cube *cube) +static uint64_t +index_corners(Cube cube) { - return (uint64_t)perm_to_index(cube->ep, 8); + return cube.coud * FACTORIAL8 + cube.cp; } -uint64_t -index_epos(Cube *cube) +static uint64_t +index_cp(Cube cube) { - int i, a[12]; - - for (i = 0; i < 12; i++) - a[i] = (cube->ep[i] < 8) ? 0 : 1; - - return (uint64_t)subset_to_index(a, 12, 4); + return cube.cp; } -uint64_t -index_eposepe(Cube *cube) +static uint64_t +index_cphtr(Cube cube) { - int i, j, e[4]; - uint64_t epos, epe; - - epos = (uint64_t)index_epos(cube); - for (i = 0, j = 0; i < 12; i++) - if (cube->ep[i] >= 8) - e[j++] = cube->ep[i] - 8; - epe = (uint64_t)perm_to_index(e, 4); - - return epos * FACTORIAL4 + epe; + return cphtr_right_cosets[cube.cp]; } -/* Inverse indexers **********************************************************/ - -void -invindex_eofb(uint64_t ind, Cube *cube) +static uint64_t +index_cornershtr(Cube cube) { - int_to_sum_zero_array(ind, 2, 12, cube->eo); + return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube); } -void -invindex_coud(uint64_t ind, Cube *cube) +static uint64_t +index_cornershtrfin(Cube cube) { - int_to_sum_zero_array(ind, 3, 8, cube->co); + return cornershtrfin_ind[cube.cp]; } -void -invindex_cp(uint64_t ind, Cube *cube) +static uint64_t +index_drud(Cube cube) { - index_to_perm(ind, 8, cube->cp); -} - -void -invindex_cpudsep(uint64_t ind, Cube *cube) -{ - int i, j, k, c[8]; - - index_to_subset(ind, 8, 4, c); - for (i = 0, j = 0, k = 4; i < 8; i++) - cube->cp[i] = c[i] == 0 ? j++ : k++; -} + uint64_t a, b, c; + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; -void -invindex_epe(uint64_t ind, Cube *cube) -{ - int i; + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; - index_to_perm(ind, 4, &cube->ep[8]); - for (i = 0; i < 4; i++) - cube->ep[i+8] += 8; + return a + b + c; } -void -invindex_epud(uint64_t ind, Cube *cube) +static uint64_t +index_drud_eofb(Cube cube) { - index_to_perm(ind, 8, cube->ep); + return index_drud(cube) / POW2TO11; } -void -invindex_epos(uint64_t ind, Cube *cube) +static uint64_t +index_htr_drud(Cube cube) { - int i, j, k; + uint64_t a, b; - index_to_subset(ind, 12, 4, cube->ep); - for (i = 0, j = 0, k = 8; i < 12; i++) - if (cube->ep[i] == 0) - cube->ep[i] = j++; - else - cube->ep[i] = k++; -} - -void -invindex_eposepe(uint64_t ind, Cube *cube) -{ - int i, j, k, e[4]; - uint64_t epos, epe; + a = index_cphtr(cube); + b = htr_eposs_ind[cube.eposs/24]; - epos = ind / FACTORIAL4; - epe = ind % FACTORIAL4; - - index_to_subset(epos, 12, 4, cube->ep); - index_to_perm(epe, 4, e); - - for (i = 0, j = 0, k = 0; i < 12; i++) - if (cube->ep[i] == 0) - cube->ep[i] = j++; - else - cube->ep[i] = e[k++] + 8; + return a * BINOM8ON4 + b; } -/* Other local functions *****************************************************/ - -uint64_t -indexers_getmax(Indexer **is) +static uint64_t +index_htrfin(Cube cube) { - int i; - uint64_t max = 1; + uint64_t epe, eps, epm, cp, ep; - for (i = 0; is[i] != NULL; i++) - max *= is[i]->n; + epe = cube.epose % 24; + eps = cube.eposs % 24; + epm = cube.eposm % 24; + ep = (epe * 24 + eps) *24 + epm; + cp = index_cornershtrfin(cube); - return max; + return cp * 24 * 24 * 24 + ep; } -uint64_t -indexers_getind(Indexer **is, Cube *c) +static uint64_t +index_cpud_separate(Cube cube) { - int i; - uint64_t max = 0; + return cpud_separate_ind[cube.cp]; +} - for (i = 0; is[i] != NULL; i++) { - max *= is[i]->n; - max += is[i]->index(c); - } +/* Coordinate movers *********************************************************/ - return max; +static uint64_t +move_eofb(Move m, uint64_t ind) +{ + return eofb_mtable[m][ind]; } -void -indexers_makecube(Indexer **is, uint64_t ind, Cube *c) +static uint64_t +move_eofbepos(Move m, uint64_t ind) { - /* Warning: anti-indexers are applied in the same order as indexers. */ - /* We assume order does not matter, but it would make more sense to */ - /* apply them in reverse. */ + uint64_t a, b; - int i; - uint64_t m; + a = epose_mtable[m][(ind / POW2TO11)*24]; + b = eofb_mtable[m][ind % POW2TO11]; - make_solved(c); - m = indexers_getmax(is); - for (i = 0; is[i] != NULL; i++) { - m /= is[i]->n; - is[i]->to_cube(ind / m, c); - ind %= m; - } + return (a/24) * POW2TO11 + b; } -static void -gen_coord_comp(Coordinate *coord) +static uint64_t +move_epud(Move m, uint64_t ind) { + /* TODO: save to file? */ + static bool initialized = false; + static int a[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 }; + static int shortlist[NMOVES] = { + [U] = 0, [U2] = 1, [U3] = 2, [D] = 3, [D2] = 4, [D3] = 5, + [R2] = 6, [L2] = 7, [F2] = 8, [B2] = 9 + }; + static uint64_t aux[10][FACTORIAL8]; uint64_t ui; - Cube c, mvd; - Move m; - Trans t; - - coord->max = indexers_getmax(coord->i); - - for (m = 0; m < NMOVES; m++) - coord->mtable[m] = malloc(coord->max * sizeof(uint64_t)); - - for (t = 0; t < NTRANS; t++) - coord->ttable[t] = malloc(coord->max * sizeof(uint64_t)); - - if (!read_coord_mtable(coord)) { - fprintf(stderr, "%s: generating mtable\n", coord->name); - - for (ui = 0; ui < coord->max; ui++) { - indexers_makecube(coord->i, ui, &c); - for (m = 0; m < NMOVES; m++) { - copy_cube(&c, &mvd); - apply_move(m, &mvd); - coord->mtable[m][ui] = - indexers_getind(coord->i, &mvd); - } - } - if (!write_coord_mtable(coord)) - fprintf(stderr, "%s: error writing mtable\n", - coord->name); - - fprintf(stderr, "%s: mtable generated\n", coord->name); + int j; + Move mj; + Cube c; + CubeArray *arr, *auxarr; + + if (!moveset_drud.allowed(m)) { + fprintf(stderr, "Move not allowed for epud\n" + "This is a bug, please report\n"); + return coord_epud.max; } - if (!read_coord_ttable(coord)) { - fprintf(stderr, "%s: generating ttable\n", coord->name); - - for (ui = 0; ui < coord->max; ui++) { - indexers_makecube(coord->i, ui, &c); - for (t = 0; t < NTRANS; t++) { - copy_cube(&c, &mvd); - apply_trans(t, &mvd); - coord->ttable[t][ui] = - indexers_getind(coord->i, &mvd); + if (!initialized) { + auxarr = malloc(sizeof(CubeArray)); + auxarr->ep = a; + for (ui = 0; ui < coord_epud.max; ui++) { + index_to_perm(ui, 8, a); + c = arrays_to_cube(auxarr, pf_ep); + for (j = 0; moveset_drud.sorted_moves[j] != NULLMOVE; + j++) { + mj = moveset_drud.sorted_moves[j]; + arr = new_cubearray(apply_move(mj, c), pf_ep); + aux[shortlist[mj]][ui] = + perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); } } - if (!write_coord_ttable(coord)) - fprintf(stderr, "%s: error writing ttable\n", - coord->name); + free(auxarr); + + initialized = true; } + + return aux[shortlist[m]][ind]; } -static void -gen_coord_sym(Coordinate *coord) +static uint64_t +move_coud(Move m, uint64_t ind) { - uint64_t i, in, ui, uj, uu, M, nr; - int j; - Move m; - Trans t; - - M = coord->base[0]->max; - coord->selfsim = malloc(M * sizeof(uint64_t)); - coord->symclass = malloc(M * sizeof(uint64_t)); - coord->symrep = malloc(M * sizeof(uint64_t)); - coord->transtorep = malloc(M * sizeof(Trans)); - - if (!read_coord_sd(coord)) { - fprintf(stderr, "%s: generating syms\n", coord->name); + return coud_mtable[m][ind]; +} - for (i = 0; i < M; i++) - coord->symclass[i] = M+1; +static uint64_t +move_corners(Move m, uint64_t ind) +{ + uint64_t a, b; - for (i = 0, nr = 0; i < M; i++) { - if (coord->symclass[i] != M+1) - continue; + a = coud_mtable[m][ind / FACTORIAL8]; + b = cp_mtable[m][ind % FACTORIAL8]; - coord->symrep[nr] = i; - coord->transtorep[i] = uf; - coord->selfsim[nr] = (uint64_t)0; - for (j = 0; j < coord->tgrp->n; j++) { - t = coord->tgrp->t[j]; - in = trans_coord(coord->base[0], t, i); - coord->symclass[in] = nr; - if (in == i) - coord->selfsim[nr] |= ((uint64_t)1<<t); - else - coord->transtorep[in] = - inverse_trans(t); - } - nr++; - } + return a * FACTORIAL8 + b; +} - coord->max = nr; +static uint64_t +move_cp(Move m, uint64_t ind) +{ + return cp_mtable[m][ind]; +} - fprintf(stderr, "%s: found %" PRIu64 " classes\n", - coord->name, nr); - if (!write_coord_sd(coord)) - fprintf(stderr, "%s: error writing symdata\n", - coord->name); - } +static uint64_t +move_cphtr(Move m, uint64_t ind) +{ + static bool initialized = false; + static uint64_t aux[NMOVES][BINOM8ON4*6]; + uint64_t ui; + Move j; - coord->symrep = realloc(coord->symrep, coord->max*sizeof(uint64_t)); - coord->selfsim = realloc(coord->selfsim, coord->max*sizeof(uint64_t)); + if (!initialized) { + for (ui = 0; ui < BINOM8ON4*6; ui++) + for (j = U; j < NMOVES; j++) + aux[j][ui] = cphtr_right_cosets[ + cp_mtable[j][cphtr_right_rep[ui]]]; - for (m = 0; m < NMOVES; m++) { - coord->mtable[m] = malloc(coord->max*sizeof(uint64_t)); - coord->ttrep_move[m] = malloc(coord->max*sizeof(Trans)); + initialized = true; } - if (!read_coord_mtable(coord)) { - for (ui = 0; ui < coord->max; ui++) { - uu = coord->symrep[ui]; - for (m = 0; m < NMOVES; m++) { - uj = move_coord(coord->base[0], m, uu, NULL); - coord->mtable[m][ui] = coord->symclass[uj]; - coord->ttrep_move[m][ui] = - coord->transtorep[uj]; - } - } - if (!write_coord_mtable(coord)) - fprintf(stderr, "%s: error writing mtable\n", - coord->name); - } + return aux[m][ind]; } -static bool -read_coord_mtable(Coordinate *coord) +static uint64_t +move_cornershtr(Move m, uint64_t ind) { - FILE *f; - char fname[strlen(tabledir)+256]; - Move m; - uint64_t M; - bool r; + uint64_t a, b; - strcpy(fname, tabledir); - strcat(fname, "/mt_"); - strcat(fname, coord->name); + a = coud_mtable[m][ind/(BINOM8ON4 * 6)]; + b = move_cphtr(m, ind % (BINOM8ON4 * 6)); - if ((f = fopen(fname, "rb")) == NULL) - return false; - - M = coord->max; - r = true; - for (m = 0; m < NMOVES; m++) - r = r && fread(coord->mtable[m], sizeof(uint64_t), M, f) == M; - - if (coord->type == SYM_COORD) - for (m = 0; m < NMOVES; m++) - r = r && fread(coord->ttrep_move[m], - sizeof(Trans), M, f) == M; - - fclose(f); - return r; + return a * BINOM8ON4 * 6 + b; } -static bool -read_coord_sd(Coordinate *coord) +static uint64_t +move_cornershtrfin(Move m, uint64_t ind) { - FILE *f; - char fname[strlen(tabledir)+256]; - uint64_t M, N; - bool r; - - strcpy(fname, tabledir); - strcat(fname, "/sd_"); - strcat(fname, coord->name); + int a; - if ((f = fopen(fname, "rb")) == NULL) - return false; + a = cp_mtable[m][cornershtrfin_ant[ind]]; - r = true; - r = r && fread(&coord->max, sizeof(uint64_t), 1, f) == 1; - M = coord->max; - N = coord->base[0]->max; - r = r && fread(coord->symrep, sizeof(uint64_t), M, f) == M; - r = r && fread(coord->selfsim, sizeof(uint64_t), M, f) == M; - r = r && fread(coord->symclass, sizeof(uint64_t), N, f) == N; - r = r && fread(coord->transtorep, sizeof(Trans), N, f) == N; - - fclose(f); - return r; + return cornershtrfin_ind[a]; } -static bool -read_coord_ttable(Coordinate *coord) +static uint64_t +move_drud(Move m, uint64_t ind) { - FILE *f; - char fname[strlen(tabledir)+256]; - Trans t; - uint64_t M; - bool r; - - strcpy(fname, tabledir); - strcat(fname, "/tt_"); - strcat(fname, coord->name); - - if ((f = fopen(fname, "rb")) == NULL) - return false; + uint64_t a, b, c; - M = coord->max; - r = true; - for (t = 0; t < NTRANS; t++) - r = r && fread(coord->ttable[t], sizeof(uint64_t), M, f) == M; + a = eofb_mtable[m][ind % POW2TO11]; + b = coud_mtable[m][(ind / POW2TO11) % POW3TO7]; + c = epose_mtable[m][ind / (POW2TO11 * POW3TO7)]; - fclose(f); - return r; + return a + (b + c * POW3TO7) * POW2TO11; } -static bool -write_coord_mtable(Coordinate *coord) +static uint64_t +move_drud_eofb(Move m, uint64_t ind) { - FILE *f; - char fname[strlen(tabledir)+256]; - Move m; - uint64_t M; - bool r; - - strcpy(fname, tabledir); - strcat(fname, "/mt_"); - strcat(fname, coord->name); - - if ((f = fopen(fname, "wb")) == NULL) - return false; - - M = coord->max; - r = true; - for (m = 0; m < NMOVES; m++) - r = r && fwrite(coord->mtable[m], sizeof(uint64_t), M, f) == M; + uint64_t a, b; - if (coord->type == SYM_COORD) - for (m = 0; m < NMOVES; m++) - r = r && fwrite(coord->ttrep_move[m], - sizeof(Trans), M, f) == M; + a = coud_mtable[m][ind % POW3TO7]; + b = epose_mtable[m][(ind / POW3TO7) * 24] / 24; - fclose(f); - return r; + return a + b * POW3TO7; } -static bool -write_coord_sd(Coordinate *coord) +static uint64_t +move_htr_drud(Move m, uint64_t ind) { - FILE *f; - char fname[strlen(tabledir)+256]; - uint64_t M, N; - bool r; + uint64_t a, b; - strcpy(fname, tabledir); - strcat(fname, "/sd_"); - strcat(fname, coord->name); + a = move_cphtr(m, ind/BINOM8ON4); + b = eposs_mtable[m][htr_eposs_ant[ind%BINOM8ON4]]; - if ((f = fopen(fname, "wb")) == NULL) - return false; - - r = true; - M = coord->max; - N = coord->base[0]->max; - r = r && fwrite(&coord->max, sizeof(uint64_t), 1, f) == 1; - r = r && fwrite(coord->symrep, sizeof(uint64_t), M, f) == M; - r = r && fwrite(coord->selfsim, sizeof(uint64_t), M, f) == M; - r = r && fwrite(coord->symclass, sizeof(uint64_t), N, f) == N; - r = r && fwrite(coord->transtorep, sizeof(Trans), N, f) == N; - - fclose(f); - return r; + return a*BINOM8ON4 + htr_eposs_ind[b/24]; } -static bool -write_coord_ttable(Coordinate *coord) +static uint64_t +move_htrfin(Move m, uint64_t ind) { - FILE *f; - char fname[strlen(tabledir)+256]; - Trans t; - uint64_t M; - bool r; - - strcpy(fname, tabledir); - strcat(fname, "/tt_"); - strcat(fname, coord->name); - - if ((f = fopen(fname, "wb")) == NULL) - return false; + uint64_t a, b, bm, bs, be; - M = coord->max; - r = true; - for (t = 0; t < NTRANS; t++) - r = r && fwrite(coord->ttable[t], sizeof(uint64_t), M, f) == M; + a = move_cornershtrfin(m, ind / (24*24*24)); + bm = eposm_mtable[m][ind%24] % 24; + bs = eposs_mtable[m][(ind/24)%24] % 24; + be = epose_mtable[m][(ind/(24*24))%24] % 24; + b = (be * 24 + bs) * 24 + bm; - fclose(f); - return r; + return a * (24*24*24) + b; } -/* Public functions **********************************************************/ - -void -gen_coord(Coordinate *coord) +static uint64_t +move_cpud_separate(Move m, uint64_t ind) { - int i; + return cpud_separate_ind[cp_mtable[m][cpud_separate_ant[ind]]]; +} - if (coord == NULL || coord->generated) - return; +/* Init functions implementation *********************************************/ + +/* + * There is certainly a better way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; - for (i = 0; i < 2; i++) - gen_coord(coord->base[i]); - - switch (coord->type) { - case COMP_COORD: - if (coord->i[0] == NULL) - goto error_gc; - gen_coord_comp(coord); - break; - case SYM_COORD: - if (coord->base[0] == NULL || coord->tgrp == NULL) - goto error_gc; - gen_coord_sym(coord); - break; - case SYMCOMP_COORD: - if (coord->base[0] == NULL || coord->base[1] == NULL) - goto error_gc; - coord->max = coord->base[0]->max * coord->base[1]->max; - break; - default: - break; + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; } - coord->generated = true; - return; + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); -error_gc: - fprintf(stderr, "Error generating coordinates.\n" - "This is a bug, pleae report.\n"); - exit(1); + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); } -uint64_t -index_coord(Coordinate *coord, Cube *cube, Trans *offtrans) +static void +init_cphtr_left_cosets_bfs(int i, int c) { - uint64_t c[2], cnosym; - Trans ttr; + int j, jj, next[FACTORIAL8], next2[FACTORIAL8], n, n2; - switch (coord->type) { - case COMP_COORD: - if (offtrans != NULL) - *offtrans = uf; + Move k; - return indexers_getind(coord->i, cube); - case SYM_COORD: - cnosym = index_coord(coord->base[0], cube, NULL); - ttr = coord->transtorep[cnosym]; + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; - if (offtrans != NULL) - *offtrans = ttr; + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = U2; k < B3; k++) { + if (!moveset_htr.allowed(k)) + continue; + jj = apply_move(k, (Cube){ .cp = next[j] }).cp; - return coord->symclass[cnosym]; - case SYMCOMP_COORD: - c[0] = index_coord(coord->base[0], cube, NULL); - cnosym = index_coord(coord->base[0]->base[0], cube, NULL); - ttr = coord->base[0]->transtorep[cnosym]; - c[1] = index_coord(coord->base[1], cube, NULL); - c[1] = trans_coord(coord->base[1], ttr, c[1]); - - if (offtrans != NULL) - *offtrans = ttr; + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } - return c[0] * coord->base[1]->max + c[1]; - default: - break; + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; } - - return coord->max; /* Only reached in case of error */ } -uint64_t -move_coord(Coordinate *coord, Move m, uint64_t ind, Trans *offtrans) +static void +init_cphtr_right_cosets_color(int i, int d) { - uint64_t i[2], M; - Trans ttr; - - /* Some safety checks should be done here, but for performance * - * reasons we'd rather do them before calling this function. * - * We should check if coord is generated. */ - - switch (coord->type) { - case COMP_COORD: - if (offtrans != NULL) - *offtrans = uf; - - return coord->mtable[m][ind]; - case SYM_COORD: - ttr = coord->ttrep_move[m][ind]; + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} - if (offtrans != NULL) - *offtrans = ttr; +static void +init_cpud_separate() +{ + unsigned int ui; + int i, co[8]; + + for (ui = 0; ui < FACTORIAL8; ui++) { + for (i = 0; i < 8; i++) + co[i] = what_corner_at((Cube){.cp=ui},i)>UBR ? 1 : 0; + cpud_separate_ind[ui] = subset_to_index(co, 8, 4); + cpud_separate_ant[cpud_separate_ind[ui]] = ui; + } +} - return coord->mtable[m][ind]; - case SYMCOMP_COORD: - M = coord->base[1]->max; - i[0] = ind / M; - i[1] = ind % M; - ttr = coord->base[0]->ttrep_move[m][i[0]]; - i[0] = coord->base[0]->mtable[m][i[0]]; - i[1] = coord->base[1]->mtable[m][i[1]]; - i[1] = coord->base[1]->ttable[ttr][i[1]]; +static void +init_cornershtrfin() +{ + unsigned int i, j; + int n, c; + Move m; - if (offtrans != NULL) - *offtrans = ttr; + for (i = 0; i < FACTORIAL8; i++) + cornershtrfin_ind[i] = -1; + cornershtrfin_ind[0] = 0; - return i[0] * M + i[1]; - default: - break; + /* 10-pass, I think 5 is enough, but just in case */ + n = 1; + for (i = 0; i < 10; i++) { + for (j = 0; j < FACTORIAL8; j++) { + if (cornershtrfin_ind[j] == -1) + continue; + for (m = U; m < NMOVES; m++) { + if (moveset_htr.allowed(m)) { + c = cp_mtable[m][j]; + if (cornershtrfin_ind[c] == -1) { + cornershtrfin_ind[c] = n; + cornershtrfin_ant[n] = c; + n++; + } + } + } + } } - - return coord->max; /* Only reached in case of error */ } -uint64_t -trans_coord(Coordinate *coord, Trans t, uint64_t ind) +void +init_htr_eposs() { - uint64_t i[2], M; + int ep[12], ep2[12]; + int eps_solved[4] = {UL, UR, DL, DR}; + unsigned int i, j; + + for (i = 0; i < BINOM12ON4; i++) { + for (j = 0; j < 12; j++) + ep[j] = ep2[j] = 0; + epos_to_partial_ep(i*24, ep, eps_solved); + for (j = 0; j < 8; j++) + ep2[j/2 + 4*(j%2)] = ep[j] ? 1 : 0; + htr_eposs_ind[i] = subset_to_index(ep2, 8, 4); + htr_eposs_ant[htr_eposs_ind[i]] = i*24; + } +} - /* Some safety checks should be done here, but for performance * - * reasons we'd rather do them before calling this function. * - * We should check if coord is generated. */ +void +init_coord() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; - switch (coord->type) { - case COMP_COORD: - return coord->ttable[t][ind]; - case SYM_COORD: - return ind; - case SYMCOMP_COORD: - M = coord->base[1]->max; - i[0] = ind / M; /* Always fixed */ - i[1] = ind % M; - i[1] = coord->base[1]->ttable[t][i[1]]; - return i[0] * M + i[1]; - default: - break; - } + init_trans(); - return coord->max; /* Only reached in case of error */ + init_cphtr_cosets(); + init_cornershtrfin(); + init_htr_eposs(); + init_cpud_separate(); } + diff --git a/src/coord.h b/src/coord.h @@ -3,257 +3,26 @@ #include "trans.h" -void gen_coord(Coordinate *coord); -uint64_t index_coord(Coordinate *coord, Cube *cube, - Trans *offtrans); -uint64_t indexers_getind(Indexer **is, Cube *c); -void indexers_makecube(Indexer **is, uint64_t ind, Cube *c); -uint64_t move_coord(Coordinate *coord, Move m, - uint64_t ind, Trans *offtrans); -uint64_t trans_coord(Coordinate *coord, Trans t, uint64_t ind); - -/* Base coordinates and their index functions ********************************/ - -#ifndef COORD_C - -extern Coordinate coord_eofb; -extern Coordinate coord_coud; -extern Coordinate coord_cp; -extern Coordinate coord_cpudsep; -extern Coordinate coord_epos; -extern Coordinate coord_epe; -extern Coordinate coord_eposepe; -extern Coordinate coord_epud; -extern Coordinate coord_eofbepos; -extern Coordinate coord_coud_cpudsep; -extern Coordinate coord_eofbepos_sym16; -extern Coordinate coord_cp_sym16; -extern Coordinate coord_corners_sym16; -extern Coordinate coord_drud_sym16; -extern Coordinate coord_drudfin_noE_sym16; -extern Coordinate coord_nxopt31; - -extern Coordinate *all_coordinates[]; - -#else - -/* Indexers ******************************************************************/ - -uint64_t index_eofb(Cube *cube); -void invindex_eofb(uint64_t ind, Cube *ret); -Indexer -i_eofb = { - .n = POW2TO11, - .index = index_eofb, - .to_cube = invindex_eofb, -}; - -uint64_t index_coud(Cube *cube); -void invindex_coud(uint64_t ind, Cube *ret); -Indexer -i_coud = { - .n = POW3TO7, - .index = index_coud, - .to_cube = invindex_coud, -}; - -uint64_t index_cp(Cube *cube); -void invindex_cp(uint64_t ind, Cube *ret); -Indexer -i_cp = { - .n = FACTORIAL8, - .index = index_cp, - .to_cube = invindex_cp, -}; - -uint64_t index_cpudsep(Cube *cube); -void invindex_cpudsep(uint64_t ind, Cube *ret); -Indexer -i_cpudsep = { - .n = BINOM8ON4, - .index = index_cpudsep, - .to_cube = invindex_cpudsep, -}; - -uint64_t index_epos(Cube *cube); -void invindex_epos(uint64_t ind, Cube *ret); -Indexer -i_epos = { - .n = BINOM12ON4, - .index = index_epos, - .to_cube = invindex_epos, -}; - -uint64_t index_epe(Cube *cube); -void invindex_epe(uint64_t ind, Cube *ret); -Indexer -i_epe = { - .n = FACTORIAL4, - .index = index_epe, - .to_cube = invindex_epe, -}; - -uint64_t index_eposepe(Cube *cube); -void invindex_eposepe(uint64_t ind, Cube *ret); -Indexer -i_eposepe = { - .n = BINOM12ON4 * FACTORIAL4, - .index = index_eposepe, - .to_cube = invindex_eposepe, -}; - -uint64_t index_epud(Cube *cube); -void invindex_epud(uint64_t ind, Cube *ret); -Indexer -i_epud = { - .n = FACTORIAL8, - .index = index_epud, - .to_cube = invindex_epud, -}; - -/* Composite coordinates *****************************************************/ - -Coordinate -coord_eofb = { - .name = "eofb", - .type = COMP_COORD, - .i = {&i_eofb, NULL}, -}; - -Coordinate -coord_coud = { - .name = "coud", - .type = COMP_COORD, - .i = {&i_coud, NULL}, -}; - -Coordinate -coord_cp = { - .name = "cp", - .type = COMP_COORD, - .i = {&i_cp, NULL}, -}; - -Coordinate -coord_cpudsep = { - .name = "cpudsep", - .type = COMP_COORD, - .i = {&i_cpudsep, NULL}, -}; - -Coordinate -coord_epos = { - .name = "epos", - .type = COMP_COORD, - .i = {&i_epos, NULL}, -}; - -Coordinate -coord_epe = { - .name = "epe", - .type = COMP_COORD, - .i = {&i_epe, NULL}, -}; - -Coordinate -coord_eposepe = { /* Has to be done by hand, hard compose epos + epe */ - .name = "eposepe", - .type = COMP_COORD, - .i = {&i_eposepe, NULL}, -}; - -Coordinate -coord_epud = { - .name = "epud", - .type = COMP_COORD, - .i = {&i_epud, NULL}, -}; - -Coordinate -coord_eofbepos = { - .name = "eofbepos", - .type = COMP_COORD, - .i = {&i_epos, &i_eofb, NULL}, -}; - -Coordinate -coord_coud_cpudsep = { - .name = "coud_cpudsep", - .type = COMP_COORD, - .i = {&i_coud, &i_cpudsep, NULL}, -}; - -/* Symcoordinates ************************************************************/ - -Coordinate -coord_eofbepos_sym16 = { - .name = "eofbepos_sym16", - .type = SYM_COORD, - .base = {&coord_eofbepos, NULL}, - .tgrp = &tgrp_udfix, -}; - -Coordinate -coord_cp_sym16 = { - .name = "cp_sym16", - .type = SYM_COORD, - .base = {&coord_cp, NULL}, - .tgrp = &tgrp_udfix, -}; - -/* "Symcomp" coordinates *****************************************************/ - -Coordinate -coord_corners_sym16 = { - .name = "corners_sym16", - .type = SYMCOMP_COORD, - .base = {&coord_cp_sym16, &coord_coud}, -}; - -Coordinate -coord_drud_sym16 = { - .name = "drud_sym16", - .type = SYMCOMP_COORD, - .base = {&coord_eofbepos_sym16, &coord_coud}, -}; - -Coordinate -coord_drudfin_noE_sym16 = { - .name = "drudfin_noE_sym16", - .type = SYMCOMP_COORD, - .base = {&coord_cp_sym16, &coord_epud}, -}; - -Coordinate -coord_nxopt31 = { - .name = "nxopt31", - .type = SYMCOMP_COORD, - .base = {&coord_eofbepos_sym16, &coord_coud_cpudsep}, -}; - -/* All coordinates ***********************************************************/ - -Coordinate *all_coordinates[] = { - &coord_eofb, - &coord_coud, - &coord_cp, - &coord_cpudsep, - &coord_epos, - &coord_epe, - &coord_eposepe, - &coord_epud, - &coord_eofbepos, - &coord_coud_cpudsep, - &coord_eofbepos_sym16, - &coord_cp_sym16, - &coord_corners_sym16, - &coord_drud_sym16, - &coord_drudfin_noE_sym16, - &coord_nxopt31, - NULL -}; - -#endif +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_cp; +extern Coordinate coord_cphtr; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_cornershtrfin; +extern Coordinate coord_epud; +extern Coordinate coord_drud; +extern Coordinate coord_drud_eofb; +extern Coordinate coord_htr_drud; +extern Coordinate coord_htrfin; +extern Coordinate coord_cpud_separate; + +extern int cpud_separate_ant[BINOM8ON4]; +extern int cpud_separate_ind[FACTORIAL8]; +extern int cornershtrfin_ant[24*24/6]; + +void init_coord(); #endif diff --git a/src/cube.c b/src/cube.c @@ -1,205 +1,531 @@ -#define CUBE_C - #include "cube.h" -static int where_is_piece(int piece, int *arr, int n); +/* Local functions ***********************************************************/ -void -compose_centers(Cube *c2, Cube *c1) +static void init_inverse(); +static bool read_invtables_file(); +static bool write_invtables_file(); + +/* Tables ********************************************************************/ + +static uint16_t eo_invtable_e[POW2TO11][BINOM12ON4*FACTORIAL4]; +static uint16_t eo_invtable_s[POW2TO11][BINOM12ON4*FACTORIAL4]; +static uint16_t eo_invtable_m[POW2TO11][BINOM12ON4*FACTORIAL4]; +static uint16_t co_invtable[POW3TO7][FACTORIAL8]; +static uint16_t cp_invtable[FACTORIAL8]; +static uint16_t cpos_invtable[FACTORIAL6]; + +/* Functions implementation **************************************************/ + +int +array_ep_to_epos(int *ep, int *ss) { - apply_permutation(c2->xp, c1->xp, 6); + int epos[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return 24 * subset_to_index(epos, 12, 4) + perm_to_index(eps, 4); } -void -compose_corners(Cube *c2, Cube *c1) +Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) { - apply_permutation(c2->cp, c1->cp, 8); - apply_permutation(c2->cp, c1->co, 8); - sum_arrays_mod(c2->co, c1->co, 8, 3); + Cube ret = {0}; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; } -void -compose_edges(Cube *c2, Cube *c1) +Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) { - apply_permutation(c2->ep, c1->ep, 12); - apply_permutation(c2->ep, c1->eo, 12); - sum_arrays_mod(c2->eo, c1->eo, 12, 2); + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; } void -compose(Cube *c2, Cube *c1) +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) { - compose_centers(c2, c1); - compose_corners(c2, c1); - compose_edges(c2, c1); + int i; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); } void -copy_cube_centers(Cube *src, Cube *dst) +epos_to_compatible_ep(int epos, int *ep, int *ss) { - memcpy(dst->xp, src->xp, 6 * sizeof(int)); + int i, j, k, other[8]; + bool flag; + + for (i = 0; i < 12; i++) + ep[i] = -1; + + epos_to_partial_ep(epos, ep, ss); + + for (i = 0, j = 0; i < 12; i++) { + flag = false; + for (k = 0; k < 4; k++) + flag = flag || (i == ss[k]); + if (!flag) + other[j++] = i; + } + + for (i = 0, j = 0; i < 12; i++) + if (ep[i] == -1) + ep[i] = other[j++]; } void -copy_cube_corners(Cube *src, Cube *dst) +epos_to_partial_ep(int epos, int *ep, int *ss) { - memcpy(dst->cp, src->cp, 8 * sizeof(int)); - memcpy(dst->co, src->co, 8 * sizeof(int)); + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; } void -copy_cube_edges(Cube *src, Cube *dst) +fix_eorleoud(CubeArray *arr) { - memcpy(dst->ep, src->ep, 12 * sizeof(int)); - memcpy(dst->eo, src->eo, 12 * sizeof(int)); + int i; + + for (i = 0; i < 12; i++) { + if ((edge_slice(i) == 0 && edge_slice(arr->ep[i]) != 0) || + (edge_slice(i) != 0 && edge_slice(arr->ep[i]) == 0)) { + arr->eorl[i] = 1 - arr->eofb[i]; + } else { + arr->eorl[i] = arr->eofb[i]; + } + + if ((edge_slice(i) == 2 && edge_slice(arr->ep[i]) != 2) || + (edge_slice(i) != 2 && edge_slice(arr->ep[i]) == 2)) { + arr->eoud[i] = 1 - arr->eofb[i]; + } else { + arr->eoud[i] = arr->eofb[i]; + } + } } void -copy_cube(Cube *src, Cube *dst) +fix_cofbcorl(CubeArray *arr) { - copy_cube_centers(src, dst); - copy_cube_corners(src, dst); - copy_cube_edges(src, dst); + int i; + + for (i = 0; i < 8; i++) { + if (i % 2 == arr->cp[i] % 2) { + arr->cofb[i] = arr->coud[i]; + arr->corl[i] = arr->coud[i]; + } else { + if (arr->cp[i] % 2 == 0) { + arr->cofb[i] = (arr->coud[i]+1)%3; + arr->corl[i] = (arr->coud[i]+2)%3; + } else { + arr->cofb[i] = (arr->coud[i]+2)%3; + arr->corl[i] = (arr->coud[i]+1)%3; + } + } + } } -bool -equal(Cube *c1, Cube *c2) +Cube +fourval_to_cube(int eofb, int ep, int coud, int cp) { - int i; + CubeArray *arr; - for (i = 0; i < 12; i++) - if (c1->ep[i] != c2->ep[i] || c1->eo[i] != c2->eo[i]) - return false; + arr = new_cubearray((Cube){0}, pf_all); - for (i = 0; i < 8; i++) - if (c1->cp[i] != c2->cp[i] || c1->co[i] != c2->co[i]) - return false; + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eofb, 2, 12, arr->eofb); + int_to_sum_zero_array(coud, 3, 8, arr->coud); - for (i = 0; i < 6; i++) - if (c1->xp[i] != c2->xp[i]) - return false; + /* fix parity */ + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); - return true; + fix_eorleoud(arr); + fix_cofbcorl(arr); + + return arrays_to_cube(arr, pf_all); } void -invert_cube_centers(Cube *cube) +free_cubearray(CubeArray *arr, PieceFilter f) { - int i; - Cube aux; + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} - copy_cube_centers(cube, &aux); +Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; - for (i = 0; i < 6; i++) - cube->xp[aux.xp[i]] = i; + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; } -void -invert_cube_corners(Cube *cube) +CubeArray * +new_cubearray(Cube cube, PieceFilter f) { - int i; - Cube aux; + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} - copy_cube_corners(cube, &aux); +Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; - for (i = 0; i < 8; i++) { - cube->cp[aux.cp[i]] = i; - cube->co[aux.cp[i]] = (3 - aux.co[i]) % 3; + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; } -void -invert_cube_edges(Cube *cube) +Cube +compose(Cube c2, Cube c1) { - int i; - Cube aux; + return compose_filtered(c2, c1, pf_all); +} - copy_cube_edges(cube, &aux); +int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; - for (i = 0; i < 12; i++) { - cube->ep[aux.ep[i]] = i; - cube->eo[aux.ep[i]] = aux.eo[i]; - } + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; } -void -invert_cube(Cube *cube) +bool +equal(Cube c1, Cube c2) { - invert_cube_centers(cube); - invert_cube_corners(cube); - invert_cube_edges(cube); + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray inv; + Cube ret; + int i, ep[12]; + + for (i = 0; i < 12; i++) + ep[i] = where_is_edge(cube, i); + inv = (CubeArray){.ep = ep}; + ret = arrays_to_cube(&inv, pf_ep); + + ret.eofb = ((int)eo_invtable_e[cube.eofb][cube.epose]) | + ((int)eo_invtable_m[cube.eofb][cube.eposm]) | + ((int)eo_invtable_s[cube.eofb][cube.eposs]); + ret.eorl = ((int)eo_invtable_e[cube.eorl][cube.epose]) | + ((int)eo_invtable_m[cube.eorl][cube.eposm]) | + ((int)eo_invtable_s[cube.eorl][cube.eposs]); + ret.eoud = ((int)eo_invtable_e[cube.eoud][cube.epose]) | + ((int)eo_invtable_m[cube.eoud][cube.eposm]) | + ((int)eo_invtable_s[cube.eoud][cube.eposs]); + ret.cp = cp_invtable[cube.cp]; + ret.cpos = cpos_invtable[cube.cpos]; + ret.coud = co_invtable[cube.coud][cube.cp]; + ret.corl = co_invtable[cube.corl][cube.cp]; + ret.cofb = co_invtable[cube.cofb][cube.cp]; + + return ret; } bool -is_admissible(Cube *c) { - bool perm; - int sign, i; - int sum_e, sum_c; +is_admissible(Cube cube) { - perm = is_perm(c->ep, 12) && is_perm(c->cp, 8) && is_perm(c->xp, 6); + /* TODO: this should check consistency of different orientations */ + /* check also that centers are opposite and admissible */ - sign = perm_sign(c->ep,12) + perm_sign(c->cp,8) + perm_sign(c->xp,6); + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; - for (i = 0, sum_e = 0; i < 12; i++) - if (c->eo[i] > 1) - return false; - else - sum_e += c->eo[i]; + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); - for (i = 0, sum_c = 0; i < 8; i++) - if (c->co[i] > 2) - return false; - else - sum_c += c->co[i]; + free_cubearray(a, pf_all); - return (perm && sign % 2 == 0 && sum_e % 2 == 0 && sum_c % 2 == 0); + return perm && parity % 2 == 0; } bool -is_solved(Cube *cube) +is_solved(Cube cube) { - Cube solved_cube; - make_solved(&solved_cube); - - return equal(cube, &solved_cube); + return equal(cube, (Cube){0}); } -void -make_solved_centers(Cube *cube) +bool +is_block_solved(Cube cube, Block block) { - static int sorted[6] = {0, 1, 2, 3, 4, 5}; + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; - memcpy(cube->xp, sorted, 6 * sizeof(int)); + return true; } -void -make_solved_corners(Cube *cube) +bool +is_solved_center(Cube cube, Center c) { - static int sorted[8] = {0, 1, 2, 3, 4, 5, 6, 7}; - - memcpy(cube->cp, sorted, 8 * sizeof(int)); - memset(cube->co, 0, 8 * sizeof(int)); + return what_center_at(cube, c) == c; } -void -make_solved_edges(Cube *cube) +bool +is_solved_corner(Cube cube, Corner c) { - static int sorted[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} - memcpy(cube->ep, sorted, 12 * sizeof(int)); - memset(cube->eo, 0, 12 * sizeof(int)); +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); } -void -make_solved(Cube *cube) +int +piece_orientation(Cube cube, int piece, char *orientation) { - make_solved_centers(cube); - make_solved_corners(cube); - make_solved_edges(cube); + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; } void -print_cube(Cube *cube) +print_cube(Cube cube) { static char edge_string[12][7] = { [UF] = "UF", [UL] = "UL", [UB] = "UB", [UR] = "UR", @@ -219,52 +545,388 @@ print_cube(Cube *cube) }; for (int i = 0; i < 12; i++) - printf(" %s ", edge_string[cube->ep[i]]); + printf(" %s ", edge_string[what_edge_at(cube, i)]); printf("\n"); for (int i = 0; i < 12; i++) - printf(" %" PRIu8 " ", cube->eo[i]); + printf(" %d ", what_orientation_edge(cube.eofb, i)); printf("\n"); for (int i = 0; i < 8; i++) - printf("%s ", corner_string[cube->cp[i]]); + printf("%s ", corner_string[what_corner_at(cube, i)]); printf("\n"); for (int i = 0; i < 8; i++) - printf(" %" PRIu8 " ", cube->co[i]); + printf(" %d ", what_orientation_corner(cube.coud, i)); printf("\n"); for (int i = 0; i < 6; i++) - printf(" %s ", center_string[cube->xp[i]]); + printf(" %s ", center_string[what_center_at(cube, i)]); printf("\n"); } -int -where_is_center(Center x, Cube *c) +Center +what_center_at(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][i] = arr->cpos[i]; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + int i; + unsigned int ui; + CubeArray *arr; + + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][i] = arr->cp[i]; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + + return aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) { - return where_is_piece(x, c->xp, 6); + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; } int -where_is_corner(Corner k, Cube *c) +what_orientation_corner(int co, Corner c) { - return where_is_piece(k, c->cp, 8); + static bool initialized = false; + static int auxlast[POW3TO7]; + static int auxarr[8]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + auxlast[ui] = auxarr[7]; + } + + initialized = true; + } + + if (c < 7) + return (co / powint(3, c)) % 3; + else + return auxlast[co]; } int -where_is_edge(Edge e, Cube *c) +what_orientation_edge(int eo, Edge e) { - return where_is_piece(e, c->ep, 12); + static bool initialized = false; + static int auxlast[POW2TO11]; + static int auxarr[12]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + auxlast[ui] = auxarr[11]; + } + + initialized = true; + } + + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return auxlast[eo]; } -static int -where_is_piece(int piece, int *arr, int n) +Center +where_is_center(Cube cube, Center c) { - int i; + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][arr->cpos[i]] = i; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } - for (i = 0; i < n; i++) - if (arr[i] == piece) - return i; + return aux[cube.cpos][c]; +} - return -1; +Corner +where_is_corner(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][arr->cp[i]] = i; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + return aux[cube.cp][c]; +} + +Edge +where_is_edge(Cube c, Edge e) +{ + int r0, r1, r2; + + static bool initialized = false; + static int aux[3][BINOM12ON4*FACTORIAL4][12]; + static int i, j; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < BINOM12ON4*FACTORIAL4; ui++) { + for (i = 0; i < 3; i++) + for (j = 0; j < 12; j++) + aux[i][ui][j] = -1; + + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + initialized = true; + } + + r0 = aux[0][c.epose][e]; + r1 = aux[1][c.eposs][e]; + r2 = aux[2][c.eposm][e]; + return MAX(r0, MAX(r1, r2)); +} + +static bool +read_invtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int b; + unsigned int ui, meeo, meco, mecp, mecpos; + bool r; + + strcpy(fname, tabledir); + strcat(fname, "/invtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + b = sizeof(uint16_t); + r = true; + meeo = BINOM12ON4*FACTORIAL4; + meco = FACTORIAL8; + mecp = FACTORIAL8; + mecpos = FACTORIAL6; + + for (ui = 0; ui < POW2TO11; ui++) { + r = r && fread(eo_invtable_e[ui], b, meeo, f) == meeo; + r = r && fread(eo_invtable_m[ui], b, meeo, f) == meeo; + r = r && fread(eo_invtable_s[ui], b, meeo, f) == meeo; + } + + for (ui = 0; ui < POW3TO7; ui++) { + r = r && fread(co_invtable[ui], b, meco, f) == meco; + } + + r = r && fread(cp_invtable, b, mecp, f) == mecp; + r = r && fread(cpos_invtable, b, mecpos, f) == mecpos; + + fclose(f); + return r; +} + +static bool +write_invtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + unsigned int ui, meeo, meco, mecp, mecpos; + int b; + bool r; + + strcpy(fname, tabledir); + strcat(fname, "/invtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + b = sizeof(uint16_t); + r = true; + meeo = BINOM12ON4*FACTORIAL4; + meco = FACTORIAL8; + mecp = FACTORIAL8; + mecpos = FACTORIAL6; + + for (ui = 0; ui < POW2TO11; ui++) { + r = r && fwrite(eo_invtable_e[ui], b, meeo, f) == meeo; + r = r && fwrite(eo_invtable_m[ui], b, meeo, f) == meeo; + r = r && fwrite(eo_invtable_s[ui], b, meeo, f) == meeo; + } + + for (ui = 0; ui < POW3TO7; ui++) { + r = r && fwrite(co_invtable[ui], b, meco, f) == meco; + } + + r = r && fwrite(cp_invtable, b, mecp, f) == mecp; + r = r && fwrite(cpos_invtable, b, mecpos, f) == mecpos; + + fclose(f); + return r; +} + +void +init_inverse() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + if (read_invtables_file()) + return; + + fprintf(stderr, "Cannot load invtables, generating it\n"); + + CubeArray *aux, *inv; + Cube c; + int i, j, eoaux[12], eoinv[12]; + unsigned int ui, uj; + + aux = new_cubearray((Cube){0}, pf_all); + inv = new_cubearray((Cube){0}, pf_all); + + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, eoaux); + for (uj = 0; uj < BINOM12ON4*FACTORIAL4; uj++) { + for (j = 0; j < 12; j++) + eoinv[j] = 0; + c = (Cube){.epose = uj, .eposm = 0, .eposs = 0}; + eoinv[FR] = eoaux[where_is_edge(c, FR)]; + eoinv[FL] = eoaux[where_is_edge(c, FL)]; + eoinv[BL] = eoaux[where_is_edge(c, BL)]; + eoinv[BR] = eoaux[where_is_edge(c, BR)]; + eo_invtable_e[ui][uj] = digit_array_to_int(eoinv,11,2); + + for (j = 0; j < 12; j++) + eoinv[j] = 0; + c = (Cube){.epose = 0, .eposm = uj, .eposs = 0}; + eoinv[UF] = eoaux[where_is_edge(c, UF)]; + eoinv[UB] = eoaux[where_is_edge(c, UB)]; + eoinv[DF] = eoaux[where_is_edge(c, DF)]; + eoinv[DB] = eoaux[where_is_edge(c, DB)]; + eo_invtable_m[ui][uj] = digit_array_to_int(eoinv,11,2); + + for (j = 0; j < 12; j++) + eoinv[j] = 0; + c = (Cube){.epose = 0, .eposm = 0, .eposs = uj}; + eoinv[UL] = eoaux[where_is_edge(c, UL)]; + eoinv[UR] = eoaux[where_is_edge(c, UR)]; + eoinv[DL] = eoaux[where_is_edge(c, DL)]; + eoinv[DR] = eoaux[where_is_edge(c, DR)]; + eo_invtable_s[ui][uj] = digit_array_to_int(eoinv,11,2); + } + } + + for (ui = 0; ui < FACTORIAL8; ui++) { + cube_to_arrays((Cube){.cp = ui}, aux, pf_cp); + for (i = 0; i < 8; i++) + inv->cp[aux->cp[i]] = i; + cp_invtable[ui] = (uint16_t)arrays_to_cube(inv, pf_cp).cp; + + for (uj = 0; uj < POW3TO7; uj++) { + cube_to_arrays((Cube){.coud = uj}, aux, pf_coud); + for (i = 0; i < 8; i++) + inv->coud[aux->cp[i]] = (3-aux->coud[i])%3; + co_invtable[uj][ui] = + (uint16_t)arrays_to_cube(inv, pf_coud).coud; + } + } + + for (ui = 0; ui < FACTORIAL6; ui++) { + cube_to_arrays((Cube){.cpos = ui}, aux, pf_cpos); + for (i = 0; i < 6; i++) + inv->cpos[aux->cpos[i]] = i; + cpos_invtable[ui] = + (uint16_t)arrays_to_cube(inv, pf_cpos).cpos; + } + + free_cubearray(aux, pf_all); + free_cubearray(inv, pf_all); + + if (!write_invtables_file()) + fprintf(stderr, "Error writing invtables\n"); +} + +void +init_cube() +{ + init_inverse(); } diff --git a/src/cube.h b/src/cube.h @@ -3,33 +3,44 @@ #include <stdio.h> -#include "cubetypes.h" #include "env.h" +#include "pf.h" #include "utils.h" -void compose(Cube *c2, Cube *c1); /* Use c2 as an alg on c1 */ -void compose_centers(Cube *c2, Cube *c1); -void compose_corners(Cube *c2, Cube *c1); -void compose_edges(Cube *c2, Cube *c1); -void copy_cube(Cube *src, Cube *dst); -void copy_cube_centers(Cube *src, Cube *dst); -void copy_cube_corners(Cube *src, Cube *dst); -void copy_cube_edges(Cube *src, Cube *dst); -bool equal(Cube *c1, Cube *c2); -void invert_cube(Cube *cube); -void invert_cube_centers(Cube *cube); -void invert_cube_corners(Cube *cube); -void invert_cube_edges(Cube *cube); -bool is_admissible(Cube *cube); -bool is_solved(Cube *cube); -void make_solved(Cube *cube); -void make_solved_centers(Cube *cube); -void make_solved_corners(Cube *cube); -void make_solved_edges(Cube *cube); -void print_cube(Cube *cube); -int where_is_center(Center x, Cube *c); -int where_is_corner(Corner k, Cube *c); -int where_is_edge(Edge e, Cube *c); +Cube admissible_ep(Cube cube, PieceFilter f); +int array_ep_to_epos(int *ep, int *eps_solved); +Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +int edge_slice(Edge e); /* E=0, S=1, M=2 */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +bool is_admissible(Cube cube); +bool is_solved(Cube cube); +bool is_block_solved(Cube cube, Block); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void epos_to_partial_ep(int epos, int *ep, int *ss); +void epos_to_compatible_ep(int epos, int *ep, int *ss); +void fix_eorleoud(CubeArray *arr); +void fix_cofbcorl(CubeArray *arr); +Cube fourval_to_cube(int eofb, int ep, int coud, int cp); +void free_cubearray(CubeArray *arr, PieceFilter f); +Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +CubeArray * new_cubearray(Cube cube, PieceFilter f); +void print_cube(Cube cube); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +void init_cube(); #endif diff --git a/src/cubetypes.h b/src/cubetypes.h @@ -10,8 +10,6 @@ #define NROTATIONS 24 #define entry_group_t uint8_t /* For pruning tables */ -#define MAX_N_COORD 6 - /* Enums *********************************************************************/ typedef enum @@ -30,12 +28,6 @@ corner } Corner; typedef enum -coordtype -{ - COMP_COORD, SYM_COORD, SYMCOMP_COORD -} CoordType; - -typedef enum edge { UF, UL, UB, UR, @@ -84,32 +76,33 @@ trans typedef struct alg Alg; typedef struct alglist AlgList; typedef struct alglistnode AlgListNode; -typedef struct choicestep ChoiceStep; +typedef struct block Block; typedef struct command Command; typedef struct commandargs CommandArgs; typedef struct coordinate Coordinate; typedef struct cube Cube; -/*typedef struct dfsarg DfsArg;*/ -typedef struct fstcube FstCube; -typedef struct indexer Indexer; -typedef struct movable Movable; +typedef struct cubearray CubeArray; +typedef struct dfsarg DfsArg; +typedef struct estimatedata EstimateData; typedef struct moveset Moveset; +typedef struct piecefilter PieceFilter; typedef struct prunedata PruneData; typedef struct solveoptions SolveOptions; typedef struct step Step; typedef struct symdata SymData; typedef struct threaddatasolve ThreadDataSolve; typedef struct threaddatagenpt ThreadDataGenpt; -typedef struct transgroup TransGroup; -typedef bool (*Checker) (Cube *); -typedef bool (*CubeTester) (Cube *, Alg *); -/*typedef bool (*DfsMover) (DfsArg *);*/ -typedef void (*DfsExtraCopier) (void *, void *); -typedef Alg * (*Validator) (Alg *); +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef uint64_t (*CoordMover) (Move, uint64_t); +typedef uint64_t (*CoordTransformer) (Trans, uint64_t); +typedef int (*Estimator) (DfsArg *); +typedef bool (*Validator) (Alg *); typedef void (*Exec) (CommandArgs *); +typedef uint64_t (*Indexer) (Cube); typedef CommandArgs * (*ArgParser) (int, char **); -typedef bool (*Tester) (void); +typedef int (*TransDetector) (Cube, Trans *); typedef int (*TransFinder) (uint64_t, Trans *); @@ -122,10 +115,6 @@ alg bool * inv; int len; int allocated; - Move * move_normal; - int len_normal; - Move * move_inverse; - int len_inverse; }; struct @@ -144,13 +133,11 @@ alglistnode }; struct -choicestep +block { - char * shortname; - char * name; - Step * step[99]; - Trans t[99]; - char * ready_msg; + bool edge[12]; + bool corner[8]; + bool center[6]; }; struct @@ -169,7 +156,7 @@ commandargs bool success; Alg * scramble; SolveOptions * opts; - ChoiceStep * cs; + Step * step; Command * command; /* For help */ int n; char scrtype[20]; @@ -180,119 +167,119 @@ commandargs struct coordinate { - char * name; - CoordType type; - bool generated; - Indexer * i[99]; + Indexer index; uint64_t max; - uint64_t * mtable[NMOVES]; - uint64_t * ttable[NTRANS]; - TransGroup * tgrp; - Coordinate * base[2]; - uint64_t * symclass; - uint64_t * symrep; - Trans * transtorep; - Trans * ttrep_move[NMOVES]; - uint64_t * selfsim; + CoordMover move; + CoordTransformer transform; + SymData * sd; + TransFinder tfind; /* TODO: should be easy to remove */ + Coordinate * base; /* TODO: part of refactor */ }; struct cube { - int ep[12]; - int eo[12]; - int cp[8]; - int co[8]; - int xp[6]; + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; }; -/* struct -movable +cubearray { - uint64_t val; - Trans t; + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; }; -*/ -/* struct dfsarg { - Cube * cube; - Movable ind[MAX_N_COORD]; - Trans t; - Step * s; + Step * step; SolveOptions * opts; + Trans t; + Cube cube; + Cube inverse; int d; - int bound; + uint64_t badmoves; + uint64_t badmovesinv; bool niss; + Move last1; + Move last2; + Move last1inv; + Move last2inv; + EstimateData * ed; AlgList * sols; pthread_mutex_t * sols_mutex; Alg * current_alg; - void * extra; }; -*/ -/* struct -dfsarg +estimatedata { - void * cube_data; - SolveOptions * opts; - int d; - int bound; - bool niss; - AlgList * sols; - Alg * current_alg; - Solver * solver; - Threader * threader; + int corners; + int normal_ud; + int normal_fb; + int normal_rl; + int inverse_ud; + int inverse_fb; + int inverse_rl; + int oldret; }; -*/ struct -fstcube +moveset { - uint16_t uf_eofb; - uint16_t uf_eposepe; - uint16_t uf_coud; - uint16_t uf_cp; - uint16_t fr_eofb; - uint16_t fr_eposepe; - uint16_t fr_coud; - uint16_t rd_eofb; - uint16_t rd_eposepe; - uint16_t rd_coud; + bool (*allowed)(Move); + bool (*allowed_next)(Move, Move, Move); + Move sorted_moves[NMOVES+1]; + uint64_t mask[NMOVES][NMOVES]; }; struct -indexer +piecefilter { - int n; - uint64_t (*index)(Cube *); - void (*to_cube)(uint64_t, Cube *); -}; - -struct -moveset -{ - char * name; - bool (*allowed)(Move); - bool (*can_append)(Alg *, Move, bool); - bool (*cancel_niss)(Alg *); - Move sorted_moves[NMOVES+1]; + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; }; struct prunedata { + char * filename; entry_group_t * ptable; + bool generated; uint64_t n; Coordinate * coord; Moveset * moveset; - uint64_t count[16]; bool compact; int base; + uint64_t count[16]; + PruneData * fallback; + uint64_t fbmod; }; struct @@ -313,30 +300,52 @@ solveoptions struct step { - Checker ready; + char * shortname; + char * name; bool final; - Moveset * moveset; - int n_coord; - Coordinate * coord[MAX_N_COORD]; - Trans coord_trans[MAX_N_COORD]; - PruneData * pd[MAX_N_COORD]; - bool pd_compact[MAX_N_COORD]; + Checker is_done; + Estimator estimate; + Checker ready; + char * ready_msg; Validator is_valid; - /*DfsMover custom_move_checkstop;*/ - DfsExtraCopier copy_extra; + Moveset * moveset; + Trans pre_trans; + TransDetector detect; + int ntables; + PruneData * tables[10]; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + uint64_t * unsym; + Trans * transtorep; + uint64_t * selfsim; + CoordTransformer transform; /* TODO: remove, use that of base coord */ }; -/* struct threaddatasolve { - DfsArg arg; int thid; + Trans t; + Cube cube; + Step * step; + int depth; + SolveOptions * opts; AlgList * start; AlgListNode ** node; + AlgList * sols; pthread_mutex_t * start_mutex; + pthread_mutex_t * sols_mutex; }; -*/ struct threaddatagenpt @@ -350,11 +359,4 @@ threaddatagenpt pthread_mutex_t * upmutex; }; -struct -transgroup -{ - int n; - Trans t[NTRANS]; -}; - #endif diff --git a/src/env.c b/src/env.c @@ -1,5 +1,3 @@ -#define ENV_C - #include "env.h" bool initialized_env = false; diff --git a/src/env.h b/src/env.h @@ -8,8 +8,8 @@ #include <unistd.h> #include <sys/stat.h> -void init_env(); - extern char *tabledir; +void init_env(); + #endif diff --git a/src/fst.c b/src/fst.c @@ -1,401 +0,0 @@ -#define FST_C - -#include "fst.h" - -static FstCube ep_to_fst_epos(int *ep); -static void init_fst_corner_invtables(); -static void init_fst_eo_invtables(); -static void init_fst_eo_update(uint64_t, uint64_t, int, Cube *); -static void init_fst_where_is_edge(); -static bool read_fst_tables_file(); -static bool write_fst_tables_file(); - -static int edge_slice[12] = {[FR] = 0, [FL] = 0, [BL] = 0, [BR] = 0, - [UL] = 1, [UR] = 1, [DR] = 1, [DL] = 1, - [UF] = 2, [UB] = 2, [DF] = 2, [DB] = 2}; - -static uint16_t inv_coud[FACTORIAL8][POW3TO7]; -static uint16_t inv_cp[FACTORIAL8]; -static uint16_t uf_cp_to_fr_cp[FACTORIAL8]; -static uint16_t uf_cp_to_rd_cp[FACTORIAL8]; -static uint16_t eo_invtable[3][POW2TO11][BINOM12ON4*FACTORIAL4]; -static uint16_t fst_where_is_edge_arr[3][12][BINOM12ON4*FACTORIAL4]; - -FstCube -cube_to_fst(Cube *cube) -{ - Cube c; - FstCube ret; - - copy_cube(cube, &c); - ret.uf_eofb = coord_eofb.i[0]->index(&c); - ret.uf_eposepe = coord_eposepe.i[0]->index(&c); - ret.uf_coud = coord_coud.i[0]->index(&c); - ret.uf_cp = coord_cp.i[0]->index(&c); - copy_cube(cube, &c); - apply_trans(fr, &c); - ret.fr_eofb = coord_eofb.i[0]->index(&c); - ret.fr_eposepe = coord_eposepe.i[0]->index(&c); - ret.fr_coud = coord_coud.i[0]->index(&c); - copy_cube(cube, &c); - apply_trans(rd, &c); - ret.rd_eofb = coord_eofb.i[0]->index(&c); - ret.rd_eposepe = coord_eposepe.i[0]->index(&c); - ret.rd_coud = coord_coud.i[0]->index(&c); - - return ret; -} - -static FstCube -ep_to_fst_epos(int *ep) -{ - static int eind[12] = { - [FR] = 0, [FL] = 1, [BL] = 2, [BR] = 3, - [UR] = 0, [DR] = 1, [DL] = 2, [UL] = 3, - [DB] = 0, [DF] = 1, [UF] = 2, [UB] = 3 - }; - static int eptrans_fr[12] = { - [FR] = UF, [DF] = UL, [FL] = UB, [UF] = UR, - [BR] = DF, [DB] = DL, [BL] = DB, [UB] = DR, - [UR] = FR, [DR] = FL, [DL] = BL, [UL] = BR - }; - static int eptrans_rd[12] = { - [DR] = UF, [FR] = UL, [UR] = UB, [BR] = UR, - [DL] = DF, [FL] = DL, [UL] = DB, [BL] = DR, - [DB] = FR, [DF] = FL, [UF] = BL, [UB] = BR - }; - - FstCube ret; - int i, ce, cs, cm; - int epe[4], eps[4], epm[4], epose[12], eposs[12], eposm[12]; - - memset(epose, 0, 12*sizeof(int)); - memset(eposs, 0, 12*sizeof(int)); - memset(eposm, 0, 12*sizeof(int)); - - for (i = 0, ce = 0; i < 12; i++) { - switch (edge_slice[ep[i]]) { - case 0: - epose[i] = 1; - epe[ce++] = eind[ep[i]]; - break; - case 1: - eposs[eptrans_fr[i]] = eind[ep[i]] + 1; - break; - default: - eposm[eptrans_rd[i]] = eind[ep[i]] + 1; - break; - } - } - - for (i = 0, cs = 0, cm = 0; i < 12; i++) { - if (eposs[i]) { - eps[cs++] = eposs[i] - 1; - eposs[i] = 1; - } - if (eposm[i]) { - epm[cm++] = eposm[i] - 1; - eposm[i] = 1; - } - } - - ret.uf_eposepe = subset_to_index(epose, 12, 4) * FACTORIAL4 + - perm_to_index(epe, 4); - ret.fr_eposepe = subset_to_index(eposs, 12, 4) * FACTORIAL4 + - perm_to_index(eps, 4); - ret.rd_eposepe = subset_to_index(eposm, 12, 4) * FACTORIAL4 + - perm_to_index(epm, 4); - - return ret; -} - -FstCube -fst_inverse(FstCube fst) -{ - FstCube ret; - int ep_inv[12]; - - ep_inv[FR] = fst_where_is_edge_arr[0][FR][fst.uf_eposepe]; - ep_inv[FL] = fst_where_is_edge_arr[0][FL][fst.uf_eposepe]; - ep_inv[BL] = fst_where_is_edge_arr[0][BL][fst.uf_eposepe]; - ep_inv[BR] = fst_where_is_edge_arr[0][BR][fst.uf_eposepe]; - - ep_inv[UR] = fst_where_is_edge_arr[1][UR][fst.fr_eposepe]; - ep_inv[UL] = fst_where_is_edge_arr[1][UL][fst.fr_eposepe]; - ep_inv[DR] = fst_where_is_edge_arr[1][DR][fst.fr_eposepe]; - ep_inv[DL] = fst_where_is_edge_arr[1][DL][fst.fr_eposepe]; - - ep_inv[UF] = fst_where_is_edge_arr[2][UF][fst.rd_eposepe]; - ep_inv[UB] = fst_where_is_edge_arr[2][UB][fst.rd_eposepe]; - ep_inv[DF] = fst_where_is_edge_arr[2][DF][fst.rd_eposepe]; - ep_inv[DB] = fst_where_is_edge_arr[2][DB][fst.rd_eposepe]; - - ret = ep_to_fst_epos(ep_inv); - - ret.uf_eofb = ((uint16_t)eo_invtable[0][fst.uf_eofb][fst.uf_eposepe]) | - ((uint16_t)eo_invtable[1][fst.uf_eofb][fst.fr_eposepe]) | - ((uint16_t)eo_invtable[2][fst.uf_eofb][fst.rd_eposepe]); - ret.fr_eofb = ((uint16_t)eo_invtable[0][fst.fr_eofb][fst.uf_eposepe]) | - ((uint16_t)eo_invtable[1][fst.fr_eofb][fst.fr_eposepe]) | - ((uint16_t)eo_invtable[2][fst.fr_eofb][fst.rd_eposepe]); - ret.rd_eofb = ((uint16_t)eo_invtable[0][fst.rd_eofb][fst.uf_eposepe]) | - ((uint16_t)eo_invtable[1][fst.rd_eofb][fst.fr_eposepe]) | - ((uint16_t)eo_invtable[2][fst.rd_eofb][fst.rd_eposepe]); - - ret.uf_cp = inv_cp[fst.uf_cp]; - - ret.uf_coud = inv_coud[fst.uf_cp][fst.uf_coud]; - ret.fr_coud = inv_coud[uf_cp_to_fr_cp[fst.uf_cp]][fst.fr_coud]; - ret.rd_coud = inv_coud[uf_cp_to_rd_cp[fst.uf_cp]][fst.rd_coud]; - - return ret; -} - -FstCube -fst_move(Move m, FstCube fst) -{ - FstCube ret; - Move m_fr, m_rd; - - m_fr = transform_move(fr, m); - m_rd = transform_move(rd, m); - - ret.uf_eofb = coord_eofb.mtable[m][fst.uf_eofb]; - ret.uf_eposepe = coord_eposepe.mtable[m][fst.uf_eposepe]; - ret.uf_coud = coord_coud.mtable[m][fst.uf_coud]; - ret.uf_cp = coord_cp.mtable[m][fst.uf_cp]; - - ret.fr_eofb = coord_eofb.mtable[m_fr][fst.fr_eofb]; - ret.fr_eposepe = coord_eposepe.mtable[m_fr][fst.fr_eposepe]; - ret.fr_coud = coord_coud.mtable[m_fr][fst.fr_coud]; - - ret.rd_eofb = coord_eofb.mtable[m_rd][fst.rd_eofb]; - ret.rd_eposepe = coord_eposepe.mtable[m_rd][fst.rd_eposepe]; - ret.rd_coud = coord_coud.mtable[m_rd][fst.rd_coud]; - - return ret; -} - -void -fst_to_cube(FstCube fst, Cube *cube) -{ - Cube e, s, m; - int i; - - coord_eposepe.i[0]->to_cube(fst.uf_eposepe, &e); - coord_eposepe.i[0]->to_cube(fst.fr_eposepe, &s); - apply_trans(inverse_trans(fr), &s); - coord_eposepe.i[0]->to_cube(fst.rd_eposepe, &m); - apply_trans(inverse_trans(rd), &m); - - for (i = 0; i < 12; i++) { - if (edge_slice[e.ep[i]] == 0) - cube->ep[i] = e.ep[i]; - if (edge_slice[s.ep[i]] == 1) - cube->ep[i] = s.ep[i]; - if (edge_slice[m.ep[i]] == 2) - cube->ep[i] = m.ep[i]; - } - - coord_eofb.i[0]->to_cube((uint64_t)fst.uf_eofb, cube); - coord_coud.i[0]->to_cube((uint64_t)fst.uf_coud, cube); - coord_cp.i[0]->to_cube((uint64_t)fst.uf_cp, cube); - - for (i = 0; i < 6; i++) - cube->xp[i] = i; -} - -void -init_fst() -{ - init_trans(); - gen_coord(&coord_eofb); - gen_coord(&coord_eposepe); - gen_coord(&coord_coud); - gen_coord(&coord_cp); - - if (!read_fst_tables_file()) { - fprintf(stderr, - "Could not load fst_tables, generating them\n"); - init_fst_corner_invtables(); - init_fst_eo_invtables(); - init_fst_where_is_edge(); - if (!write_fst_tables_file()) - fprintf(stderr, "fst_tables could not be written\b"); - } -} - -static void -init_fst_corner_invtables() -{ - Cube c, d; - uint64_t cp, coud; - - for (cp = 0; cp < FACTORIAL8; cp++) { - make_solved_corners(&c); - coord_cp.i[0]->to_cube(cp, &c); - - copy_cube_corners(&c, &d); - invert_cube_corners(&d); - inv_cp[cp] = coord_cp.i[0]->index(&d); - - for (coud = 0; coud < POW3TO7; coud++) { - copy_cube_corners(&c, &d); - coord_coud.i[0]->to_cube(coud, &d); - invert_cube_corners(&d); - inv_coud[cp][coud] = coord_coud.i[0]->index(&d); - } - - copy_cube_corners(&c, &d); - apply_trans(fr, &d); - uf_cp_to_fr_cp[cp] = coord_cp.i[0]->index(&d); - - copy_cube_corners(&c, &d); - apply_trans(rd, &d); - uf_cp_to_rd_cp[cp] = coord_cp.i[0]->index(&d); - } -} - -static void -init_fst_eo_invtables() -{ - uint64_t ep, eo; - Cube c, d; - - for (ep = 0; ep < BINOM12ON4 * FACTORIAL4; ep++) { - make_solved(&c); - coord_eposepe.i[0]->to_cube(ep, &c); - for (eo = 0; eo < POW2TO11; eo++) { - copy_cube_edges(&c, &d); - coord_eofb.i[0]->to_cube(eo, &d); - init_fst_eo_update(eo, ep, 0, &d); - - apply_trans(inverse_trans(fr), &d); - coord_eofb.i[0]->to_cube(eo, &d); - init_fst_eo_update(eo, ep, 1, &d); - - copy_cube_edges(&c, &d); - apply_trans(inverse_trans(rd), &d); - coord_eofb.i[0]->to_cube(eo, &d); - init_fst_eo_update(eo, ep, 2, &d); - } - } -} - -static void -init_fst_eo_update(uint64_t eo, uint64_t ep, int s, Cube *d) -{ - int i; - - for (i = 0; i < 12; i++) { - if (edge_slice[d->ep[i]] == s && d->eo[i] && d->ep[i] != 11) - eo_invtable[s][eo][ep] |= - ((uint16_t)1) << ((uint16_t)d->ep[i]); - } -} - -static void -init_fst_where_is_edge() -{ - Cube c, d; - uint64_t e; - - make_solved(&c); - for (e = 0; e < BINOM12ON4 * FACTORIAL4; e++) { - coord_eposepe.i[0]->to_cube(e, &c); - - copy_cube_edges(&c, &d); - fst_where_is_edge_arr[0][FR][e] = where_is_edge(FR, &d); - fst_where_is_edge_arr[0][FL][e] = where_is_edge(FL, &d); - fst_where_is_edge_arr[0][BL][e] = where_is_edge(BL, &d); - fst_where_is_edge_arr[0][BR][e] = where_is_edge(BR, &d); - - copy_cube_edges(&c, &d); - apply_trans(inverse_trans(fr), &d); - fst_where_is_edge_arr[1][UL][e] = where_is_edge(UL, &d); - fst_where_is_edge_arr[1][UR][e] = where_is_edge(UR, &d); - fst_where_is_edge_arr[1][DL][e] = where_is_edge(DL, &d); - fst_where_is_edge_arr[1][DR][e] = where_is_edge(DR, &d); - - copy_cube_edges(&c, &d); - apply_trans(inverse_trans(rd), &d); - fst_where_is_edge_arr[2][UF][e] = where_is_edge(UF, &d); - fst_where_is_edge_arr[2][UB][e] = where_is_edge(UB, &d); - fst_where_is_edge_arr[2][DF][e] = where_is_edge(DF, &d); - fst_where_is_edge_arr[2][DB][e] = where_is_edge(DB, &d); - } -} - -static bool -read_fst_tables_file() -{ - init_env(); - - FILE *f; - char fname[strlen(tabledir)+256]; - uint64_t i, j, r, total; - - strcpy(fname, tabledir); - strcat(fname, "/fst_tables"); - - if ((f = fopen(fname, "rb")) == NULL) - return false; - - r = 0; - total = FACTORIAL8*(POW3TO7+3) + 3*BINOM12ON4*FACTORIAL4*(12+POW2TO11); - - for (i = 0; i < FACTORIAL8; i++) - r += fread(inv_coud[i], sizeof(uint16_t), POW3TO7, f); - r += fread(inv_cp, sizeof(uint16_t), FACTORIAL8, f); - r += fread(uf_cp_to_fr_cp, sizeof(uint16_t), FACTORIAL8, f); - r += fread(uf_cp_to_rd_cp, sizeof(uint16_t), FACTORIAL8, f); - for (i = 0; i < 3; i++) - for (j = 0; j < POW2TO11; j++) - r += fread(eo_invtable[i][j], - sizeof(uint16_t), BINOM12ON4*FACTORIAL4, f); - for (i = 0; i < 3; i++) - for (j = 0; j < 12; j++) - r += fread(fst_where_is_edge_arr[i][j], - sizeof(uint16_t), BINOM12ON4*FACTORIAL4, f); - - fclose(f); - - return r == total; -} - -static bool -write_fst_tables_file() -{ - init_env(); - - FILE *f; - char fname[strlen(tabledir)+256]; - uint64_t i, j, w, total; - - strcpy(fname, tabledir); - strcat(fname, "/fst_tables"); - - if ((f = fopen(fname, "wb")) == NULL) - return false; - - w = 0; - total = FACTORIAL8*(POW3TO7+3) + 3*BINOM12ON4*FACTORIAL4*(12+POW2TO11); - - for (i = 0; i < FACTORIAL8; i++) - w += fwrite(inv_coud[i], sizeof(uint16_t), POW3TO7, f); - w += fwrite(inv_cp, sizeof(uint16_t), FACTORIAL8, f); - w += fwrite(uf_cp_to_fr_cp, sizeof(uint16_t), FACTORIAL8, f); - w += fwrite(uf_cp_to_rd_cp, sizeof(uint16_t), FACTORIAL8, f); - for (i = 0; i < 3; i++) - for (j = 0; j < POW2TO11; j++) - w += fwrite(eo_invtable[i][j], - sizeof(uint16_t), BINOM12ON4*FACTORIAL4, f); - for (i = 0; i < 3; i++) - for (j = 0; j < 12; j++) - w += fwrite(fst_where_is_edge_arr[i][j], - sizeof(uint16_t), BINOM12ON4*FACTORIAL4, f); - - fclose(f); - - return w == total; -} diff --git a/src/fst.h b/src/fst.h @@ -1,13 +0,0 @@ -#ifndef FST_H -#define FST_H - -#include "coord.h" - -FstCube cube_to_fst(Cube *cube); -FstCube fst_inverse(FstCube fst); -FstCube fst_move(Move m, FstCube fst); -void fst_to_cube(FstCube fst, Cube *cube); -void init_fst(); - -#endif - diff --git a/src/moves.c b/src/moves.c @@ -1,18 +1,79 @@ -#define MOVES_C - #include "moves.h" /* Local functions ***********************************************************/ +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); static void cleanup_aux(Alg *alg, Alg *ret, bool inv); +static bool read_mtables_file(); +static bool write_mtables_file(); /* Tables and other data *****************************************************/ -/* Moves are represented as cubes and applied using compose(). Every move is * - * translated to a an <U, x, y> alg before filling the transition tables. * - * See init_moves(). */ +/* Every move is translated to a an <U, x, y> alg before filling the + transition tables, see init_moves() */ + +static int edge_cycle[NMOVES][12] = +{ + [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, + [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, + [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL } +}; + +static int corner_cycle[NMOVES][8] = +{ + [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR }, + [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR }, + [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL } +}; + +static int center_cycle[NMOVES][6] = +{ + [x] = { F_center, B_center, R_center, L_center, D_center, U_center }, + [y] = { U_center, D_center, B_center, F_center, R_center, L_center } +}; + +static int eofb_flipped[NMOVES][12] = { + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; + +static int eorl_flipped[NMOVES][12] = { + [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; + +static int eoud_flipped[NMOVES][12] = { + [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } +}; + +static int coud_flipped[NMOVES][8] = { + [x] = { + [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, + [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 + } +}; + +static int corl_flipped[NMOVES][8] = { + [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + } +}; -static Cube move_array[NMOVES]; +static int cofb_flipped[NMOVES][8] = { + [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + }, + [y] = { + [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1, + [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2 + } +}; static char equiv_alg_string[100][NMOVES] = { [NULLMOVE] = "", @@ -76,52 +137,89 @@ static char equiv_alg_string[100][NMOVES] = { [z3] = " y x yyy " }; +/* Transition tables, to be loaded up at the beginning */ +int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +int eofb_mtable[NMOVES][POW2TO11]; +int eorl_mtable[NMOVES][POW2TO11]; +int eoud_mtable[NMOVES][POW2TO11]; +int cp_mtable[NMOVES][FACTORIAL8]; +int coud_mtable[NMOVES][POW3TO7]; +int cofb_mtable[NMOVES][POW3TO7]; +int corl_mtable[NMOVES][POW3TO7]; +int cpos_mtable[NMOVES][FACTORIAL6]; + + +/* Local functions implementation ********************************************/ + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + /*init_moves();*/ + + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} /* Public functions **********************************************************/ -void -apply_alg(Alg *alg, Cube *cube) +Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) { - Cube aux; + Cube ret = {0}; int i; - copy_cube(cube, &aux); - make_solved(cube); - for (i = 0; i < alg->len; i++) if (alg->inv[i]) - apply_move(alg->move[i], cube); + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); - invert_cube(cube); - compose(&aux, cube); + ret = compose_filtered(c, inverse_cube(ret), f); for (i = 0; i < alg->len; i++) if (!alg->inv[i]) - apply_move(alg->move[i], cube); -} + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); -void -apply_move(Move m, Cube *cube) -{ - compose(&move_array[m], cube); + return ret; } -void -apply_move_centers(Move m, Cube *cube) +Cube +apply_alg(Alg *alg, Cube cube) { - compose_centers(&move_array[m], cube); + return apply_alg_generic(alg, cube, pf_all, true); } -void -apply_move_corners(Move m, Cube *cube) +Cube +apply_move(Move m, Cube cube) { - compose_corners(&move_array[m], cube); -} - -void -apply_move_edges(Move m, Cube *cube) -{ - compose_edges(&move_array[m], cube); + /*init_moves();*/ + + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; } Alg * @@ -181,27 +279,31 @@ cleanup_aux(Alg *alg, Alg *ret, bool inv) { int i, j; Cube c, d; - Move m; + Move m, mm; Alg *equiv_alg; - make_solved(&c); + c = (Cube){0}; for (i = 0; i < alg->len; i++) { if (alg->inv[i] != inv) continue; equiv_alg = new_alg(equiv_alg_string[alg->move[i]]); - for (j = 0; j < equiv_alg->len; j++) - if (equiv_alg->move[j] == U) - append_move(ret, 3 * c.xp[U_center] + 1, inv); - else - apply_move(equiv_alg->move[j], &c); + for (j = 0; j < equiv_alg->len; j++) { + m = equiv_alg->move[j]; + if (m == U) { + mm = 3*what_center_at(c, U_center) + 1; + append_move(ret, mm, inv); + } else { + c = apply_move(m, c); + } + } free_alg(equiv_alg); } m = NULLMOVE; - switch (c.xp[F_center]) { + switch (what_center_at(c, F_center)) { case U_center: m = x3; break; @@ -215,7 +317,7 @@ cleanup_aux(Alg *alg, Alg *ret, bool inv) m = y3; break; case B_center: - if (c.xp[U_center] == U_center) + if (what_center_at(c, U_center) == U_center) m = y2; else m = x2; @@ -223,24 +325,120 @@ cleanup_aux(Alg *alg, Alg *ret, bool inv) default: break; } - - make_solved(&d); - apply_move(m, &d); + d = apply_move(m, (Cube){0}); if (m != NULLMOVE) append_move(ret, m, inv); m = NULLMOVE; - if (c.xp[U_center] == d.xp[D_center]) { + if (what_center_at(c, U_center) == what_center_at(d, D_center)) { m = z2; - } else if (c.xp[U_center] == d.xp[R_center]) { + } else if (what_center_at(c, U_center) == what_center_at(d, R_center)) { m = z3; - } else if (c.xp[U_center] == d.xp[L_center]) { + } else if (what_center_at(c, U_center) == what_center_at(d, L_center)) { m = z; } if (m != NULLMOVE) append_move(ret, m, inv); } +static bool +read_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + void init_moves() { static bool initialized = false; @@ -248,54 +446,101 @@ init_moves() { return; initialized = true; + Cube c; + CubeArray arrs; + int i; + unsigned int ui; Move m; Alg *equiv_alg[NMOVES]; - static const Cube mcu = { - .ep = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, - .cp = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR }, - }; - static const Cube mcx = { - .ep = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, - .eo = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, - .cp = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR }, - .co = { [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, - [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 }, - .xp = { F_center, B_center, R_center, - L_center, D_center, U_center }, - }; - static const Cube mcy = { - .ep = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL }, - .eo = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 }, - .cp = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL }, - .xp = { U_center, D_center, B_center, - F_center, R_center, L_center }, - }; + init_cube(); + + for (i = 0; i < NMOVES; i++) + equiv_alg[i] = new_alg(equiv_alg_string[i]); - move_array[U] = mcu; - move_array[x] = mcx; - move_array[y] = mcy; + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; - for (m = 0; m < NMOVES; m++) - equiv_alg[m] = new_alg(equiv_alg_string[m]); + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + /* Initialize transition tables */ for (m = 0; m < NMOVES; m++) { - switch (m) { - case NULLMOVE: - make_solved(&move_array[m]); - break; - case U: - case x: - case y: - break; - default: - make_solved(&move_array[m]); - apply_alg(equiv_alg[m], &move_array[m]); - break; + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; } } - for (m = 0; m < NMOVES; m++) - free_alg(equiv_alg[m]); + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); + + for (i = 0; i < NMOVES; i++) + free_alg(equiv_alg[i]); } diff --git a/src/moves.h b/src/moves.h @@ -5,11 +5,24 @@ #include "cube.h" #include "env.h" -void apply_alg(Alg *alg, Cube *cube); -void apply_move(Move m, Cube *cube); -void apply_move_centers(Move m, Cube *cube); -void apply_move_corners(Move m, Cube *cube); -void apply_move_edges(Move m, Cube *cube); +/* + * Tables are exposed to allow for faster moves in some cases. + */ +extern int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +extern int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +extern int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +extern int eofb_mtable[NMOVES][POW2TO11]; +extern int eorl_mtable[NMOVES][POW2TO11]; +extern int eoud_mtable[NMOVES][POW2TO11]; +extern int cp_mtable[NMOVES][FACTORIAL8]; +extern int coud_mtable[NMOVES][POW3TO7]; +extern int cofb_mtable[NMOVES][POW3TO7]; +extern int corl_mtable[NMOVES][POW3TO7]; +extern int cpos_mtable[NMOVES][FACTORIAL6]; + +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +Cube apply_move(Move m, Cube cube); Alg * cleanup(Alg *alg); void init_moves(); diff --git a/src/movesets.c b/src/movesets.c @@ -1,194 +0,0 @@ -#define MOVESETS_C - -#include "movesets.h" - -static bool allowed_HTM(Move m); -static bool allowed_URF(Move m); -static bool allowed_eofb(Move m); -static bool allowed_drud(Move m); -static bool allowed_htr(Move m); -static bool can_append_HTM(Move l2, Move l1, Move m); -static bool can_append_HTM_cached(Alg *alg, Move m, bool inverse); -static bool cancel_niss_HTM_cached(Alg *alg); -static void init_can_append_HTM(); - -Moveset -moveset_HTM = { - .name = "HTM", - .allowed = allowed_HTM, - .can_append = can_append_HTM_cached, - .cancel_niss = cancel_niss_HTM_cached, -}; - -Moveset -moveset_URF = { - .name = "URF", - .allowed = allowed_URF, - .can_append = can_append_HTM_cached, - .cancel_niss = cancel_niss_HTM_cached, -}; - -Moveset -moveset_eofb = { - .name = "eofb", - .allowed = allowed_eofb, - .can_append = can_append_HTM_cached, - .cancel_niss = cancel_niss_HTM_cached, -}; - -Moveset -moveset_drud = { - .name = "drud", - .allowed = allowed_drud, - .can_append = can_append_HTM_cached, - .cancel_niss = cancel_niss_HTM_cached, -}; - -Moveset -moveset_htr = { - .name = "htr", - .allowed = allowed_htr, - .can_append = can_append_HTM_cached, - .cancel_niss = cancel_niss_HTM_cached, -}; - -Moveset * -all_movesets[] = { - &moveset_HTM, - &moveset_URF, - &moveset_eofb, - &moveset_drud, - &moveset_htr, - NULL -}; - -static uint64_t can_append_HTM_mask[NMOVES][NMOVES]; - -static bool -allowed_HTM(Move m) -{ - return m >= U && m <= B3; -} - -static bool -allowed_URF(Move m) -{ - Move b = base_move(m); - - return b == U || b == R || b == F; -} - -static bool -allowed_eofb(Move m) -{ - Move b = base_move(m); - - return b == U || b == D || b == R || b == L || - ((b == F || b == B) && m == b+1); -} - -static bool -allowed_drud(Move m) -{ - Move b = base_move(m); - - return b == U || b == D || - ((b == R || b == L || b == F || b == B) && m == b + 1); -} - -static bool -allowed_htr(Move m) -{ - Move b = base_move(m); - - return moveset_HTM.allowed(m) && m == b + 1; -} - -static bool -can_append_HTM(Move l2, Move l1, Move m) -{ - bool cancel, cancel_last, cancel_swap; - - cancel_last = l1 != NULLMOVE && base_move(l1) == base_move(m); - cancel_swap = l2 != NULLMOVE && base_move(l2) == base_move(m); - cancel = cancel_last || (commute(l1, l2) && cancel_swap); - - return !cancel; -} - -static bool -can_append_HTM_cached(Alg *alg, Move m, bool inverse) -{ - Move *moves, l1, l2; - uint64_t mbit; - int n; - - if (inverse) { - moves = alg->move_inverse; - n = alg->len_inverse; - } else { - moves = alg->move_normal; - n = alg->len_normal; - } - - l1 = n > 0 ? moves[n-1] : NULLMOVE; - l2 = n > 1 ? moves[n-2] : NULLMOVE; - - mbit = ((uint64_t)1) << m; - - return can_append_HTM_mask[l2][l1] & mbit; -} - -static bool -cancel_niss_HTM_cached(Alg *alg) -{ - Move i1, i2; - int n; - bool can_first, can_swap; - - n = alg->len_inverse; - i1 = n > 0 ? alg->move_inverse[n-1] : NULLMOVE; - i2 = n > 1 ? alg->move_inverse[n-2] : NULLMOVE; - - can_first = can_append_HTM_cached(alg, inverse_move(i1), false); - can_swap = can_append_HTM_cached(alg, inverse_move(i2), false); - - return can_first && (!commute(i1, i2) || can_swap); -} - -static void -init_can_append_HTM() -{ - Move l2, l1, m; - - for (l1 = 0; l1 < NMOVES; l1++) - for (l2 = 0; l2 < NMOVES; l2++) - for (m = 0; m < NMOVES; m++) - if (can_append_HTM(l2, l1, m)) - can_append_HTM_mask[l2][l1] - |= (((uint64_t)1) << m); -} - -void -init_moveset(Moveset *ms) -{ - int j; - Move m; - - for (j = 0, m = U; m < NMOVES; m++) - if (ms->allowed(m)) - ms->sorted_moves[j++] = m; - ms->sorted_moves[j] = NULLMOVE; - -/* TODO: should be here? maybe just init all movesets together anyway... */ - init_can_append_HTM(); -} - -void -init_movesets() -{ - int i; - - for (i = 0; all_movesets[i] != NULL; i++) - init_moveset(all_movesets[i]); -} diff --git a/src/movesets.h b/src/movesets.h @@ -1,15 +0,0 @@ -#ifndef MOVESETS_H -#define MOVESETS_H - -#include "alg.h" - -void init_moveset(Moveset *); -void init_movesets(); - -extern Moveset moveset_HTM; -extern Moveset moveset_URF; -extern Moveset moveset_eofb; -extern Moveset moveset_drud; -extern Moveset moveset_htr; - -#endif diff --git a/src/pf.c b/src/pf.c @@ -0,0 +1,95 @@ +#include "pf.h" + +PieceFilter +pf_all = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .eorl = true, + .eoud = true, + .cp = true, + .cofb = true, + .corl = true, + .coud = true, + .cpos = true +}; + +PieceFilter +pf_4val = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .coud = true, + .cp = true +}; + +PieceFilter +pf_epcp = { + .epose = true, + .eposs = true, + .eposm = true, + .cp = true +}; + +PieceFilter +pf_cpos = { + .cpos = true +}; + +PieceFilter +pf_cp = { + .cp = true +}; + +PieceFilter +pf_ep = { + .epose = true, + .eposs = true, + .eposm = true +}; + +PieceFilter +pf_e = { + .epose = true +}; + +PieceFilter +pf_s = { + .eposs = true +}; + +PieceFilter +pf_m = { + .eposm = true +}; + +PieceFilter +pf_eo = { + .eofb = true, + .eorl = true, + .eoud = true +}; + +PieceFilter +pf_co = { + .cofb = true, + .corl = true, + .coud = true +}; + +PieceFilter +pf_coud = { + .coud = true +}; + +PieceFilter +pf_edges = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .eorl = true, + .eoud = true +}; diff --git a/src/pf.h b/src/pf.h @@ -0,0 +1,20 @@ +#ifndef PF_H +#define PF_H + +#include "cubetypes.h" + +extern PieceFilter pf_all; +extern PieceFilter pf_4val; +extern PieceFilter pf_epcp; +extern PieceFilter pf_cpos; +extern PieceFilter pf_cp; +extern PieceFilter pf_ep; +extern PieceFilter pf_e; +extern PieceFilter pf_s; +extern PieceFilter pf_m; +extern PieceFilter pf_eo; +extern PieceFilter pf_co; +extern PieceFilter pf_coud; +extern PieceFilter pf_edges; + +#endif diff --git a/src/pruning.c b/src/pruning.c @@ -1,5 +1,3 @@ -#define PRUNING_C - #include "pruning.h" #define ENTRIES_PER_GROUP (2*sizeof(entry_group_t)) @@ -7,14 +5,106 @@ static int findchunk(PruneData *pd, int nchunks, uint64_t i); static void genptable_bfs(PruneData *pd, int d, int nt, int nc); +static void genptable_compress(PruneData *pd); static void genptable_fixnasty(PruneData *pd, int d, int nthreads); +static void genptable_setbase(PruneData *pd); static void * instance_bfs(void *arg); static void * instance_fixnasty(void *arg); -static void ptable_update(PruneData *pd, uint64_t ind, int m); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void ptable_update_index(PruneData *pd, uint64_t ind, int m); +static int ptableval_index(PruneData *pd, uint64_t ind); static bool read_ptable_file(PruneData *pd); static bool write_ptable_file(PruneData *pd); -PruneData *active_pd[256]; +PruneData +pd_eofb_HTM = { + .filename = "pt_eofb_HTM", + .coord = &coord_eofb, + .moveset = &moveset_HTM, +}; + +PruneData +pd_coud_HTM = { + .filename = "pt_coud_HTM", + .coord = &coord_coud, + .moveset = &moveset_HTM, +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "pt_cornershtr_HTM", + .coord = &coord_cornershtr, + .moveset = &moveset_HTM, +}; + +PruneData +pd_corners_HTM = { + .filename = "pt_corners_HTM", + .coord = &coord_corners, + .moveset = &moveset_HTM, +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "pt_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = &moveset_HTM, +}; + +PruneData +pd_drud_eofb = { + .filename = "pt_drud_eofb", + .coord = &coord_drud_eofb, + .moveset = &moveset_eofb, +}; + +PruneData +pd_drudfin_noE_sym16_drud = { + .filename = "pt_drudfin_noE_sym16_drud", + .coord = &coord_drudfin_noE_sym16, + .moveset = &moveset_drud, +}; + +PruneData +pd_htr_drud = { + .filename = "pt_htr_drud", + .coord = &coord_htr_drud, + .moveset = &moveset_drud, +}; + +PruneData +pd_htrfin_htr = { + .filename = "pt_htrfin_htr", + .coord = &coord_htrfin, + .moveset = &moveset_htr, +}; + +PruneData +pd_nxopt31_HTM = { + .filename = "pt_nxopt31_HTM", + .coord = &coord_nxopt31, + .moveset = &moveset_HTM, + + .compact = true, + .fallback = &pd_drud_sym16_HTM, + .fbmod = BINOM8ON4, +}; + +PruneData * all_pd[] = { + &pd_eofb_HTM, + &pd_coud_HTM, + &pd_cornershtr_HTM, + &pd_corners_HTM, + &pd_drud_sym16_HTM, + &pd_drud_eofb, + &pd_drudfin_noE_sym16_drud, + &pd_htr_drud, + &pd_htrfin_htr, + &pd_nxopt31_HTM, + NULL +}; + +/* Functions *****************************************************************/ int findchunk(PruneData *pd, int nchunks, uint64_t i) @@ -27,47 +117,59 @@ findchunk(PruneData *pd, int nchunks, uint64_t i) return MIN(nchunks-1, (int)(i / chunksize)); } -PruneData * +void +free_pd(PruneData *pd) +{ + if (pd->generated) + free(pd->ptable); + + pd->generated = false; +} + +void genptable(PruneData *pd, int nthreads) { - int d, nchunks, i, maxv; - uint64_t oldn; - - for (i = 0; active_pd[i] != NULL; i++) { - if (active_pd[i]->coord == pd->coord && - active_pd[i]->moveset == pd->moveset && - active_pd[i]->compact == pd->compact) - return active_pd[i]; - } + bool compact; + int d, nchunks; + uint64_t oldn, sz; - init_moveset(pd->moveset); - gen_coord(pd->coord); + if (pd->generated) + return; - pd->ptable = malloc(ptablesize(pd) * sizeof(entry_group_t)); + /* TODO: check if memory is enough, otherwise maybe exit gracefully? */ + sz = ptablesize(pd) * (pd->compact ? 2 : 1); + pd->ptable = malloc(sz * sizeof(entry_group_t)); - if (read_ptable_file(pd)) - goto genptable_done; + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } if (nthreads < 4) { fprintf(stderr, "--- Warning ---\n" "You are using only %d threads to generate the pruning" - "tables. This can take a while.\n" + "tables. This can take a while." "Unless you did this intentionally, you should re-run" "this command with `-t 4' or more.\n" "---------------\n\n", nthreads ); } + + /* For the first steps we proceed the same way for compact and not */ + compact = pd->compact; + pd->compact = false; + pd->generated = true; + nchunks = MIN(ptablesize(pd), 100000); - fprintf(stderr, "Generating pt_%s_%s with %d threads\n", - pd->coord->name, pd->moveset->name, nthreads); + fprintf(stderr, "Cannot load %s, generating it with %d threads\n", + pd->filename, nthreads); + memset(pd->ptable, ~(uint8_t)0, ptablesize(pd)*sizeof(entry_group_t)); - for (i = 0; i < 16; i++) - pd->count[i] = 0; - ptable_update(pd, 0, 0); + ptable_update(pd, (Cube){0}, 0); pd->n = 1; oldn = 0; genptable_fixnasty(pd, 0, nthreads); @@ -76,9 +178,7 @@ genptable(PruneData *pd, int nthreads) 0, pd->n - oldn, pd->n, pd->coord->max); oldn = pd->n; pd->count[0] = pd->n; - - maxv = pd->compact ? MIN(15, pd->base + 4) : 15; - for (d = 0; d < maxv && pd->n < pd->coord->max; d++) { + for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { genptable_bfs(pd, d, nthreads, nchunks); genptable_fixnasty(pd, d+1, nthreads); fprintf(stderr, "Depth %d done, generated %" @@ -87,17 +187,14 @@ genptable(PruneData *pd, int nthreads) pd->count[d+1] = pd->n - oldn; oldn = pd->n; } - if (pd->compact) - fprintf(stderr, "Compact table, values above " - "%d are inaccurate.\n", maxv-1); fprintf(stderr, "Pruning table generated!\n"); - + + genptable_setbase(pd); + if (compact) + genptable_compress(pd); + if (!write_ptable_file(pd)) fprintf(stderr, "Error writing ptable file\n"); - -genptable_done: - for (i = 0; active_pd[i] != NULL; i++); - return active_pd[i] = pd; } static void @@ -135,6 +232,31 @@ genptable_bfs(PruneData *pd, int d, int nthreads, int nchunks) } static void +genptable_compress(PruneData *pd) +{ + int val; + uint64_t i, j; + entry_group_t mask, v; + + fprintf(stderr, "Compressing table to 2 bits per entry\n"); + + for (i = 0; i < pd->coord->max; i += ENTRIES_PER_GROUP_COMPACT) { + mask = (entry_group_t)0; + for (j = 0; j < ENTRIES_PER_GROUP_COMPACT; j++) { + if (i+j >= pd->coord->max) + break; + val = ptableval_index(pd, i+j) - pd->base; + v = (entry_group_t)MIN(3, MAX(0, val)); + mask |= v << (2*j); + } + pd->ptable[i/ENTRIES_PER_GROUP_COMPACT] = mask; + } + + pd->compact = true; + pd->ptable = realloc(pd->ptable, sizeof(entry_group_t)*ptablesize(pd)); +} + +static void genptable_fixnasty(PruneData *pd, int d, int nthreads) { int i; @@ -142,7 +264,7 @@ genptable_fixnasty(PruneData *pd, int d, int nthreads) ThreadDataGenpt td[nthreads]; pthread_mutex_t *upmtx; - if (pd->coord->type != SYMCOMP_COORD) + if (pd->coord->tfind == NULL) return; upmtx = malloc(sizeof(pthread_mutex_t)); @@ -162,12 +284,28 @@ genptable_fixnasty(PruneData *pd, int d, int nthreads) free(upmtx); } +static void +genptable_setbase(PruneData *pd) +{ + int i; + uint64_t sum, newsum; + + pd->base = 0; + sum = pd->count[0] + pd->count[1] + pd->count[2]; + for (i = 3; i < 16; i++) { + newsum = sum + pd->count[i] - pd->count[i-3]; + if (newsum > sum) + pd->base = i-3; + sum = newsum; + } +} + static void * instance_bfs(void *arg) { ThreadDataGenpt *td; uint64_t i, ii, blocksize, rmin, rmax, updated; - int j, pval, ichunk, oldc, newc; + int j, pval, ichunk; Move *ms; td = (ThreadDataGenpt *)arg; @@ -178,43 +316,25 @@ instance_bfs(void *arg) td->pd->coord->max : ((uint64_t)td->thid + 1) * blocksize; - if (td->pd->compact) { - if (td->d <= td->pd->base) { - oldc = 1; - newc = 1; - } else { - oldc = td->d - td->pd->base; - newc = td->d - td->pd->base; - } - } else { - oldc = td->d; - newc = td->d + 1; - } - updated = 0; for (i = rmin; i < rmax; i++) { ichunk = findchunk(td->pd, td->nchunks, i); pthread_mutex_lock(td->mutex[ichunk]); - pval = ptableval(td->pd, i); + pval = ptableval_index(td->pd, i); pthread_mutex_unlock(td->mutex[ichunk]); - if (pval == oldc) { + if (pval == td->d) { for (j = 0; ms[j] != NULLMOVE; j++) { - ii = move_coord(td->pd->coord, ms[j], i, NULL); + ii = td->pd->coord->move(ms[j], i); ichunk = findchunk(td->pd, td->nchunks, ii); pthread_mutex_lock(td->mutex[ichunk]); - pval = ptableval(td->pd, ii); - if (pval > newc) { - ptable_update(td->pd, ii, newc); + pval = ptableval_index(td->pd, ii); + if (pval > td->d+1) { + ptable_update_index(td->pd, + ii, td->d+1); updated++; } pthread_mutex_unlock(td->mutex[ichunk]); } - if (td->pd->compact && td->d <= td->pd->base) { - ichunk = findchunk(td->pd, td->nchunks, i); - pthread_mutex_lock(td->mutex[ichunk]); - ptable_update(td->pd, i, 0); - pthread_mutex_unlock(td->mutex[ichunk]); - } } } @@ -229,40 +349,30 @@ static void * instance_fixnasty(void *arg) { ThreadDataGenpt *td; - uint64_t i, ii, blocksize, rmin, rmax, updated, ss, M; - int j, oldc; - Trans t; + uint64_t i, ii, nb, blocksize, rmin, rmax, updated; + int j, n; + Trans t, aux[NTRANS]; td = (ThreadDataGenpt *)arg; - - /* We know type = SYMCOMP_COORD */ - M = td->pd->coord->base[1]->max; - blocksize = (td->pd->coord->base[0]->max / td->nthreads) * M; + nb = td->pd->coord->max / td->pd->coord->base->max; + blocksize = (td->pd->coord->base->max / td->nthreads) * nb; rmin = ((uint64_t)td->thid) * blocksize; rmax = td->thid == td->nthreads - 1 ? td->pd->coord->max : ((uint64_t)td->thid + 1) * blocksize; - if (td->pd->compact) { - if (td->d <= td->pd->base) - oldc = 1; - else - oldc = td->d - td->pd->base; - } else { - oldc = td->d; - } - updated = 0; for (i = rmin; i < rmax; i++) { - if (ptableval(td->pd, i) == oldc) { - ss = td->pd->coord->base[0]->selfsim[i/M]; - for (j = 0; j < td->pd->coord->base[0]->tgrp->n; j++) { - t = td->pd->coord->base[0]->tgrp->t[j]; - if (t == uf || !(ss & ((uint64_t)1<<t))) + if (ptableval_index(td->pd, i) == td->d) { + if ((n = td->pd->coord->tfind(i, aux)) == 1) + continue; + + for (j = 0; j < n; j++) { + if ((t = aux[j]) == uf) continue; - ii = trans_coord(td->pd->coord, t, i); - if (ptableval(td->pd, ii) > oldc) { - ptable_update(td->pd, ii, oldc); + ii = td->pd->coord->transform(t, i); + if (ptableval_index(td->pd, ii) > td->d) { + ptable_update_index(td->pd, ii, td->d); updated++; } } @@ -281,13 +391,11 @@ print_ptable(PruneData *pd) { uint64_t i; - printf("Table %s_%s\n", pd->coord->name, pd->moveset->name); - - if (pd->compact) { - printf("Compract table with base value: %d\n", pd->base); - printf("Values above %d are inaccurate.\n", pd->base + 3); - } - + if (!pd->generated) + genptable(pd, 1); /* TODO: set default nthreads somewhere */ + + printf("Table %s\n", pd->filename); + printf("Base value: %d\n", pd->base); for (i = 0; i < 16; i++) printf("%2" PRIu64 "\t%10" PRIu64 "\n", i, pd->count[i]); } @@ -303,31 +411,48 @@ ptablesize(PruneData *pd) } static void -ptable_update(PruneData *pd, uint64_t ind, int n) +ptable_update(PruneData *pd, Cube cube, int n) { - int sh; - entry_group_t f, mask; - uint64_t i, e, b; + ptable_update_index(pd, pd->coord->index(cube), n); +} - e = pd->compact ? ENTRIES_PER_GROUP_COMPACT : ENTRIES_PER_GROUP; - b = pd->compact ? 2 : 4; - f = pd->compact ? 3 : 15; +static void +ptable_update_index(PruneData *pd, uint64_t ind, int n) +{ + int sh; + entry_group_t mask; + uint64_t i; - sh = b * (ind % e); - mask = f << sh; - i = ind / e; + sh = 4 * (ind % ENTRIES_PER_GROUP); + mask = ((entry_group_t)15) << sh; + i = ind/ENTRIES_PER_GROUP; pd->ptable[i] &= ~mask; - pd->ptable[i] |= (((entry_group_t)n) & f) << sh; + pd->ptable[i] |= (((entry_group_t)n)&15) << sh; } int -ptableval(PruneData *pd, uint64_t ind) +ptableval(PruneData *pd, Cube cube) { - int sh; - uint64_t e; + return ptableval_index(pd, pd->coord->index(cube)); +} + +static int +ptableval_index(PruneData *pd, uint64_t ind) +{ + int sh, ret; + entry_group_t mask; + uint64_t i, e; entry_group_t m; + if (!pd->generated) { + fprintf(stderr, "Warning: request pruning table value" + " for uninitialized table %s.\n It's fine, but it" + " should not happen. Please report bug.\n", + pd->filename); + genptable(pd, 1); /* TODO: set default or remove this case */ + } + if (pd->compact) { e = ENTRIES_PER_GROUP_COMPACT; m = 3; @@ -338,7 +463,19 @@ ptableval(PruneData *pd, uint64_t ind) sh = (ind % e) * 4; } - return (pd->ptable[ind/e] & (m << sh)) >> sh; + mask = m << sh; + i = ind/e; + + ret = (pd->ptable[i] & mask) >> sh; + + if (pd->compact) { + if (ret) + ret += pd->base; + else + ret = ptableval_index(pd->fallback, ind / pd->fbmod); + } + + return ret; } static bool @@ -347,15 +484,13 @@ read_ptable_file(PruneData *pd) init_env(); FILE *f; - char fname[strlen(tabledir)+256]; + char fname[strlen(tabledir)+100]; int i; uint64_t r; strcpy(fname, tabledir); - strcat(fname, "/pt_"); - strcat(fname, pd->coord->name); - strcat(fname, "_"); - strcat(fname, pd->moveset->name); + strcat(fname, "/"); + strcat(fname, pd->filename); if ((f = fopen(fname, "rb")) == NULL) return false; @@ -376,15 +511,13 @@ write_ptable_file(PruneData *pd) init_env(); FILE *f; - char fname[strlen(tabledir)+256]; + char fname[strlen(tabledir)+100]; int i; uint64_t w; strcpy(fname, tabledir); - strcat(fname, "/pt_"); - strcat(fname, pd->coord->name); - strcat(fname, "_"); - strcat(fname, pd->moveset->name); + strcat(fname, "/"); + strcat(fname, pd->filename); if ((f = fopen(fname, "wb")) == NULL) return false; diff --git a/src/pruning.h b/src/pruning.h @@ -1,16 +1,26 @@ #ifndef PRUNING_H #define PRUNING_H -#include "coord.h" -#include "movesets.h" +#include "symcoord.h" + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_drud_sym16_HTM; +extern PruneData pd_drud_eofb; +extern PruneData pd_drudfin_noE_sym16_drud; +extern PruneData pd_htr_drud; +extern PruneData pd_htrfin_htr; +extern PruneData pd_nxopt31_HTM; + +extern PruneData * all_pd[]; void free_pd(PruneData *pd); -PruneData * genptable(PruneData *data, int nthreads); +void genptable(PruneData *pd, int nthreads); void print_ptable(PruneData *pd); uint64_t ptablesize(PruneData *pd); -int ptableval(PruneData *pd, uint64_t ind); - -extern PruneData *active_pd[256]; +int ptableval(PruneData *pd, Cube cube); #endif diff --git a/src/shell.c b/src/shell.c @@ -1,5 +1,3 @@ -#define SHELL_C - #include "shell.h" static void cleanwhitespaces(char *line); @@ -9,9 +7,7 @@ bool checkfiles() { /* TODO: add more checks (other files, use checksum...) */ - /* How to check for pruning tables with new method? */ - /* Solution: use list of steps */ - /* + FILE *f; char fname[strlen(tabledir)+100]; int i; @@ -24,7 +20,6 @@ checkfiles() else fclose(f); } - */ return true; } @@ -142,14 +137,12 @@ launch(bool batchmode) free(shell_argv); } -#ifndef TEST int main(int argc, char *argv[]) { char *closing_cmd[1] = { "freemem" }; init_env(); - init_trans(); if (!checkfiles()) { fprintf(stderr, @@ -174,4 +167,3 @@ main(int argc, char *argv[]) return 0; } -#endif diff --git a/src/solve.c b/src/solve.c @@ -1,122 +1,500 @@ -#define SOLVE_C - #include "solve.h" -void -dfs(DfsArg *arg, Solver *solver, Threader *threader) +/* Local functions ***********************************************************/ + +static bool allowed_next(Move move, DfsArg *arg); +static bool cancel_niss(DfsArg *arg); +static void copy_dfsarg(DfsArg *src, DfsArg *dst); +static void dfs(DfsArg *arg); +static void dfs_branch(DfsArg *arg); +static bool dfs_check_solved(DfsArg *arg); +static bool dfs_switch(DfsArg *arg); +static void dfs_niss(DfsArg *arg); +static bool dfs_stop(DfsArg *arg); +static void * instance_thread(void *arg); +static void invert_branch(DfsArg *arg); +static void multidfs(Cube c, Trans t, Step *s, SolveOptions *opts, + AlgList *sols, int d); +static bool niss_makes_sense(DfsArg *arg); +static bool solvestop(int d, int op, SolveOptions *opts, AlgList *sols); + +/* Local functions ***********************************************************/ + +static bool +allowed_next(Move m, DfsArg *arg) +{ + bool bad, allowed, order; + uint64_t mbit; + + mbit = ((uint64_t)1) << m; + bad = mbit & arg->badmoves; + allowed = mbit & arg->step->moveset->mask[arg->last2][arg->last1]; + order = !commute(arg->last1, m) || arg->last1 < m; + + return allowed && !bad && order; +} + +static bool +cancel_niss(DfsArg *arg) +{ + Moveset *ms; + Move i1, i2; + bool p, p1, p2, q, q1, q2; + + if (arg->last1inv == NULLMOVE) + return false; + + ms = arg->step->moveset; + i1 = inverse_move(arg->last1inv); + i2 = inverse_move(arg->last2inv); + + p1 = !ms->allowed_next(arg->last2, arg->last1, i1); + p2 = !ms->allowed_next(arg->last2, i1, arg->last1); + p = p1 || (commute(i1, arg->last1) && p2); + + q1 = !ms->allowed_next(arg->last2, arg->last1, i2); + q2 = !ms->allowed_next(arg->last2, i2, arg->last1); + q = q1 || (commute(i2, arg->last1) && q2); + + return p || (commute(i1, i2) && q); +} + +static void +copy_dfsarg(DfsArg *src, DfsArg *dst) +{ + dst->step = src->step; + dst->opts = src->opts; + dst->t = src->t; + dst->cube = src->cube; + dst->inverse = src->inverse; + dst->d = src->d; + dst->badmoves = src->badmoves; + dst->badmovesinv = src->badmovesinv; + dst->niss = src->niss; + dst->last1 = src->last1; + dst->last2 = src->last2; + dst->last1inv = src->last1inv; + dst->last2inv = src->last2inv; + dst->sols = src->sols; + dst->sols_mutex = src->sols_mutex; + dst->current_alg = src->current_alg; + + copy_estimatedata(src->ed, dst->ed); +} + +static void +dfs(DfsArg *arg) +{ + bool sw = false; + + if (dfs_stop(arg)) + return; + + if (dfs_check_solved(arg)) + return; + + if (arg->step->final && (sw = dfs_switch(arg))) + invert_branch(arg); + dfs_branch(arg); + + if (arg->opts->can_niss && !arg->niss && niss_makes_sense(arg)) + dfs_niss(arg); + + if (sw) + invert_branch(arg); +} + +static void +dfs_branch(DfsArg *arg) { int i; - DfsArg newarg; - Alg *sol; Move m; + DfsArg *newarg; - if (arg->current_alg->len > arg->d) - return; + newarg = malloc(sizeof(DfsArg)); + newarg->ed = malloc(sizeof(EstimateData)); + + for (i = 0; arg->step->moveset->sorted_moves[i] != NULLMOVE; i++) { + m = arg->step->moveset->sorted_moves[i]; + if (allowed_next(m, arg)) { + copy_dfsarg(arg, newarg); + newarg->last2 = arg->last1; + newarg->last1 = m; + newarg->cube = apply_move(m, arg->cube); + append_move(arg->current_alg, m, newarg->niss); - if (solver->is_solved(solver->param, arg->cubedata)) { -/* TODO: the "all" option should be re-implemented as setting -validate to null */ - -/* TODO: we also have to check if cancel with NISS; -we can't because we have no access to the s->final field -this should be done by the step's validator? */ - sol = solver->validate_solution(solver->param,arg->current_alg); - bool accepted = sol != NULL; - bool too_short = arg->current_alg->len != arg->d; - - if (accepted && !too_short) { -/* TODO: arg->t got lost in refactoring */ -/* transform_alg(inverse_trans(arg->t), sol);*/ - if (arg->opts->verbose) - print_alg(sol, false); - threader->append_sol(sol, arg->threaddata); + dfs(newarg); + + arg->current_alg->len--; } - return; } - if (arg->current_alg->len == arg->d) - return; + free(newarg->ed); + free(newarg); +} + +static bool +dfs_check_solved(DfsArg *arg) +{ + if (!arg->step->is_done(arg->cube)) + return false; -/* TODO: do not alloc */ - newarg.cubedata = solver->alloc_cubedata(solver->param); - for (i = 0; solver->moveset->sorted_moves[i] != NULLMOVE; i++) { - m = solver->moveset->sorted_moves[i]; - if (solver->moveset->can_append(arg->current_alg, m, arg->niss) - && compare_last(arg->current_alg, m, arg->niss) >= 0) { - append_move(arg->current_alg, m, arg->niss); - - solver->copy_cubedata( - solver->param, arg->cubedata, newarg.cubedata); - newarg.threaddata = arg->threaddata; - newarg.opts = arg->opts; - newarg.d = arg->d; - newarg.niss = arg->niss; - newarg.current_alg = arg->current_alg; - if (!solver->move_check_stop( - solver->param, &newarg, threader)) - dfs(&newarg, solver, threader); - - remove_last_move(arg->current_alg); + if (arg->current_alg->len == arg->d) { + if ((arg->step->is_valid(arg->current_alg) || arg->opts->all) + && (!arg->step->final || !cancel_niss(arg))) { + + pthread_mutex_lock(arg->sols_mutex); + + if (arg->sols->len < arg->opts->max_solutions) { + append_alg(arg->sols, arg->current_alg); + + transform_alg( + inverse_trans(arg->t), + arg->sols->last->alg + ); + if (arg->step->final) + inplace(unniss, arg->sols->last->alg); + + if (arg->opts->verbose) + print_alg(arg->sols->last->alg, false); + } + + pthread_mutex_unlock(arg->sols_mutex); } } - solver->free_cubedata(solver->param, newarg.cubedata); - - if (arg->opts->can_niss && !arg->niss && - solver->niss_makes_sense( - solver->param, arg->cubedata, arg->current_alg)) { - solver->invert_cube(solver->param, arg->cubedata); - arg->niss = true; - dfs(arg, solver, threader); + + return true; +} + +static void +dfs_niss(DfsArg *arg) +{ + DfsArg *newarg; + + newarg = malloc(sizeof(DfsArg)); + newarg->ed = malloc(sizeof(EstimateData)); + + copy_dfsarg(arg, newarg); + swapmove(&(newarg->last1), &(newarg->last1inv)); + swapmove(&(newarg->last2), &(newarg->last2inv)); + newarg->niss = !(arg->niss); + newarg->cube = inverse_cube(arg->cube); + + dfs(newarg); + + free(newarg->ed); + free(newarg); +} + +static bool +dfs_stop(DfsArg *arg) +{ + int lowerbound; + bool b; + + lowerbound = arg->step->estimate(arg); + if (arg->opts->can_niss && !arg->niss) + lowerbound = MIN(1, lowerbound); + + if (arg->current_alg->len + lowerbound > arg->d) { + b = true; + } else { + pthread_mutex_lock(arg->sols_mutex); + b = arg->sols->len >= arg->opts->max_solutions; + pthread_mutex_unlock(arg->sols_mutex); } + + return b; } +static bool +dfs_switch(DfsArg *arg) +{ + int i, bn, bi; + + bn = 0; + for (i = 0; arg->step->moveset->sorted_moves[i] != NULLMOVE; i++) + if (allowed_next(arg->step->moveset->sorted_moves[i], arg)) + bn++; + + swapmove(&(arg->last1), &(arg->last1inv)); + swapmove(&(arg->last2), &(arg->last2inv)); + swapu64(&(arg->badmoves), &(arg->badmovesinv)); + + bi = 0; + for (i = 0; arg->step->moveset->sorted_moves[i] != NULLMOVE; i++) + if (allowed_next(arg->step->moveset->sorted_moves[i], arg)) + bi++; + + swapmove(&(arg->last1), &(arg->last1inv)); + swapmove(&(arg->last2), &(arg->last2inv)); + swapu64(&(arg->badmoves), &(arg->badmovesinv)); + + return bi < bn; +} + +static void * +instance_thread(void *arg) +{ + bool b; + Cube c; + ThreadDataSolve *td; + AlgListNode *node; + DfsArg darg; + + td = (ThreadDataSolve *)arg; + + while (1) { + b = false; + + pthread_mutex_lock(td->start_mutex); + if ((node = *(td->node)) == NULL) + b = true; + else + *(td->node) = (*(td->node))->next; + pthread_mutex_unlock(td->start_mutex); + + if (b) + break; + + c = node->alg->inv[0] ? + apply_move(node->alg->move[0], inverse_cube(td->cube)) : + apply_move(node->alg->move[0], td->cube); + + darg.step = td->step; + darg.opts = td->opts; + darg.t = td->t; + darg.cube = c; + darg.d = td->depth; + darg.niss = node->alg->inv[0]; + darg.last1 = node->alg->move[0]; + darg.last2 = NULLMOVE; + darg.last1inv = NULLMOVE; + darg.last2inv = NULLMOVE; + darg.sols = td->sols; + darg.sols_mutex = td->sols_mutex; + darg.current_alg = new_alg(""); + append_move(darg.current_alg, node->alg->move[0], + node->alg->inv[0]); + darg.ed = malloc(sizeof(EstimateData)); + reset_estimatedata(darg.ed); + darg.badmoves = 0; + darg.badmovesinv = 0; + + dfs(&darg); + + free_alg(darg.current_alg); + free(darg.ed); + } + + return NULL; +} + +static void +invert_branch(DfsArg *arg) +{ + Cube aux; + + aux = arg->cube; + arg->cube = is_solved(arg->inverse) ? + inverse_cube(arg->cube) : arg->inverse; + arg->inverse = aux; + + swapu64(&(arg->badmoves), &(arg->badmovesinv)); + arg->niss = !(arg->niss); + swapmove(&(arg->last1), &(arg->last1inv)); + swapmove(&(arg->last2), &(arg->last2inv)); + invert_estimatedata(arg->ed); +} + +static void +multidfs(Cube c, Trans tr, Step *s, SolveOptions *opts, AlgList *sols, int d) +{ + int i; + Alg *alg; + AlgList *start; + AlgListNode **node; + pthread_t t[opts->nthreads]; + ThreadDataSolve td[opts->nthreads]; + pthread_mutex_t *start_mutex, *sols_mutex; + + node = malloc(sizeof(AlgListNode *)); + start_mutex = malloc(sizeof(pthread_mutex_t)); + sols_mutex = malloc(sizeof(pthread_mutex_t)); + + start = new_alglist(); + pthread_mutex_init(start_mutex, NULL); + pthread_mutex_init(sols_mutex, NULL); + + for (i = 0; s->moveset->sorted_moves[i] != NULLMOVE; i++) { + alg = new_alg(""); + append_move(alg, s->moveset->sorted_moves[i], false); + append_alg(start, alg); + if (opts->can_niss) { + alg->inv[0] = true; + append_alg(start, alg); + } + free_alg(alg); + } + *node = start->first; + + for (i = 0; i < opts->nthreads; i++) { + td[i].thid = i; + td[i].t = tr; + td[i].cube = c; + td[i].step = s; + td[i].depth = d; + td[i].opts = opts; + td[i].start = start; + td[i].node = node; + td[i].sols = sols; + td[i].start_mutex = start_mutex; + td[i].sols_mutex = sols_mutex; + pthread_create(&t[i], NULL, instance_thread, &td[i]); + } + + for (i = 0; i < opts->nthreads; i++) + pthread_join(t[i], NULL); + + free_alglist(start); + free(node); + free(start_mutex); + free(sols_mutex); +} + +static bool +niss_makes_sense(DfsArg *arg) +{ + Cube testcube; + + testcube = apply_move(inverse_move(arg->last1), (Cube){0}); + return arg->current_alg->len == 0 || !arg->step->is_done(testcube); +} + +static bool +solvestop(int d, int op, SolveOptions *opts, AlgList *sols) +{ + bool opt_done, max_moves_exceeded, max_sols_exceeded; + + opt_done = opts->optimal != -1 && op != -1 && d > opts->optimal + op; + max_moves_exceeded = d > opts->max_moves; + max_sols_exceeded = sols->len >= opts->max_solutions; + + return opt_done || max_moves_exceeded || max_sols_exceeded; +} + +/* Public functions **********************************************************/ + AlgList * -solve(Cube *cube, SolveOptions *opts, Solver **solver, Threader *threader) +solve(Cube cube, Step *step, SolveOptions *opts) { - int i, d, optimal; - bool ready[MAX_SOLVERS], stop, one_ready; - DfsArg arg[MAX_SOLVERS]; + bool ready; + int i, d, op, nt; AlgList *sols; + Cube c; + Trans tt[NTRANS]; + + prepare_step(step, opts); - one_ready = false; - for (i = 0; solver[i] != NULL; i++) { - arg[i].cubedata = - solver[i]->prepare_cube(solver[i]->param, cube); - arg[i].opts = opts; - ready[i] = arg[i].cubedata != NULL; - one_ready = one_ready || ready[i]; + if (step->detect != NULL) { + nt = step->detect(cube, tt); + } else { + tt[0] = step->pre_trans; + ready = step->ready == NULL || + step->ready(apply_trans(tt[0], cube)); + nt = ready ? 1 : 0; } sols = new_alglist(); - if (!one_ready) { - fprintf(stderr, "Cube not ready for solving\n"); + + if (nt == 0) { + fprintf(stderr, "Cube not ready for solving step: "); + fprintf(stderr, "%s\n", step->ready_msg); return sols; } - optimal = opts->max_moves; - stop = false; - for (d = opts->min_moves; d <= opts->max_moves && !stop; d++) { + if (opts->min_moves == 0) { + for (i = 0; i < nt; i++) { + c = apply_trans(tt[i], cube); + if (step->is_done(c)) { + append_alg(sols, new_alg("")); + return sols; + } + } + } + + op = -1; + for (d = opts->min_moves; !solvestop(d, op, opts, sols); d++) { if (opts->verbose) fprintf(stderr, "Searching depth %d\n", d); - for (i = 0; solver[i] != NULL && !stop; i++) { - if (!ready[i]) - continue; + for (i = 0; i < nt && !solvestop(d, op, opts, sols); i++) { + c = apply_trans(tt[i], cube); + multidfs(c, tt[i], step, opts, sols, d); + if (sols->len > 0 && op == -1) + op = d; + } + } - arg[i].d = d; - threader->dispatch(&arg[i], sols, solver[i], threader); + return sols; +} - if (sols->len > 0) - optimal = MIN(optimal, d); +/* TODO: make more general! */ +Alg * +solve_2phase(Cube cube, int nthreads) +{ + int bestlen, newb; + Alg *bestalg, *ret; + AlgList *sols1, *sols2; + AlgListNode *i; + Cube c; + SolveOptions opts1, opts2; + + opts1.min_moves = 0; + opts1.max_moves = 13; + opts1.max_solutions = 20; + opts1.nthreads = nthreads; + opts1.optimal = 3; + opts1.can_niss = false; + opts1.verbose = false; + opts1.all = true; - stop = sols->len >= opts->max_solutions; + opts2.min_moves = 0; + opts2.max_moves = 19; + opts2.max_solutions = 1; + opts2.nthreads = nthreads; + opts2.can_niss = false; + opts2.verbose = false; + + /* We skip step1 if it is solved on any axis */ + if (drany_HTM.is_done(cube)) { + sols1 = new_alglist(); + append_alg(sols1, new_alg("")); + } else { + sols1 = solve(cube, &drany_HTM, &opts1); + } + bestalg = new_alg(""); + bestlen = 999; + for (i = sols1->first; i != NULL; i = i->next) { + c = apply_alg(i->alg, cube); + sols2 = solve(c, &dranyfin_DR, &opts2); + + if (sols2->len > 0) { + newb = i->alg->len + sols2->first->alg->len; + if (newb < bestlen) { + bestlen = newb; + copy_alg(i->alg, bestalg); + compose_alg(bestalg, sols2->first->alg); + } } - stop = stop || - (opts->optimal != -1 && d >= opts->optimal + optimal); + + free_alglist(sols2); } -/* TODO: some cleanup (free cubedata) */ -/* TODO: actually, preparation should be done somewhere else */ + free_alglist(sols1); - return sols; + ret = cleanup(bestalg); + free_alg(bestalg); + + return ret; } diff --git a/src/solve.h b/src/solve.h @@ -2,53 +2,10 @@ #define SOLVE_H #include "moves.h" +#include "steps.h" +#include "trans.h" -#define MAX_SOLVERS 99 - -typedef struct dfsarg DfsArg; -typedef struct threader Threader; -typedef struct solver Solver; - -/* TODO: add solver and threader in DfsData, remove from dispatch args and similar */ - -struct dfsarg { - void * cubedata; - void * threaddata; - SolveOptions * opts; - int d; - bool niss; - Alg * current_alg; -}; - -struct threader { - void (*append_sol)(Alg *, void *); - void (*dispatch)(DfsArg *, AlgList *, Solver *, Threader *); - int (*get_nsol)(void *); -/* TODO: threader should have param, like solver? */ -}; - -struct solver { - Moveset * moveset; - bool (*move_check_stop)(void *, DfsArg *, Threader *); - Alg * (*validate_solution)(void *, Alg *); - bool (*niss_makes_sense)(void *, void *, Alg *); -/* TODO: move param to somewhere where it makes more sense */ - void * param; -/* TODO: the following should be part of a generic cube description */ -/* TODO: remove alloc? */ -/* TODO: revisit apply_move, maybe apply_alg? or both? */ - void * (*alloc_cubedata)(void *); - void (*copy_cubedata)(void *, void *, void *); - void (*free_cubedata)(void *, void *); - void (*invert_cube)(void *, void *); - bool (*is_solved)(void *, void *); - void (*apply_move)(void *, void *, Move); -/* TODO: remove dependence on Cube, preparation should be done before */ - void * (*prepare_cube)(void *, Cube *); -}; - -void dfs(DfsArg *, Solver *, Threader *); -/* TODO: remove dependence on Cube, preparation should be done before */ -AlgList * solve(Cube *, SolveOptions *, Solver **, Threader *); +AlgList * solve(Cube cube, Step *step, SolveOptions *opts); +Alg * solve_2phase(Cube cube, int nthreads); #endif diff --git a/src/solver_step.c b/src/solver_step.c @@ -1,306 +0,0 @@ -#include "solver_step.h" - -typedef struct { - Cube * cube; - uint64_t * val; - Trans * t; -} CubeData; - -static void apply_move_cubedata(void *, void *, Move); -static void init_indexes(Step *, CubeData *); -static void * prepare_cube(void *, Cube *); -static bool move_check_stop_eager(void *, DfsArg *, Threader *); -static bool move_check_stop_lazy(void *, DfsArg *, Threader *); -static bool move_check_stop_nonsol(void *, DfsArg *, Threader *); -static bool is_solved_step(void *, void *); -static Alg * validate_solution(void *, Alg *); -static void * alloc_cubedata(void *); -static void copy_cubedata(void *, void *, void *); -static void free_cubedata(void *, void *); -static void invert_cubedata(void *, void *); -static bool niss_makes_sense(void *, void *, Alg *); -static Solver * new_stepsolver_nocheckstop(Step *step); - -static void -apply_move_cubedata(void *param, void *cubedata, Move m) -{ - Step *s = (Step *)param; - CubeData *data = (CubeData *)cubedata; - - Trans tt; - for (int i = 0; i < s->n_coord; i++) { - Move mm = transform_move(data->t[i], m); - data->val[i] = move_coord(s->coord[i], mm, data->val[i], &tt); - data->t[i] = transform_trans(tt, data->t[i]); - } -} - -static void -init_indexes(Step *step, CubeData *data) -{ - int i; - Cube moved; - Trans t, tt; - - for (i = 0; i < step->n_coord; i++) { - t = step->coord_trans[i]; - copy_cube(data->cube, &moved); - apply_trans(t, &moved); - data->val[i] = index_coord(step->coord[i], &moved, &tt); - data->t[i] = transform_trans(tt, t); - } -} - -static void * -prepare_cube(void *param, Cube *cube) -{ - int i; - Step *s; - CubeData *data; - - s = (Step *)param; - - for (i = 0; i < s->n_coord; i++) { - s->pd[i] = malloc(sizeof(PruneData)); - s->pd[i]->moveset = s->moveset; -/* TODO: check if moveset initialization works fine, - e.g. if there is a variable to save the initialized status - or if it gets initialized multiple times */ - init_moveset(s->moveset); - s->pd[i]->coord = s->coord[i]; - gen_coord(s->coord[i]); - s->pd[i]->compact = s->pd_compact[i]; - s->pd[i] = genptable(s->pd[i], 4); /* TODO: threads */ - } - - data = alloc_cubedata(param); - data->cube = malloc(sizeof(Cube)); - copy_cube(cube, data->cube); - init_indexes(s, data); - - return data; -} - -static bool -move_check_stop_eager(void *param, DfsArg *arg, Threader *threader) -{ - int nsol; - - if (move_check_stop_nonsol(param, arg, threader)) - return true; - - nsol = threader->get_nsol(arg->threaddata); - return nsol >= arg->opts->max_solutions; -} - -static bool -move_check_stop_lazy(void *param, DfsArg *arg, Threader *threader) -{ - int nsol; - - nsol = threader->get_nsol(arg->threaddata); - if (nsol >= arg->opts->max_solutions) - return true; - - return move_check_stop_nonsol(param, arg, threader); -} - -/* TODO: split in 2 (nissable / non-nissable) and only move cube - when nissable */ -static bool -move_check_stop_nonsol(void *param, DfsArg *arg, Threader *threader) -{ - int i, goal, bound; - Move mm, lastmove; - Trans tt = uf; - CubeData *data; - Step *s; - - s = (Step *)param; - data = (CubeData *)arg->cubedata; - - - bound = 0; - goal = arg->d - arg->current_alg->len; -/* TODO: check if len is 0 */ - lastmove = arg->current_alg->move[arg->current_alg->len-1]; - for (i = 0; i < s->n_coord; i++) { - mm = transform_move(data->t[i], lastmove); - data->val[i] = move_coord(s->coord[i], mm, data->val[i], &tt); - data->t[i] = transform_trans(tt, data->t[i]); - - bound = MAX(bound, ptableval(s->pd[i], data->val[i])); - if (arg->opts->can_niss && !arg->niss) - bound = MIN(1, bound); - - if (bound > goal) { - return true; - } - } - if (arg->opts->can_niss && !arg->niss) - apply_move(lastmove, data->cube); - - return false; -} - -static bool -is_solved_step(void *param, void *cubedata) -{ - int i; - Step *s; - CubeData *data; - - s = (Step *)param; - data = (CubeData *)cubedata; - - for (i = 0; i < s->n_coord; i++) - if (data->val[i] != 0) - return false; - - return true; -} - -static Alg * -validate_solution(void *param, Alg *alg) -{ - return ((Step *)param)->is_valid(alg); -} - -static void * -alloc_cubedata(void *param) -{ - Step *s; - CubeData *data; - - s = (Step *)param; - - data = malloc(sizeof(CubeData)); - /* We do not need to allocate a cube */ - data->val = malloc(s->n_coord * sizeof(uint64_t)); - data->t = malloc(s->n_coord * sizeof(Trans)); - - return data; -} - -static void -copy_cubedata(void *param, void *src, void *dst) -{ - int i; - Step *s; - CubeData *newdata, *olddata; - - s = (Step *)param; - olddata = (CubeData *)src; - newdata = (CubeData *)dst; - -/* TODO: do not copy if not nissable */ - newdata->cube = malloc(sizeof(Cube)); - copy_cube(olddata->cube, newdata->cube); - for (i = 0; i < s->n_coord; i++) { - newdata->val[i] = olddata->val[i]; - newdata->t[i] = olddata->t[i]; - } -} - -static void -free_cubedata(void *param, void *cubedata) -{ - CubeData *data; - - data = (CubeData *)cubedata; - - free(data->t); - free(data->val); - free(data->cube); - free(data); -} - -static void -invert_cubedata(void *param, void *cubedata) -{ - Step *s; - CubeData *data; - - s = (Step *)param; - data = (CubeData *)cubedata; - - invert_cube(data->cube); - init_indexes(s, data); -} - -static bool -niss_makes_sense(void *param, void *cubedata, Alg *alg) -{ - Step *s; - CubeData *data; - - s = (Step *)param; - data = (CubeData *)cubedata; - - if (s->final) - return false; - - if (alg->len_normal == 0) - return true; - - Move m = inverse_move(alg->move_normal[alg->len_normal-1]); - for (int i = 0; i < s->n_coord; i++) { - Move mm = transform_move(data->t[i], m); - uint64_t u = move_coord(s->coord[i], mm, 0, NULL); - if (ptableval(s->pd[i], u) > 0) - return true; - } - - return false; -} - -static Solver * -new_stepsolver_nocheckstop(Step *step) -{ - Solver *solver; - - solver = malloc(sizeof(Solver)); - - solver->moveset = step->moveset; - solver->param = step; - - solver->apply_move = apply_move_cubedata; - solver->prepare_cube = prepare_cube; - solver->is_solved = is_solved_step; - solver->validate_solution = validate_solution; - solver->alloc_cubedata = alloc_cubedata; - solver->copy_cubedata = copy_cubedata; - solver->free_cubedata = free_cubedata; - solver->invert_cube = invert_cubedata; - solver->niss_makes_sense = niss_makes_sense; - - return solver; -} - -Solver * -new_stepsolver_eager(Step *step) -{ - Solver *solver; - - solver = new_stepsolver_nocheckstop(step); - solver->move_check_stop = move_check_stop_eager; - - return solver; -} - -Solver * -new_stepsolver_lazy(Step *step) -{ - Solver *solver; - - solver = new_stepsolver_nocheckstop(step); - solver->move_check_stop = move_check_stop_lazy; - - return solver; -} - -void -free_stepsolver(Solver *solver) -{ - free(solver); -} diff --git a/src/solver_step.h b/src/solver_step.h @@ -1,12 +0,0 @@ -#ifndef SOLVER_STEP_H -#define SOLVER_STEP_H - -#include "cube.h" -#include "solve.h" -#include "steps.h" - -Solver *new_stepsolver_eager(Step *); -Solver *new_stepsolver_lazy(Step *); -void free_stepsolver(Solver *); - -#endif diff --git a/src/steps.c b/src/steps.c @@ -1,117 +1,1376 @@ -#define STEPS_C - #include "steps.h" -/* TODO: change all checkers to use coordinates! */ +#define UPDATECHECKSTOP(a, b, c) if ((a=(MAX((a),(b))))>(c)) return (a); + +/* Checkers, estimators and validators ***************************************/ + +static bool check_centers(Cube cube); +static bool check_coud_HTM(Cube cube); +static bool check_coud_URF(Cube cube); +static bool check_corners_HTM(Cube cube); +static bool check_corners_URF(Cube cube); +static bool check_cornershtr(Cube cube); +static bool check_eofb(Cube cube); +static bool check_drud(Cube cube); +static bool check_htr(Cube cube); + +static int estimate_eofb_HTM(DfsArg *arg); +static int estimate_coud_HTM(DfsArg *arg); +static int estimate_coud_URF(DfsArg *arg); +static int estimate_corners_HTM(DfsArg *arg); +static int estimate_cornershtr_HTM(DfsArg *arg); +static int estimate_corners_URF(DfsArg *arg); +static int estimate_cornershtr_URF(DfsArg *arg); +static int estimate_drud_HTM(DfsArg *arg); +static int estimate_drud_eofb(DfsArg *arg); +static int estimate_dr_eofb(DfsArg *arg); +static int estimate_drudfin_drud(DfsArg *arg); +static int estimate_htr_drud(DfsArg *arg); +static int estimate_htrfin_htr(DfsArg *arg); +static int estimate_nxopt31_HTM(DfsArg *arg); +static int estimate_light_HTM(DfsArg *arg); + +static int estimate_nxoptlike(DfsArg *arg, PruneData *pd); + +static bool always_valid(Alg *alg); +static bool validate_singlecw_ending(Alg *alg); + +/* Pre-transformation detectors **********************************************/ + +static int detect_pretrans_eofb(Cube cube, Trans *ret); +static int detect_pretrans_drud(Cube cube, Trans *ret); +static int detect_pretrans_void_3axis(Cube cube, Trans *ret); + +/* Messages for when cube is not ready ***************************************/ + +static char check_centers_msg[100] = "cube must be oriented (centers solved)"; +static char check_eo_msg[100] = "EO must be solved on given axis"; +static char check_dr_msg[100] = "DR must be solved on given axis"; +static char check_htr_msg[100] = "HTR must be solved"; +static char check_drany_msg[100] = "DR must be solved on at least one axis"; + +/* Steps *********************************************************************/ + +/* Optimal solvers *******************/ + +Step +optimal_HTM = { + .shortname = "optimal", + .name = "Optimal solve (in HTM)", + + .final = true, + .is_done = is_solved, + .estimate = estimate_nxopt31_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = always_valid, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, + .ntables = 2, +}; + +Step +optimal_light_HTM = { + .shortname = "light", + .name = "Optimal solve (in HTM), small table (500Mb RAM total)", + + .final = true, + .is_done = is_solved, + .estimate = estimate_light_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = always_valid, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_drud_sym16_HTM, &pd_corners_HTM}, + .ntables = 2, +}; + +/* Optimal after EO ******************/ + +Step +eofin_eo = { + .shortname = "eofin", + .name = "Optimal solve after EO without breaking EO (detected)", + + .final = true, + .is_done = is_solved, + .estimate = estimate_nxopt31_HTM, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = always_valid, + .moveset = &moveset_eofb, + + .detect = detect_pretrans_eofb, + + .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, + .ntables = 2, +}; + +Step +eofbfin_eofb = { + .shortname = "eofbfin", + .name = "Optimal after EO on F/B without breaking EO", + + .final = true, + .is_done = is_solved, + .estimate = estimate_nxopt31_HTM, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = always_valid, + .moveset = &moveset_eofb, + + .pre_trans = uf, + + .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, + .ntables = 2, +}; + +Step +eorlfin_eorl = { + .shortname = "eorlfin", + .name = "Optimal after EO on R/L without breaking EO", + + .final = true, + .is_done = is_solved, + .estimate = estimate_nxopt31_HTM, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = always_valid, + .moveset = &moveset_eofb, + + .pre_trans = ur, + + .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, + .ntables = 2, +}; + +Step +eoudfin_eoud = { + .shortname = "eoudfin", + .name = "Optimal after EO on U/D without breaking EO", + + .final = true, + .is_done = is_solved, + .estimate = estimate_nxopt31_HTM, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = always_valid, + .moveset = &moveset_eofb, + + .pre_trans = fd, + + .tables = {&pd_nxopt31_HTM, &pd_corners_HTM}, + .ntables = 2, +}; + +/* EO steps **************************/ +Step +eoany_HTM = { + .shortname = "eo", + .name = "EO on any axis", + + .final = false, + .is_done = check_eofb, + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .detect = detect_pretrans_void_3axis, + + .tables = {&pd_eofb_HTM}, + .ntables = 1, +}; + +Step +eofb_HTM = { + .shortname = "eofb", + .name = "EO on F/B", + + .final = false, + .is_done = check_eofb, + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_eofb_HTM}, + .ntables = 1, +}; + +Step +eorl_HTM = { + .shortname = "eorl", + .name = "EO on R/L", + + .final = false, + .is_done = check_eofb, + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = ur, + + .tables = {&pd_eofb_HTM}, + .ntables = 1, +}; + +Step +eoud_HTM = { + .shortname = "eoud", + .name = "EO on U/D", + + .final = false, + .is_done = check_eofb, + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = fd, + + .tables = {&pd_eofb_HTM}, + .ntables = 1, +}; + +/* CO steps **************************/ +Step +coany_HTM = { + .shortname = "co", + .name = "CO on any axis", + + .final = false, + .is_done = check_coud_HTM, + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .detect = detect_pretrans_void_3axis, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +coud_HTM = { + .shortname = "coud", + .name = "CO on U/D", + + .final = false, + .is_done = check_coud_HTM, + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +corl_HTM = { + .shortname = "corl", + .name = "CO on R/L", + + .final = false, + .is_done = check_coud_HTM, + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = rf, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +cofb_HTM = { + .shortname = "cofb", + .name = "CO on F/B", + + .final = false, + .is_done = check_coud_HTM, + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = fd, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +coany_URF = { + .shortname = "co-URF", + .name = "CO any axis (URF moveset)", + + .final = false, + .is_done = check_coud_URF, + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_URF, + + .detect = detect_pretrans_void_3axis, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +coud_URF = { + .shortname = "coud-URF", + .name = "CO on U/D (URF moveset)", + + .final = false, + .is_done = check_coud_URF, + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_URF, + + .pre_trans = uf, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +corl_URF = { + .shortname = "corl-URF", + .name = "CO on R/L (URF moveset)", + + .final = false, + .is_done = check_coud_URF, + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_URF, + + .pre_trans = rf, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +Step +cofb_URF = { + .shortname = "cofb-URF", + .name = "CO on F/B (URF moveset)", + + .final = false, + .is_done = check_coud_URF, + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_URF, + + .pre_trans = fd, + + .tables = {&pd_coud_HTM}, + .ntables = 1, +}; + +/* Misc corner steps *****************/ +Step +cornershtr_HTM = { + .shortname = "chtr", + .name = "Solve corners to HTR state", + + .final = false, + .is_done = check_cornershtr, + .estimate = estimate_cornershtr_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_cornershtr_HTM}, + .ntables = 1, +}; + +Step +cornershtr_URF = { + .shortname = "chtr-URF", + .name = "Solve corners to HTR state (URF moveset)", + + .final = false, + .is_done = check_cornershtr, + .estimate = estimate_cornershtr_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_URF, + + .pre_trans = uf, + + .tables = {&pd_cornershtr_HTM}, + .ntables = 1, +}; + +Step +corners_HTM = { + .shortname = "corners", + .name = "Solve corners", + + .final = true, + .is_done = check_corners_HTM, + .estimate = estimate_corners_HTM, + .ready = NULL, + .is_valid = always_valid, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_corners_HTM}, + .ntables = 1, +}; + +Step +corners_URF = { + .shortname = "corners-URF", + .name = "Solve corners (URF moveset)", + + .final = true, /* TODO: check if this works with reorient */ + .is_done = check_corners_URF, + .estimate = estimate_corners_URF, + .ready = NULL, + .is_valid = always_valid, + .moveset = &moveset_URF, + + .pre_trans = uf, + + .tables = {&pd_corners_HTM}, + .ntables = 1, +}; + +/* DR steps **************************/ +Step +drany_HTM = { + .shortname = "dr", + .name = "DR on any axis", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .detect = detect_pretrans_void_3axis, + + .tables = {&pd_drud_sym16_HTM}, + .ntables = 1, +}; + +Step +drud_HTM = { + .shortname = "drud", + .name = "DR on U/D", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = uf, + + .tables = {&pd_drud_sym16_HTM}, + .ntables = 1, +}; + +Step +drrl_HTM = { + .shortname = "drrl", + .name = "DR on R/L", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = rf, + + .tables = {&pd_drud_sym16_HTM}, + .ntables = 1, +}; + +Step +drfb_HTM = { + .shortname = "drfb", + .name = "DR on F/B", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_HTM, + + .pre_trans = fd, + + .tables = {&pd_drud_sym16_HTM}, + .ntables = 1, +}; + +/* DR from EO */ +Step +dr_eo = { + .shortname = "dr-eo", + .name = "DR without breaking EO (automatically detected)", + + .final = false, + .is_done = check_drud, + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .detect = detect_pretrans_eofb, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +dr_eofb = { + .shortname = "dr-eofb", + .name = "DR on U/D or R/L without breaking EO on F/B", + + .final = false, + .is_done = check_drud, + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = uf, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +dr_eorl = { + .shortname = "dr-eorl", + .name = "DR on U/D or F/B without breaking EO on R/L", + + .final = false, + .is_done = check_drud, + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = ur, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +dr_eoud = { + .shortname = "dr-eoud", + .name = "DR on R/L or F/B without breaking EO on U/D", + + .final = false, + .is_done = check_drud, + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = fd, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +drud_eofb = { + .shortname = "drud-eofb", + .name = "DR on U/D without breaking EO on F/B", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = uf, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +drrl_eofb = { + .shortname = "drrl-eofb", + .name = "DR on R/L without breaking EO on F/B", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = rf, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +drud_eorl = { + .shortname = "drud-eorl", + .name = "DR on U/D without breaking EO on R/L", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = ur, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +drfb_eorl = { + .shortname = "drfb-eorl", + .name = "DR on F/B without breaking EO on R/L", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = fr, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +drfb_eoud = { + .shortname = "drfb-eoud", + .name = "DR on F/B without breaking EO on U/D", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = fd, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +Step +drrl_eoud = { + .shortname = "drrl-eoud", + .name = "DR on R/L without breaking EO on U/D", + + .final = false, + .is_done = check_drud, + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_eofb, + + .pre_trans = rd, + + .tables = {&pd_drud_eofb}, + .ntables = 1, +}; + +/* DR finish steps */ +Step +dranyfin_DR = { + .shortname = "drfin", + .name = "DR finish on any axis without breaking DR", + + .final = true, + .is_done = is_solved, + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = always_valid, + .moveset = &moveset_drud, + + .detect = detect_pretrans_drud, + + .tables = {&pd_drudfin_noE_sym16_drud}, + .ntables = 1, +}; + +Step +drudfin_drud = { + .shortname = "drudfin", + .name = "DR finish on U/D without breaking DR", + + .final = true, + .is_done = is_solved, + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = &moveset_drud, + + .pre_trans = uf, + + .tables = {&pd_drudfin_noE_sym16_drud}, + .ntables = 1, +}; + +Step +drrlfin_drrl = { + .shortname = "drrlfin", + .name = "DR finish on R/L without breaking DR", + + .final = true, + .is_done = is_solved, + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = &moveset_drud, + + .pre_trans = rf, + + .tables = {&pd_drudfin_noE_sym16_drud}, + .ntables = 1, +}; + +Step +drfbfin_drfb = { + .shortname = "drfbfin", + .name = "DR finish on F/B without breaking DR", + + .final = true, + .is_done = is_solved, + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = &moveset_drud, + + .pre_trans = fd, + + .tables = {&pd_drudfin_noE_sym16_drud}, + .ntables = 1, +}; + +/* HTR from DR */ +Step +htr_any = { + .shortname = "htr", + .name = "HTR from DR", + + .final = false, + .is_done = check_htr, + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_drud, + + .detect = detect_pretrans_drud, + + .tables = {&pd_htr_drud}, + .ntables = 1, +}; + +Step +htr_drud = { + .shortname = "htr-drud", + .name = "HTR from DR on U/D", + + .final = false, + .is_done = check_htr, + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_drud, -bool -check_centers(Cube *cube) + .pre_trans = uf, + + .tables = {&pd_htr_drud}, + .ntables = 1, +}; + +Step +htr_drrl = { + .shortname = "htr-drrl", + .name = "HTR from DR on R/L", + + .final = false, + .is_done = check_htr, + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_drud, + + .pre_trans = rf, + + .tables = {&pd_htr_drud}, + .ntables = 1, +}; + +Step +htr_drfb = { + .shortname = "htr-drfb", + .name = "HTR from DR on F/B", + + .final = false, + .is_done = check_htr, + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = &moveset_drud, + + .pre_trans = fd, + + .tables = {&pd_htr_drud}, + .ntables = 1, +}; + +/* HTR finish */ +Step +htrfin_htr = { + .shortname = "htrfin", + .name = "HTR finish without breaking HTR", + + .final = true, + .is_done = is_solved, + .estimate = estimate_htrfin_htr, + .ready = check_htr, + .ready_msg = check_htr_msg, + .is_valid = always_valid, + .moveset = &moveset_htr, + + .pre_trans = uf, + + .tables = {&pd_htrfin_htr}, + .ntables = 1, +}; + +Step *steps[] = { + &optimal_HTM, /* first is default */ + &optimal_light_HTM, + + &eofin_eo, + &eofbfin_eofb, + &eorlfin_eorl, + &eoudfin_eoud, + + &eoany_HTM, + &eofb_HTM, + &eorl_HTM, + &eoud_HTM, + + &coany_HTM, + &coud_HTM, + &corl_HTM, + &cofb_HTM, + + &coany_URF, + &coud_URF, + &corl_URF, + &cofb_URF, + + &drany_HTM, + &drud_HTM, + &drrl_HTM, + &drfb_HTM, + + &dr_eo, + &dr_eofb, + &dr_eorl, + &dr_eoud, + &drud_eofb, + &drrl_eofb, + &drud_eorl, + &drfb_eorl, + &drfb_eoud, + &drrl_eoud, + + &dranyfin_DR, + &drudfin_drud, + &drrlfin_drrl, + &drfbfin_drfb, + + &htr_any, + &htr_drud, + &htr_drrl, + &htr_drfb, + + &htrfin_htr, + + &cornershtr_HTM, + &cornershtr_URF, + &corners_HTM, + &corners_URF, + + NULL +}; + +/* Checkers, estimators and validators ***************************************/ + +static bool +check_centers(Cube cube) { - int i; + return cube.cpos == 0; +} - for (i = 0; i < 6; i++) - if (cube->xp[i] != i) - return false; +static bool +check_coud_HTM(Cube cube) +{ + return cube.coud == 0; +} - return true; +static bool +check_coud_URF(Cube cube) +{ + Cube c2, c3; + + c2 = apply_move(z, cube); + c3 = apply_move(x, cube); + + return cube.coud == 0 || c2.coud == 0 || c3.coud == 0; } -bool -check_coud_HTM(Cube *cube) +static bool +check_corners_URF(Cube cube) { - int i; + Cube c; + Trans i; - for (i = 0; i < 8; i++) - if (cube->co[i] != 0) - return false; + for (i = 0; i < NROTATIONS; i++) { + c = apply_alg(rotation_alg(i), cube); + if (c.cp && c.coud) + return true; + } - return true; + return false; } -bool -check_coud_URF(Cube *cube) +static bool +check_corners_HTM(Cube cube) { - Cube c2, c3; + return cube.cp == 0 && cube.coud == 0; +} - copy_cube(cube, &c2); - copy_cube(cube, &c3); +static bool +check_cornershtr(Cube cube) +{ + return coord_cornershtr.index(cube) == 0; +} - apply_move(z, &c2); - apply_move(x, &c3); +static bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} - return check_coud_HTM(cube) || - check_coud_HTM(&c2) || - check_coud_HTM(&c3); +static bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; } -bool -check_cp_HTM(Cube *cube) +static bool +check_htr(Cube cube) { - int i; + return check_drud(cube) && coord_htr_drud.index(cube) == 0; +} - for (i = 0; i < 8; i++) - if (cube->cp[i] != i) - return false; +static int +estimate_eofb_HTM(DfsArg *arg) +{ + return ptableval(&pd_eofb_HTM, arg->cube); +} - return true; +static int +estimate_coud_HTM(DfsArg *arg) +{ + return ptableval(&pd_coud_HTM, arg->cube); } -bool -check_corners_HTM(Cube *cube) +static int +estimate_coud_URF(DfsArg *arg) { - return check_coud_HTM(cube) && check_cp_HTM(cube); + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + Cube c; + + c = arg->cube; + + int ud = estimate_coud_HTM(arg); + arg->cube = apply_move(z, c); + int rl = estimate_coud_HTM(arg); + arg->cube = apply_move(x, c); + int fb = estimate_coud_HTM(arg); + + arg->cube = c; + + return MIN(ud, MIN(rl, fb)); } -bool -check_corners_URF(Cube *cube) +static int +estimate_corners_HTM(DfsArg *arg) { + return ptableval(&pd_corners_HTM, arg->cube); +} + +static int +estimate_cornershtr_HTM(DfsArg *arg) +{ + return ptableval(&pd_cornershtr_HTM, arg->cube); +} + +static int +estimate_cornershtr_URF(DfsArg *arg) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret; Cube c; Trans i; + c = arg->cube; + ret = 15; + for (i = 0; i < NROTATIONS; i++) { - copy_cube(cube, &c); - apply_alg(rotation_alg(i), &c); - if (check_corners_HTM(&c)) - return true; + arg->cube = apply_alg(rotation_alg(i), c); + ret = MIN(ret, estimate_cornershtr_HTM(arg)); } - return false; + arg->cube = c; + + return ret; } -bool -check_cornershtr(Cube *cube) +static int +estimate_corners_URF(DfsArg *arg) { - /* TODO (use coord) */ - return true; + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int ret; + Cube c; + Trans i; + + c = arg->cube; + ret = 15; + + for (i = 0; i < NROTATIONS; i++) { + arg->cube = apply_alg(rotation_alg(i), c); + ret = MIN(ret, estimate_corners_HTM(arg)); + } + + arg->cube = c; + + return ret; } -bool -check_eofb(Cube *cube) +static int +estimate_drud_HTM(DfsArg *arg) { - /* TODO (use coord) */ - return true; + return ptableval(&pd_drud_sym16_HTM, arg->cube); } -bool -check_drud(Cube *cube) +static int +estimate_drud_eofb(DfsArg *arg) { - /* TODO (use coord) */ - return true; + return ptableval(&pd_drud_eofb, arg->cube); } -bool -check_htr(Cube *cube) +static int +estimate_dr_eofb(DfsArg *arg) +{ + int r1, r2; + + r1 = ptableval(&pd_drud_eofb, arg->cube); + r2 = ptableval(&pd_drud_eofb, apply_trans(rf, arg->cube)); + + return MIN(r1, r2); +} + +static int +estimate_drudfin_drud(DfsArg *arg) +{ + int val = ptableval(&pd_drudfin_noE_sym16_drud, arg->cube); + + if (val != 0) + return val; + + return arg->cube.epose % 24 == 0 ? 0 : 1; +} + +static int +estimate_htr_drud(DfsArg *arg) +{ + return ptableval(&pd_htr_drud, arg->cube); +} + +static int +estimate_htrfin_htr(DfsArg *arg) +{ + return ptableval(&pd_htrfin_htr, arg->cube); +} + +static int +estimate_nxopt31_HTM(DfsArg *arg) +{ + return estimate_nxoptlike(arg, &pd_nxopt31_HTM); +} + +/* TODO: also use generic procedure for this */ +static int +estimate_light_HTM(DfsArg *arg) +{ + int target, ret; + Cube aux; + + static const uint64_t udmask = (1<<U) | (1<<U2) | (1<<U3) | + (1<<D) | (1<<D2) | (1<<D3); + static const uint64_t rlmask = (1<<R) | (1<<R2) | (1<<R3) | + (1<<L) | (1<<L2) | (1<<L3); + static const uint64_t fbmask = (1<<F) | (1<<F2) | (1<<F3) | + (1<<B) | (1<<B2) | (1<<B3); + static const uint64_t htmask = (1<<U2) | (1<<D2) | + (1<<R2) | (1<<L2) | + (1<<F2) | (1<<B2); + + ret = -1; + target = arg->d - arg->current_alg->len; + arg->inverse = (Cube){0}; + arg->badmovesinv = 0; + arg->badmoves = 0; + + /* Corners */ + arg->ed->corners = ptableval(&pd_corners_HTM, arg->cube); + UPDATECHECKSTOP(ret, arg->ed->corners, target); + + /* Normal probing */ + arg->ed->normal_ud = ptableval(&pd_drud_sym16_HTM, arg->cube); + UPDATECHECKSTOP(ret, arg->ed->normal_ud, target); + aux = apply_trans(fd, arg->cube); + arg->ed->normal_fb = ptableval(&pd_drud_sym16_HTM, aux); + UPDATECHECKSTOP(ret, arg->ed->normal_fb, target); + aux = apply_trans(rf, arg->cube); + arg->ed->normal_rl = ptableval(&pd_drud_sym16_HTM, aux); + UPDATECHECKSTOP(ret, arg->ed->normal_rl, target); + + /* If ret == 0, it's solved (corners + triple slice solved) */ + if (ret == 0) + return is_solved(arg->cube) ? 0 : 1; + + /* Michel de Bondt's trick*/ + if (arg->ed->normal_ud == arg->ed->normal_fb && + arg->ed->normal_fb == arg->ed->normal_rl) { + UPDATECHECKSTOP(ret, arg->ed->normal_ud + 1, target); + } + + /* Inverse probing */ + if (!((1<<arg->last1) & htmask)) { + aux = arg->inverse = inverse_cube(arg->cube); + if (!((1<<arg->last1) & udmask) || (arg->ed->inverse_ud==-1)) { + arg->ed->inverse_ud = + ptableval(&pd_drud_sym16_HTM, aux); + } + UPDATECHECKSTOP(ret, arg->ed->inverse_ud, target); + if (!((1<<arg->last1) & fbmask) || (arg->ed->inverse_fb==-1)) { + aux = apply_trans(fd, arg->inverse); + arg->ed->inverse_fb = + ptableval(&pd_drud_sym16_HTM, aux); + } + UPDATECHECKSTOP(ret, arg->ed->inverse_fb, target); + if (!((1<<arg->last1) & rlmask) || (arg->ed->inverse_rl==-1)) { + aux = apply_trans(rf, arg->inverse); + arg->ed->inverse_rl = + ptableval(&pd_drud_sym16_HTM, aux); + } + UPDATECHECKSTOP(ret, arg->ed->inverse_rl, target); + } else { + UPDATECHECKSTOP(ret, arg->ed->inverse_ud, target); + UPDATECHECKSTOP(ret, arg->ed->inverse_fb, target); + UPDATECHECKSTOP(ret, arg->ed->inverse_rl, target); + } + + /* Michel de Bondt's trick*/ + if (arg->ed->inverse_ud == arg->ed->inverse_fb && + arg->ed->inverse_fb == arg->ed->inverse_rl) { + UPDATECHECKSTOP(ret, arg->ed->inverse_ud + 1, target); + } + + /* nxopt trick + half turn trick */ + if (arg->ed->normal_ud == target) + arg->badmovesinv |= udmask | htmask; + if (arg->ed->normal_fb == target) + arg->badmovesinv |= fbmask | htmask; + if (arg->ed->normal_rl == target) + arg->badmovesinv |= rlmask | htmask; + + if (arg->ed->inverse_ud == target) + arg->badmoves |= udmask | htmask; + if (arg->ed->inverse_fb == target) + arg->badmoves |= fbmask | htmask; + if (arg->ed->inverse_rl == target) + arg->badmoves |= rlmask | htmask; + + return arg->ed->oldret = ret; +} + +static int +estimate_nxoptlike(DfsArg *arg, PruneData *pd) +{ + int target, ret; + Cube aux; + + static const uint64_t udmask = (1<<U) | (1<<U2) | (1<<U3) | + (1<<D) | (1<<D2) | (1<<D3); + static const uint64_t rlmask = (1<<R) | (1<<R2) | (1<<R3) | + (1<<L) | (1<<L2) | (1<<L3); + static const uint64_t fbmask = (1<<F) | (1<<F2) | (1<<F3) | + (1<<B) | (1<<B2) | (1<<B3); + + ret = -1; + target = arg->d - arg->current_alg->len; + arg->inverse = (Cube){0}; + arg->badmovesinv = 0; + arg->badmoves = 0; + + /* Corners */ + arg->ed->corners = ptableval(&pd_corners_HTM, arg->cube); + UPDATECHECKSTOP(ret, arg->ed->corners, target); + + /* Normal probing */ + arg->ed->normal_ud = ptableval(pd, arg->cube); + UPDATECHECKSTOP(ret, arg->ed->normal_ud, target); + aux = apply_trans(fd, arg->cube); + arg->ed->normal_fb = ptableval(pd, aux); + UPDATECHECKSTOP(ret, arg->ed->normal_fb, target); + aux = apply_trans(rf, arg->cube); + arg->ed->normal_rl = ptableval(pd, aux); + UPDATECHECKSTOP(ret, arg->ed->normal_rl, target); + + if (ret == 0) + return arg->step->is_done(arg->cube) ? 0 : 1; + + /* Michel de Bondt's trick*/ + if (arg->ed->normal_ud == arg->ed->normal_fb && + arg->ed->normal_fb == arg->ed->normal_rl) { + UPDATECHECKSTOP(ret, arg->ed->normal_ud + 1, target); + } + + /* Inverse probing */ + aux = arg->inverse = inverse_cube(arg->cube); + if (!((1<<arg->last1) & udmask) || (arg->ed->inverse_ud == -1)) { + arg->ed->inverse_ud = ptableval(pd, aux); + } + UPDATECHECKSTOP(ret, arg->ed->inverse_ud, target); + if (!((1<<arg->last1) & fbmask) || (arg->ed->inverse_fb == -1)) { + aux = apply_trans(fd, arg->inverse); + arg->ed->inverse_fb = ptableval(pd, aux); + } + UPDATECHECKSTOP(ret, arg->ed->inverse_fb, target); + if (!((1<<arg->last1) & rlmask) || (arg->ed->inverse_rl == -1)) { + aux = apply_trans(rf, arg->inverse); + arg->ed->inverse_rl = ptableval(pd, aux); + } + UPDATECHECKSTOP(ret, arg->ed->inverse_rl, target); + + /* Michel de Bondt's trick*/ + if (arg->ed->inverse_ud == arg->ed->inverse_fb && + arg->ed->inverse_fb == arg->ed->inverse_rl) { + UPDATECHECKSTOP(ret, arg->ed->inverse_ud + 1, target); + } + + /* nxopt trick */ + if (arg->ed->normal_ud == target) + arg->badmovesinv |= udmask; + if (arg->ed->normal_fb == target) + arg->badmovesinv |= fbmask; + if (arg->ed->normal_rl == target) + arg->badmovesinv |= rlmask; + + if (arg->ed->inverse_ud == target) + arg->badmoves |= udmask; + if (arg->ed->inverse_fb == target) + arg->badmoves |= fbmask; + if (arg->ed->inverse_rl == target) + arg->badmoves |= rlmask; + + return arg->ed->oldret = ret; +} + + +static bool +always_valid(Alg *alg) { - /* TODO (check_drud(cube) and coord_htr_drud == 0) */ return true; } -Alg * +static bool validate_singlecw_ending(Alg *alg) { int i; bool nor, inv; - Alg *ret; Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE; for (i = 0; i < alg->len; i++) { @@ -127,51 +1386,96 @@ validate_singlecw_ending(Alg *alg) nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2)); inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i)); - if (nor && inv) { - ret = new_alg(""); - copy_alg(alg, ret); - } else { - ret = NULL; - } + return nor && inv; +} - return ret; +/* Pre-transformation detectors **********************************************/ + +static int +detect_pretrans_eofb(Cube cube, Trans *ret) +{ + int i, n; + static Trans tt[3] = {uf, ur, fd}; + + for (i = 0, n = 0; i < 3; i++) + if (check_eofb(apply_trans(tt[i], cube))) + ret[n++] = tt[i]; + + return n; +} + +static int +detect_pretrans_drud(Cube cube, Trans *ret) +{ + int i, n; + static Trans tt[3] = {uf, fr, rd}; + + for (i = 0, n = 0; i < 3; i++) + if (check_drud(apply_trans(tt[i], cube))) + ret[n++] = tt[i]; + + return n; +} + +static int +detect_pretrans_void_3axis(Cube cube, Trans *ret) +{ + ret[0] = uf; + ret[1] = fr; + ret[2] = rd; + + return 3; } /* Public functions **********************************************************/ -/* void -compute_ind(Step *s, Cube *cube, Movable *ind) +copy_estimatedata(EstimateData *src, EstimateData *dst) { - int i; - Cube mvd; - Trans t, tt; + dst->corners = src->corners; + dst->normal_ud = src->normal_ud; + dst->normal_fb = src->normal_fb; + dst->normal_rl = src->normal_rl; + dst->inverse_ud = src->inverse_ud; + dst->inverse_fb = src->inverse_fb; + dst->inverse_rl = src->inverse_rl; + dst->oldret = src->oldret; +} - for (i = 0; i < s->n_coord; i++) { - t = s->coord_trans[i]; - copy_cube(cube, &mvd); - apply_trans(t, &mvd); +void +invert_estimatedata(EstimateData *ed) +{ + swap(&(ed->normal_ud), &(ed->inverse_ud)); + swap(&(ed->normal_fb), &(ed->inverse_fb)); + swap(&(ed->normal_rl), &(ed->inverse_rl)); +} - ind[i].val = index_coord(s->coord[i], &mvd, &tt); - ind[i].t = transform_trans(tt, t); - } +void +reset_estimatedata(EstimateData *ed) +{ + ed->corners = -1; + ed->normal_ud = -1; + ed->normal_fb = -1; + ed->normal_rl = -1; + ed->inverse_ud = -1; + ed->inverse_fb = -1; + ed->inverse_rl = -1; + ed->oldret = -1; } -*/ void -prepare_cs(ChoiceStep *cs, SolveOptions *opts) -{ - int i, j; - Step *s; - - for (i = 0; cs->step[i] != NULL; i++) { - s = cs->step[i]; - for (j = 0; j < s->n_coord; j++) { - s->pd[j] = malloc(sizeof(PruneData)); - s->pd[j]->moveset = s->moveset; - s->pd[j]->coord = s->coord[j]; - s->pd[j]->compact = s->pd_compact[j]; - s->pd[j] = genptable(s->pd[j], opts->nthreads); - } +prepare_step(Step *step, SolveOptions *opts) +{ + int i; + + if (step->final && opts->can_niss) { + opts->can_niss = false; + fprintf(stderr, "Step is final, NISS not used (-n ignored)\n"); + } + + for (i = 0; i < step->ntables; i++) { + genptable(step->tables[i], opts->nthreads); + if (step->tables[i]->compact) + genptable(step->tables[i]->fallback, opts->nthreads); } } diff --git a/src/steps.h b/src/steps.h @@ -2,243 +2,16 @@ #define STEPS_H #include "pruning.h" -#include "movesets.h" -bool check_centers(Cube *cube); -bool check_coud_HTM(Cube *cube); -bool check_coud_URF(Cube *cube); -bool check_cp_HTM(Cube *cube); -bool check_corners_HTM(Cube *cube); -bool check_corners_URF(Cube *cube); -bool check_cornershtr(Cube *cube); -bool check_eofb(Cube *cube); -bool check_drud(Cube *cube); -bool check_htr(Cube *cube); -/*void compute_ind(Step *a, Cube *cube, Movable *ind);*/ -void prepare_cs(ChoiceStep *cs, SolveOptions *opts); -bool always_valid(Alg *alg); -Alg * validate_singlecw_ending(Alg *alg); +extern Step * steps[]; -#ifndef STEPS_C +/* Two steps used directly by two-phase solver */ +extern Step drany_HTM; +extern Step dranyfin_DR; -extern char check_centers_msg[100]; -extern char check_eo_msg[100]; -extern char check_dr_msg[100]; -extern char check_htr_msg[100]; -extern char check_drany_msg[100]; - -extern Step step_eofb_HTM; -extern Step step_drud_HTM; -extern Step step_drfin_drud; - -extern ChoiceStep optimal_HTM; -extern ChoiceStep eoany_HTM; -extern ChoiceStep eofb_HTM; -extern ChoiceStep eorl_HTM; -extern ChoiceStep eoud_HTM; -extern ChoiceStep drany_HTM; -extern ChoiceStep drud_HTM; -extern ChoiceStep drrl_HTM; -extern ChoiceStep drfb_HTM; -extern ChoiceStep dranyfin_DR; -extern ChoiceStep drudfin_drud; -extern ChoiceStep drrlfin_drrl; -extern ChoiceStep drfbfin_drfb; - -extern ChoiceStep *csteps[]; - -#else - -char check_centers_msg[100] = "cube must be oriented (centers solved)"; -char check_eo_msg[100] = "EO must be solved on given axis"; -char check_dr_msg[100] = "DR must be solved on given axis"; -char check_htr_msg[100] = "HTR must be solved"; -char check_drany_msg[100] = "DR must be solved on at least one axis"; - -/* Optimal after EO ******************/ -/* TODO: eofin_eo (generic), eofbfin_eofb, eorlfin_eorl, eoudfin_eoud */ - -/* EO steps **************************/ -/* TODO: eoany_HTM (generic), eofb_HTM, eorl_HTM, eoud_HTM */ - -Step -step_eofb_HTM = { - .ready = check_centers, - .final = false, - .moveset = &moveset_HTM, - .n_coord = 1, - .coord = {&coord_eofb}, - .coord_trans = {uf}, - .is_valid = validate_singlecw_ending, -}; -ChoiceStep -eoany_HTM = { - .shortname = "eo", - .name = "EO on any axis", - .step = {&step_eofb_HTM, &step_eofb_HTM, &step_eofb_HTM, NULL}, - .t = {uf, ur, fd}, - .ready_msg = check_centers_msg, -}; -ChoiceStep -eofb_HTM = { - .shortname = "eofb", - .name = "EO on F/B", - .step = {&step_eofb_HTM, NULL}, - .t = {uf}, - .ready_msg = check_centers_msg, -}; -ChoiceStep -eorl_HTM = { - .shortname = "eorl", - .name = "EO on R/L", - .step = {&step_eofb_HTM, NULL}, - .t = {ur}, - .ready_msg = check_centers_msg, -}; -ChoiceStep -eoud_HTM = { - .shortname = "eoud", - .name = "EO on U/D", - .step = {&step_eofb_HTM, NULL}, - .t = {fd}, - .ready_msg = check_centers_msg, -}; - -/* CO steps **************************/ -/* TODO: coany_HTM (generic), cofb_HTM, corl_HTM, coud_HTM */ -/* TODO: coany_URF (generic), cofb_URF, corl_URF, coud_URF */ - -/* Misc corner steps *****************/ -/* TODO: cornershtr_HTM, cornershtr_URF, corners_HTM, corners_URF */ -/* TODO (new): corners_drud */ - -/* DR steps **************************/ -/* TODO: dr_eo (generic) */ -/* TODO: dr_eofb (generic), dr_eorl (generic), dr_eoud (generic) */ -/* TODO: drud_eofb, drrl_eofb, drud_eorl, drfb_eorl, drrl_eoud, drfb_eoud */ - -Step -step_drud_HTM = { - .ready = check_centers, - .final = false, - .moveset = &moveset_HTM, - .n_coord = 1, - .coord = {&coord_drud_sym16}, - .coord_trans = {uf}, - .is_valid = validate_singlecw_ending, -}; -ChoiceStep -drany_HTM = { - .shortname = "dr", - .name = "DR on any axis", - .step = {&step_drud_HTM, &step_drud_HTM, &step_drud_HTM, NULL}, - .t = {uf, rf, fd}, - .ready_msg = check_centers_msg, -}; -ChoiceStep -drud_HTM = { - .shortname = "drud", - .name = "DR on U/D", - .step = {&step_drud_HTM, NULL}, - .t = {uf}, - .ready_msg = check_centers_msg, -}; -ChoiceStep -drrl_HTM = { - .shortname = "drrl", - .name = "DR on R/L", - .step = {&step_drud_HTM, NULL}, - .t = {rf}, - .ready_msg = check_centers_msg, -}; -ChoiceStep -drfb_HTM = { - .shortname = "drfb", - .name = "DR on F/B", - .step = {&step_drud_HTM, NULL}, - .t = {fd}, - .ready_msg = check_centers_msg, -}; - -/* DR finish steps */ -Step -step_drfin_drud = { - .ready = check_drud, - .final = true, - .moveset = &moveset_drud, - .n_coord = 1, - .coord = {&coord_drudfin_noE_sym16}, /* TODO: maybe no noE */ - .coord_trans = {uf}, - .is_valid = NULL, -}; -ChoiceStep -dranyfin_DR = { - .shortname = "drfin", - .name = "DR finish on any axis without breaking DR", - .step = {&step_drfin_drud, &step_drfin_drud, - &step_drfin_drud, NULL}, - .t = {uf, rf, fd}, - .ready_msg = check_dr_msg, -}; -ChoiceStep -drudfin_drud = { - .shortname = "drudfin", - .name = "DR finis on U/D without breaking DR", - .step = {&step_drfin_drud, NULL}, - .t = {uf}, - .ready_msg = check_dr_msg, -}; -ChoiceStep -drrlfin_drrl = { - .shortname = "drrlfin", - .name = "DR finish on R/L without breaking DR", - .step = {&step_drfin_drud, NULL}, - .t = {rf}, - .ready_msg = check_dr_msg, -}; -ChoiceStep -drfbfin_drfb = { - .shortname = "drfbfin", - .name = "DR finish on F/B without breaking DR", - .step = {&step_drfin_drud, NULL}, - .t = {fd}, - .ready_msg = check_dr_msg, -}; - -/* HTR from DR */ -/* TODO: htr_any (generic), htr_drud, htr_drrl, htr_drfb */ - -/* HTR finish */ -/* TODO: htrfin_htr */ - -ChoiceStep *csteps[] = { -/* TODO: re-implement optimal - &optimal_HTM, -*/ - - &eoany_HTM, &eofb_HTM, &eorl_HTM, &eoud_HTM, - &drany_HTM, &drud_HTM, &drrl_HTM, &drfb_HTM, - &dranyfin_DR, &drudfin_drud, &drrlfin_drrl, &drfbfin_drfb, - -NULL -/* TODO: - &optimal_light_HTM, - - &eofin_eo, &eofbfin_eofb, &eorlfin_eorl, &eoudfin_eoud, - &coany_HTM, &coud_HTM, &corl_HTM, &cofb_HTM, - &coany_URF, &coud_URF, &corl_URF, &cofb_URF, - &dr_eo, &dr_eofb, &dr_eorl, &dr_eoud, - &drud_eofb, &drrl_eofb, - &drud_eorl, &drfb_eorl, - &drfb_eoud, &drrl_eoud, - &htr_any, &htr_drud, &htr_drrl, &htr_drfb, - &htrfin_htr, - &cornershtr_HTM, &cornershtr_URF, &corners_HTM, &corners_URF, - NULL -*/ - -}; - -#endif +void copy_estimatedata(EstimateData *s, EstimateData *d); +void invert_estimatedata(EstimateData *ed); +void reset_estimatedata(EstimateData *ed); +void prepare_step(Step *step, SolveOptions *opts); #endif diff --git a/src/symcoord.c b/src/symcoord.c @@ -0,0 +1,687 @@ +#include "symcoord.h" + +/* These constants have been computed generating the respective SymData */ +#define CLASSES_CP_16 2768 +#define CLASSES_EOFBEPOS_16 64430 + +static uint64_t index_cp_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_drudfin_noE_sym16(Cube cube); +static uint64_t index_nxopt31(Cube cube); + +static uint64_t move_cp_sym16(Move m, uint64_t ind); +static uint64_t move_eofbepos_sym16(Move m, uint64_t ind); +static uint64_t move_drud_sym16(Move m, uint64_t ind); +static uint64_t move_drudfin_noE_sym16(Move m, uint64_t ind); +static uint64_t move_nxopt31(Move m, uint64_t ind); + +static int tfind_from_mask(uint64_t mask, Trans *ret); +static int tfind_drud_sym16(uint64_t ind, Trans *ret); +static int tfind_drudfin_noE_sym16(uint64_t ind, Trans *ret); +static int tfind_nxopt31(uint64_t ind, Trans *ret); + +static uint64_t transform_cp(Trans t, uint64_t ind); +static uint64_t transform_eofbepos(Trans t, uint64_t ind); +static uint64_t transform_drud_sym16(Trans t, uint64_t ind); +static uint64_t transform_drudfin_noE_sym16(Trans t, uint64_t ind); +static uint64_t transform_nxopt31(Trans t, uint64_t ind); + +static void gensym(SymData *sd); +static void init_symc_moves(); +static void init_symc_trans(); +static bool read_symdata_file(SymData *sd); +static bool read_symc_moves_file(); +static bool read_symc_trans_file(); +static bool write_symdata_file(SymData *sd); +static bool write_symc_moves_file(); +static bool write_symc_trans_file(); + +/* Some tables ***************************************************************/ + +static uint64_t move_cp_16[NMOVES][CLASSES_CP_16]; +static uint64_t move_eofbepos_16[NMOVES][CLASSES_EOFBEPOS_16]; + +static int trans_eofbepos[NTRANS][POW2TO11*BINOM12ON4]; +static int trans_epud[NTRANS][FACTORIAL8]; +static int trans_cpud_separate[NTRANS][BINOM8ON4]; + +static Trans ttrep_move_cp_16[NMOVES][CLASSES_CP_16]; +static Trans ttrep_move_eofbepos_16[NMOVES][CLASSES_EOFBEPOS_16]; + + +/* Transformation groups and symmetry data ***********************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +static SymData +sd_cp_16 = { + .filename = "sd_cp_16_new", + .coord = &coord_cp, + .sym_coord = &coord_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix, + .transform = transform_cp, +}; + +static SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16_new", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix, + .transform = transform_eofbepos, +}; + +SymData * all_sd[] = { + &sd_cp_16, + &sd_eofbepos_16, + NULL +}; + + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .move = move_eofbepos_sym16, +}; + +Coordinate +coord_cp_sym16 = { + .index = index_cp_sym16, + .move = move_cp_sym16, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .move = move_drud_sym16, + .max = POW3TO7 * CLASSES_EOFBEPOS_16, + .base = &coord_eofbepos_sym16, + .transform = transform_drud_sym16, + .tfind = tfind_drud_sym16, +}; + +Coordinate +coord_drudfin_noE_sym16 = { + .index = index_drudfin_noE_sym16, + .move = move_drudfin_noE_sym16, + .max = FACTORIAL8 * CLASSES_CP_16, + .base = &coord_cp_sym16, + .transform = transform_drudfin_noE_sym16, + .tfind = tfind_drudfin_noE_sym16, +}; + +Coordinate +coord_nxopt31 = { + .index = index_nxopt31, + .move = move_nxopt31, + .max = POW3TO7 * BINOM8ON4 * CLASSES_EOFBEPOS_16, + .base = &coord_eofbepos_sym16, + .transform = transform_nxopt31, + .tfind = tfind_nxopt31, +}; + +/* Functions *****************************************************************/ + +static uint64_t +index_cp_sym16(Cube cube) +{ + return sd_cp_16.class[coord_cp.index(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + + return index_eofbepos_sym16(cube) * POW3TO7 + co_ttable[t][cube.coud]; +} + +static uint64_t +index_drudfin_noE_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_cp_16.transtorep[coord_cp.index(cube)]; + c = apply_trans(t, cube); + + /* TODO: add transform to coord_epud to make this faster */ + return index_cp_sym16(c) * FACTORIAL8 + coord_epud.index(c); +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[coord_eofbepos.index(cube)]; +} + +static uint64_t +index_nxopt31(Cube cube) +{ + Trans t; + uint64_t a; + int coud, cp; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + coud = co_ttable[t][cube.coud]; + cp = cp_ttable[t][cube.cp]; + a = (index_eofbepos_sym16(cube)*POW3TO7) + coud; + + return a * BINOM8ON4 + coord_cpud_separate.index((Cube){.cp = cp}); +} + +static uint64_t +move_cp_sym16(Move m, uint64_t ind) +{ + return move_cp_16[m][ind]; +} + +static uint64_t +move_eofbepos_sym16(Move m, uint64_t ind) +{ + return move_eofbepos_16[m][ind]; +} + +static uint64_t +move_drud_sym16(Move m, uint64_t ind) +{ + uint64_t coud, eofbepos; + Trans ttr; + + eofbepos = move_eofbepos_16[m][ind / POW3TO7]; + ttr = ttrep_move_eofbepos_16[m][ind / POW3TO7]; + coud = coud_mtable[m][ind % POW3TO7]; + coud = co_ttable[ttr][coud]; /* Source is always coud */ + + return eofbepos * POW3TO7 + coud; +} + +static uint64_t +move_drudfin_noE_sym16(Move m, uint64_t ind) +{ + uint64_t cp, epud; + Trans ttr; + + cp = move_cp_16[m][ind / FACTORIAL8]; + ttr = ttrep_move_cp_16[m][ind / FACTORIAL8]; + epud = coord_epud.move(m, ind % FACTORIAL8); + epud = trans_epud[ttr][epud]; + + return cp * FACTORIAL8 + epud; +} + +static uint64_t +move_nxopt31(Move m, uint64_t ind) +{ + uint64_t eofbepos, cpsep, coud; + Trans ttr; + + eofbepos = ind / (POW3TO7 * BINOM8ON4); + coud = (ind / BINOM8ON4) % POW3TO7; + cpsep = ind % BINOM8ON4; + + ttr = ttrep_move_eofbepos_16[m][eofbepos]; + eofbepos = move_eofbepos_16[m][eofbepos]; + coud = coud_mtable[m][coud]; + coud = co_ttable[ttr][coud]; /* Source is always coud */ + cpsep = coord_cpud_separate.move(m, cpsep); + cpsep = trans_cpud_separate[ttr][cpsep]; + + return (eofbepos * POW3TO7 + coud) * BINOM8ON4 + cpsep; +} + +static uint64_t +transform_cp(Trans t, uint64_t ind) +{ + return cp_ttable[t][ind]; +} + +static uint64_t +transform_eofbepos(Trans t, uint64_t ind) +{ + return trans_eofbepos[t][ind]; +} + +static uint64_t +transform_drud_sym16(Trans t, uint64_t ind) +{ + uint64_t coud, eofbepos; + + eofbepos = ind / POW3TO7; /* Assum trans fixes eofbepos */ + coud = co_ttable[t][ind % POW3TO7]; /* Source is always coud */ + + return eofbepos * POW3TO7 + coud; +} + +static uint64_t +transform_drudfin_noE_sym16(Trans t, uint64_t ind) +{ + uint64_t cp, epud; + + cp = ind / FACTORIAL8; /* Assume trans fixes cp */ + epud = trans_epud[t][ind % FACTORIAL8]; + + return cp * FACTORIAL8 + epud; +} + +static uint64_t +transform_nxopt31(Trans t, uint64_t ind) +{ + uint64_t eofbepos, cpsep, coud; + + eofbepos = ind / (POW3TO7 * BINOM8ON4); + coud = (ind / BINOM8ON4) % POW3TO7; + cpsep = ind % BINOM8ON4; + + coud = co_ttable[t][coud]; /* Source is always coud */ + cpsep = trans_cpud_separate[t][cpsep]; + + return (eofbepos * POW3TO7 + coud) * BINOM8ON4 + cpsep; +} + +static int +tfind_from_mask(uint64_t mask, Trans *ret) +{ + Trans t; + int i = 0; + + for (t = uf; t < NTRANS; t++) + if (((uint64_t)1 << t) & mask) + ret[i++] = t; + + return i; +} + +static int +tfind_drud_sym16(uint64_t ind, Trans *ret) +{ + uint64_t mask = sd_eofbepos_16.selfsim[ind / POW3TO7]; + + return tfind_from_mask(mask, ret); +} + +static int +tfind_drudfin_noE_sym16(uint64_t ind, Trans *ret) +{ + uint64_t mask = sd_cp_16.selfsim[ind / FACTORIAL8]; + + return tfind_from_mask(mask, ret); +} + +static int +tfind_nxopt31(uint64_t ind, Trans *ret) +{ + uint64_t mask = sd_eofbepos_16.selfsim[ind / (POW3TO7 * BINOM8ON4)]; + + return tfind_from_mask(mask, ret); +} + +/* Other functions ***********************************************************/ + +void +free_sd(SymData *sd) +{ + if (sd->generated) { + free(sd->class); + free(sd->unsym); + free(sd->transtorep); + } + + sd->generated = false; +} + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + Trans t; + int j; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->unsym = malloc(sd->coord->max * sizeof(uint64_t)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + sd->selfsim = malloc(sd->coord->max * sizeof(uint64_t)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + sd->unsym[nreps] = i; + sd->transtorep[i] = uf; + sd->selfsim[nreps] = (uint64_t)0; + for (j = 0; j < sd->ntrans; j++) { + t = sd->trans[j]; + in = sd->transform(t, i); + sd->class[in] = nreps; + if (in == i) + sd->selfsim[nreps] |= + ((uint64_t)1 << t); + else + sd->transtorep[in] = inverse_trans(t); + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->unsym = realloc(sd->unsym, nreps * sizeof(uint64_t)); + sd->selfsim = realloc(sd->selfsim, nreps * sizeof(uint64_t)); + sd->generated = true; + + fprintf(stderr, "Found %" PRIu64 " classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static bool +read_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->unsym, sizeof(uint64_t), *sn, f) == *sn; + r = r && fread(sd->selfsim, sizeof(uint64_t), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +read_symc_moves_file() +{ + init_env(); + + Move m; + bool r = true; + FILE *f; + char fname[strlen(tabledir)+100]; + + strcpy(fname, tabledir); + strcat(fname, "/symc_moves"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(move_cp_16[m], sizeof(uint64_t), + CLASSES_CP_16, f) == CLASSES_CP_16; + r = r && fread(move_eofbepos_16[m], sizeof(uint64_t), + CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; + + r = r && fread(ttrep_move_cp_16[m], sizeof(Trans), + CLASSES_CP_16, f) == CLASSES_CP_16; + r = r && fread(ttrep_move_eofbepos_16[m], sizeof(Trans), + CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; + } + + fclose(f); + return r; +} + +static bool +read_symc_trans_file() +{ + init_env(); + + Trans t; + bool r = true; + FILE *f; + char fname[strlen(tabledir)+100]; + + strcpy(fname, tabledir); + strcat(fname, "/symc_trans"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (t = 0; t < NTRANS; t++) { + r = r && fread(trans_eofbepos[t], sizeof(int), + POW2TO11*BINOM12ON4, f) == POW2TO11*BINOM12ON4; + r = r && fread(trans_epud[t], sizeof(int), + FACTORIAL8, f) == FACTORIAL8; + r = r && fread(trans_cpud_separate[t], sizeof(int), + BINOM8ON4, f) == BINOM8ON4; + } + + fclose(f); + return r; +} + +static bool +write_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->unsym, sizeof(uint64_t), *sn, f) == *sn; + r = r && fwrite(sd->selfsim, sizeof(uint64_t), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_symc_moves_file() +{ + init_env(); + + Move m; + bool r = true; + FILE *f; + char fname[strlen(tabledir)+100]; + + strcpy(fname, tabledir); + strcat(fname, "/symc_moves"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(move_cp_16[m], sizeof(uint64_t), + CLASSES_CP_16, f) == CLASSES_CP_16; + r = r && fwrite(move_eofbepos_16[m], sizeof(uint64_t), + CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; + + r = r && fwrite(ttrep_move_cp_16[m], sizeof(Trans), + CLASSES_CP_16, f) == CLASSES_CP_16; + r = r && fwrite(ttrep_move_eofbepos_16[m], sizeof(Trans), + CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; + } + + fclose(f); + return r; +} + +static bool +write_symc_trans_file() +{ + init_env(); + + Trans t; + bool r = true; + FILE *f; + char fname[strlen(tabledir)+100]; + + strcpy(fname, tabledir); + strcat(fname, "/symc_trans"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (t = 0; t < NTRANS; t++) { + r = r && fwrite(trans_eofbepos[t], sizeof(int), + POW2TO11*BINOM12ON4, f) == POW2TO11*BINOM12ON4; + r = r && fwrite(trans_epud[t], sizeof(int), + FACTORIAL8, f) == FACTORIAL8; + r = r && fwrite(trans_cpud_separate[t], sizeof(int), + BINOM8ON4, f) == BINOM8ON4; + } + + fclose(f); + return r; +} + +static void +init_symc_moves() +{ + uint64_t i, ii, coo; + Move j; + + if (read_symc_moves_file()) + return; + + for (i = 0; i < CLASSES_CP_16; i++) { + ii = sd_cp_16.unsym[i]; + for (j = 0; j < NMOVES; j++) { + coo = sd_cp_16.coord->move(j, ii); + move_cp_16[j][i] = sd_cp_16.class[coo]; + ttrep_move_cp_16[j][i] = sd_cp_16.transtorep[coo]; + } + } + + for (i = 0; i < CLASSES_EOFBEPOS_16; i++) { + ii = sd_eofbepos_16.unsym[i]; + for (j = 0; j < NMOVES; j++) { + coo = sd_eofbepos_16.coord->move(j, ii); + move_eofbepos_16[j][i] = sd_eofbepos_16.class[coo]; + ttrep_move_eofbepos_16[j][i] = + sd_eofbepos_16.transtorep[coo]; + } + } + + if (!write_symc_moves_file()) + fprintf(stderr, "Error writing SymMoves file\n"); +} + +void +init_symc_trans() +{ + uint64_t i; + int j, cp; + int epe[4] = {FR, FL, BL, BR}; + int a[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 }; + Cube c; + CubeArray *arr, *aux; + Trans t; + + if (read_symc_trans_file()) + return; + + for (i = 0; i < POW2TO11*BINOM12ON4; i++) { + for (j = 0; j < 16; j++) { + t = trans_group_udfix[j]; + + arr = new_cubearray((Cube){0}, pf_edges); + int_to_sum_zero_array(i % POW2TO11, 2, 12, arr->eofb); + epos_to_compatible_ep((i / POW2TO11)*24, arr->ep, epe); + fix_eorleoud(arr); + c = arrays_to_cube(arr, pf_edges); + free_cubearray(arr, pf_edges); + + c = apply_trans(t, c); + trans_eofbepos[t][i] = (c.epose/24)*POW2TO11 + c.eofb; + } + } + + aux = malloc(sizeof(CubeArray)); + aux->ep = a; + for (i = 0; i < FACTORIAL8; i++) { + index_to_perm(i, 8, a); + c = arrays_to_cube(aux, pf_ep); + for (j = 0; j < 16; j++) { + t = trans_group_udfix[j]; + arr = new_cubearray(apply_trans(t, c), pf_ep); + trans_epud[t][i] = perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); + } + } + free(aux); + + for (i = 0; i < BINOM8ON4; i++) { + cp = cpud_separate_ant[i]; + for (j = 0; j < 16; j++) { + t = trans_group_udfix[j]; + trans_cpud_separate[t][i] = + cpud_separate_ind[cp_ttable[t][cp]]; + } + } + + if (!write_symc_trans_file()) + fprintf(stderr, "Error writing SymTrans file\n"); +} + +void +init_symcoord() +{ + int i; + + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_coord(); + + init_symc_trans(); + + for (i = 0; all_sd[i] != NULL; i++) + gensym(all_sd[i]); + + init_symc_moves(); +} + diff --git a/src/symcoord.h b/src/symcoord.h @@ -0,0 +1,17 @@ +#ifndef SYMCOORD_H +#define SYMCOORD_H + +#include "coord.h" + +extern Coordinate coord_cp_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_drudfin_noE_sym16; +extern Coordinate coord_nxopt31; + +extern SymData *all_sd[]; + +void free_sd(SymData *sd); +void init_symcoord(); + +#endif diff --git a/src/threader_eager.c b/src/threader_eager.c @@ -1,163 +0,0 @@ -#include <pthread.h> -#include "threader_eager.h" - -typedef struct { - AlgList * sols; - pthread_mutex_t * sols_mutex; -} ThreadData; - -typedef struct { - DfsArg * arg; - Solver * solver; - Threader * threader; - AlgList * starts; - AlgListNode ** node; - pthread_mutex_t * start_mutex; -} ThreadInitData; - -static void append_sol(Alg *, void *); -static void * instance_thread(void *); -static void dispatch(DfsArg *, AlgList *, Solver *, Threader *); -static AlgList * possible_starts(DfsArg *, Solver *); -static int get_nsol(void *); - -Threader threader_eager = { - .append_sol = append_sol, - .dispatch = dispatch, - .get_nsol = get_nsol, -}; - -static void -append_sol(Alg *alg, void *threaddata) -{ - ThreadData *td = (ThreadData *)threaddata; - - pthread_mutex_lock(td->sols_mutex); - append_alg(td->sols, alg); - pthread_mutex_unlock(td->sols_mutex); -} - -static AlgList * -possible_starts(DfsArg *arg, Solver *solver) -{ - AlgList *ret = new_alglist(); - - if (solver->is_solved(solver->param, arg->cubedata)) { - if (arg->opts->min_moves == 0 && arg->d == 0) - append_sol(new_alg(""), arg->threaddata); - return ret; - } - - for (int i = 0; solver->moveset->sorted_moves[i] != NULLMOVE; i++) { - Move m = solver->moveset->sorted_moves[i]; - Alg *alg = new_alg(""); - append_move(alg, m, false); - append_alg(ret, alg); - free_alg(alg); - -/* TODO: check if step not final */ - if (arg->opts->can_niss) { - alg = new_alg(""); - append_move(alg, m, true); - append_alg(ret, alg); - free_alg(alg); - } - } - - return ret; -} - -static void * -instance_thread(void *arg) -{ - ThreadInitData *tid = (ThreadInitData *)arg; - - while (true) { - pthread_mutex_lock(tid->start_mutex); - AlgListNode *node = *(tid->node); - if (node == NULL) { - pthread_mutex_unlock(tid->start_mutex); - break; - } - *(tid->node) = (*(tid->node))->next; - pthread_mutex_unlock(tid->start_mutex); - -/* TODO: adjust for longer (arbitrarily long?) starting sequences */ - void *data = tid->solver->alloc_cubedata(tid->solver->param); - tid->solver->copy_cubedata( - tid->solver->param, tid->arg->cubedata, data); - bool inv = node->alg->inv[node->alg->len-1]; - if (inv) - tid->solver->invert_cube( - tid->solver->param, data); - tid->solver->apply_move( - tid->solver->param, data, node->alg->move[0]); - - DfsArg newarg; - newarg.cubedata = data; - newarg.threaddata = tid->arg->threaddata; - newarg.opts = tid->arg->opts; - newarg.d = tid->arg->d; - newarg.niss = inv; - newarg.current_alg = new_alg(""); - copy_alg(node->alg, newarg.current_alg); - - dfs(&newarg, tid->solver, tid->threader); - - tid->solver->free_cubedata(tid->solver->param, data); - free_alg(newarg.current_alg); - } - - return NULL; -} - -static void -dispatch(DfsArg *arg, AlgList *sols, Solver *solver, Threader *threader) -{ - int nthreads = arg->opts->nthreads; - ThreadInitData tid[nthreads]; - pthread_t t[nthreads]; - - pthread_mutex_t *sols_mutex = malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(sols_mutex, NULL); - - arg->threaddata = malloc(sizeof(ThreadData)); - ThreadData *td = (ThreadData *)arg->threaddata; - td->sols = sols; - td->sols_mutex = sols_mutex; - - AlgList *starts = possible_starts(arg, solver); - AlgListNode *node = starts->first; - pthread_mutex_t *start_mutex = malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(start_mutex, NULL); - for (int i = 0; i < nthreads; i++) { - tid[i].arg = arg; - tid[i].solver = solver; - tid[i].threader = threader; - tid[i].starts = starts; - tid[i].node = &node; - tid[i].start_mutex = start_mutex; - - pthread_create(&t[i], NULL, instance_thread, &tid[i]); - } - - for (int i = 0; i < nthreads; i++) - pthread_join(t[i], NULL); - - free(td); - free(sols_mutex); - free_alglist(starts); - free(start_mutex); -} - -static int -get_nsol(void *threaddata) -{ - ThreadData *td = (ThreadData *)threaddata; - - pthread_mutex_lock(td->sols_mutex); - int n = td->sols->len; - pthread_mutex_unlock(td->sols_mutex); - - return n; -} diff --git a/src/threader_eager.h b/src/threader_eager.h @@ -1,8 +0,0 @@ -#ifndef THREADER_EAGER_H -#define THREADER_EAGER_H - -#include "solve.h" - -extern Threader threader_eager; - -#endif diff --git a/src/threader_single.c b/src/threader_single.c @@ -1,35 +0,0 @@ -#include "threader_single.h" - -static void append_sol(Alg *, void *); -static void dispatch(DfsArg *, AlgList *, Solver *, Threader *); -static int get_nsol(void *); - -Threader threader_single = { - .append_sol = append_sol, - .dispatch = dispatch, - .get_nsol = get_nsol, -}; - -static void -append_sol(Alg *alg, void *threaddata) -{ - append_alg((AlgList *)threaddata, alg); -} - -static void -dispatch(DfsArg *arg, AlgList *sols, Solver *solver, Threader *threader) -{ - arg->threaddata = sols; - arg->niss = false; - arg->current_alg = new_alg(""); - - dfs(arg, solver, threader); - - free_alg(arg->current_alg); -} - -static int -get_nsol(void *threaddata) -{ - return ((AlgList *)threaddata)->len; -} diff --git a/src/threader_single.h b/src/threader_single.h @@ -1,8 +0,0 @@ -#ifndef THREADER_SINGLE_H -#define THREADER_SINGLE_H - -#include "solve.h" - -extern Threader threader_single; - -#endif diff --git a/src/trans.c b/src/trans.c @@ -1,23 +1,31 @@ -#define TRANS_C - #include "trans.h" /* Local functions ***********************************************************/ +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static bool write_ttables_file(); + /* Tables and other data *****************************************************/ -static Cube mirror_cube = { -.ep = { [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL, +static int ep_mirror[12] = { + [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL, [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL, - [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL }, -.cp = { [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL, - [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL }, -.xp = { [U_center] = U_center, [D_center] = D_center, + [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL +}; + +static int cp_mirror[8] = { + [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL, + [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL +}; + +static int cpos_mirror[6] = { + [U_center] = U_center, [D_center] = D_center, [R_center] = L_center, [L_center] = R_center, - [F_center] = F_center, [B_center] = B_center } + [F_center] = F_center, [B_center] = B_center }; -static char rotation_alg_string[100][NROTATIONS] = { +static char rotation_alg_string[100][NROTATIONS] = { [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3", [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2", [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3", @@ -26,39 +34,176 @@ static char rotation_alg_string[100][NROTATIONS] = { [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3", }; -Alg *rotation_alg_arr[NROTATIONS]; -Move moves_ttable[NTRANS][NMOVES]; -Trans trans_ttable[NTRANS][NTRANS]; -Trans trans_itable[NTRANS]; +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; -/* Public functions **********************************************************/ +int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +int eo_ttable[NTRANS][POW2TO11]; +int cp_ttable[NTRANS][FACTORIAL8]; +int co_ttable[NTRANS][POW3TO7]; +int cpos_ttable[NTRANS][FACTORIAL6]; +Move moves_ttable[NTRANS][NMOVES]; -void -apply_trans(Trans t, Cube *cube) +/* Local functions implementation ********************************************/ + +static bool +read_ttables_file() { - Cube aux; - Alg *inv; - int i; + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; - inv = inverse_alg(rotation_alg(t % NROTATIONS)); - copy_cube(cube, &aux); - make_solved(cube); - - if (t >= NROTATIONS) - compose(&mirror_cube, cube); - apply_alg(inv, cube); - compose(&aux, cube); - apply_alg(rotation_alg(t % NROTATIONS), cube); - if (t >= NROTATIONS) { - compose(&mirror_cube, cube); - for (i = 0; i < 8; i++) - cube->co[i] = (3 - cube->co[i]) % 3; + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fread(co_ttable[m], b, me[5], f) == me[5]; + r = r && fread(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fread(moves_ttable[m], b, me[7], f) == me[7]; } + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_alg(r)); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_alg(r), ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + free_alg(inv); + return ret; +} + +static bool +write_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fwrite(co_ttable[m], b, me[5], f) == me[5]; + r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +/* Public functions **********************************************************/ + +Cube +apply_trans(Trans t, Cube cube) +{ + /*init_trans();*/ + + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; } -/* Trans inverse_trans(Trans t) { @@ -86,18 +231,23 @@ inverse_trans(Trans t) return inverse_trans_aux[t]; } -*/ - -Trans -inverse_trans(Trans t) -{ - return trans_itable[t]; -} Alg * -rotation_alg(Trans i) +rotation_alg(Trans t) { - return rotation_alg_arr[i % NROTATIONS]; + int i; + + static Alg *rotation_alg_arr[NROTATIONS]; + static bool initialized = false; + + if (!initialized) { + for (i = 0; i < NROTATIONS; i++) + rotation_alg_arr[i] = new_alg(rotation_alg_string[i]); + + initialized = true; + } + + return rotation_alg_arr[t % NROTATIONS]; } void @@ -105,20 +255,10 @@ transform_alg(Trans t, Alg *alg) { int i; + /*init_trans();*/ + for (i = 0; i < alg->len; i++) - alg->move[i] = transform_move(t, alg->move[i]); -} - -Move -transform_move(Trans t, Move m) -{ - return moves_ttable[t][m]; -} - -Trans -transform_trans(Trans t, Trans m) -{ - return trans_ttable[t][m]; + alg->move[i] = moves_ttable[t][alg->move[i]]; } void @@ -128,63 +268,106 @@ init_trans() { return; initialized = true; - int i; - Alg *nonsym_alg, *nonsym_inv; - Cube aux, cube; + init_moves(); + + Cube aux, cube, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; Move mi, move; - Trans t, u, v; + Trans m; - init_moves(); + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_alg(i), (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } - for (i = 0; i < NROTATIONS; i++) - rotation_alg_arr[i] = new_alg(rotation_alg_string[i]); + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); - for (t = 0; t < NTRANS; t++) { + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_alg(m), (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } for (mi = 0; mi < NMOVES; mi++) { - make_solved(&aux); - apply_move(mi, &aux); - apply_trans(t, &aux); + /* Old version: + * + aux = apply_trans(m, apply_move(mi, (Cube){0})); for (move = 0; move < NMOVES; move++) { - copy_cube(&aux, &cube); - apply_move(inverse_move(move), &cube); - if (is_solved(&cube)) { - moves_ttable[t][mi] = move; - break; - } + cube = apply_move(inverse_move(move), aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube) || is_solved(mirr)) + moves_ttable[m][mi] = move; } - } - } + */ - nonsym_alg = new_alg("R' U' F"); - nonsym_inv = inverse_alg(nonsym_alg); - - for (t = 0; t < NTRANS; t++) { - for (u = 0; u < NTRANS; u++) { - make_solved(&aux); - apply_alg(nonsym_alg, &aux); - apply_trans(u, &aux); - apply_trans(t, &aux); - for (v = 0; v < NTRANS; v++) { - copy_cube(&aux, &cube); - apply_trans(v, &cube); - apply_alg(nonsym_inv, &cube); - if (is_solved(&cube)) { - /* This is the inverse of the correct - value, it will be inverted later */ - trans_ttable[t][u] = v; - if (v == uf) - trans_itable[t] = u; + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + if (is_solved(cube)) { + moves_ttable[m][mi] = move; break; } } } } - for (t = 0; t < NTRANS; t++) - for (u = 0; u < NTRANS; u++) - trans_ttable[t][u] = trans_itable[trans_ttable[t][u]]; - - free_alg(nonsym_alg); - free_alg(nonsym_inv); + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); } diff --git a/src/trans.h b/src/trans.h @@ -3,30 +3,24 @@ #include "moves.h" -void apply_trans(Trans t, Cube *cube); +/* + * Tables are exposed to allow faster partial transformations in some + * specific cases (in symcoord) + */ +extern int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +extern int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +extern int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +extern int eo_ttable[NTRANS][POW2TO11]; +extern int cp_ttable[NTRANS][FACTORIAL8]; +extern int co_ttable[NTRANS][POW3TO7]; +extern int cpos_ttable[NTRANS][FACTORIAL6]; +extern Move moves_ttable[NTRANS][NMOVES]; + +Cube apply_trans(Trans t, Cube cube); Trans inverse_trans(Trans t); Alg * rotation_alg(Trans i); -void transform_alg(Trans t, Alg *alg); -Move transform_move(Trans t, Move m); -Trans transform_trans(Trans t, Trans m); +void transform_alg(Trans i, Alg *alg); void init_trans(); -#ifndef TRANS_C - -extern TransGroup tgrp_udfix; - -#else - -TransGroup -tgrp_udfix = { - .n = 16, - .t = { uf, ur, ub, ul, - df, dr, db, dl, - uf_mirror, ur_mirror, ub_mirror, ul_mirror, - df_mirror, dr_mirror, db_mirror, dl_mirror }, -}; - -#endif - #endif diff --git a/src/utils.c b/src/utils.c @@ -1,5 +1,3 @@ -#define UTILS_C - #include "utils.h" void @@ -161,24 +159,24 @@ is_perm(int *a, int n) { int *aux = malloc(n * sizeof(int)); int i; - bool ret = true; for (i = 0; i < n; i++) aux[i] = 0; for (i = 0; i < n; i++) { if (a[i] < 0 || a[i] >= n) - ret = false; + return false; else aux[a[i]] = 1; } for (i = 0; i < n; i++) if (!aux[i]) - ret = false; + return false; free(aux); - return ret; + + return true; } bool @@ -197,7 +195,7 @@ perm_sign(int *a, int n) { int i, j, ret = 0; - if (!is_perm(a, n)) + if (!is_perm(a,n)) return -1; for (i = 0; i < n; i++) @@ -213,7 +211,7 @@ perm_to_index(int *a, int n) int i, j, c, ret = 0; if (!is_perm(a, n)) - return factorial(n); + return -1; for (i = 0; i < n; i++) { c = 0; diff --git a/tests/alg_tests.c b/tests/alg_tests.c @@ -1,266 +0,0 @@ -#include "alg_tests.h" - -bool testmethod_append_move(void *); -bool testmethod_new_alg(void *); -/* -bool testmethod_compose_alg(void *); -bool testmethod_inverse_alg(void *); -bool testmethod_on_inverse(void *); -bool testmethod_unniss(void *); -*/ - -typedef struct { - Move *move; - bool *inv; - int len; - Move m; - bool inverse; -} append_move_t; - -Move m_app1[] = {F, x, D3}; -bool i_app1[] = {true, false, false}; -append_move_t append_move_case1 = { - .move = m_app1, - .inv = i_app1, - .len = 3, - .m = L3, - .inverse = false, -}; - -Move m_app2[] = {S, U, x2, M2}; -bool i_app2[] = {true, false, true, true}; -append_move_t append_move_case2 = { - .move = m_app2, - .inv = i_app2, - .len = 4, - .m = R, - .inverse = true, -}; - -Move m_app3[] = {U, U, U, U, U}; -bool i_app3[] = {false, false, false, false, false}; -append_move_t append_move_case3 = { - .move = m_app3, - .inv = i_app3, - .len = 5, - .m = U, - .inverse = false, -}; - -append_move_t *append_move_cases[] = { - &append_move_case1, - &append_move_case2, - &append_move_case3, -}; - -Test test_append_move = { - .name = "Appending a move to and alg", - .t = testmethod_append_move, - .cases = (void **)append_move_cases, -}; - -typedef struct { - char *str; - Move *move; - bool *inv; - int len; - Move *move_normal; - int len_normal; - Move *move_inverse; - int len_inverse; -} new_alg_t; - -/* Alg F U B' (L3 D) x M (S) y3 */ -Move m_new1[] = {F, U, B3, L3, D, x, M, S, y3}; -Move mn_new1[] = {F, U, B3, x, M, y3}; -Move mi_new1[] = {L3, D, S}; -bool i_new1[] = {false, false, false, true, true, false, false, true, false}; -new_alg_t new_alg_case1 = { - .str = "F U B' (L3 D) x M (S) y3", - .move = m_new1, - .inv = i_new1, - .len = 9, - .move_normal = mn_new1, - .len_normal = 6, - .move_inverse = mi_new1, - .len_inverse = 3, -}; -new_alg_t *new_alg_cases[] = {&new_alg_case1}; - -Test test_new_alg = { - .name = "Initializing an alg from a string", - .t = testmethod_new_alg, - .cases = (void **)new_alg_cases, -}; - -Test *alg_all_tests[] = { - &test_append_move, - &test_new_alg, -/* - &test_append_alg, - &test_compose_alg, - &test_inverse_alg, - &test_on_inverse, - &test_unniss, -*/ - NULL -}; -TestSuite alg_suite = { - .setup = NULL, - .tests = alg_all_tests, - .teardown = NULL, -}; - -TestSuite *alg_suites[] = { - &alg_suite, - NULL -}; - -bool -testmethod_append_move(void *a) -{ - append_move_t *b = (append_move_t *)a; - int li, ln; - Alg *alg; - - /* Small to test reallocation */ - alg = malloc(sizeof(Alg)); - alg->allocated = 5; - alg->move = malloc(alg->allocated * sizeof(Move)); - alg->inv = malloc(alg->allocated * sizeof(bool)); - alg->len = b->len; - memcpy(alg->move, b->move, alg->len * sizeof(Move)); - memcpy(alg->inv, b->inv, alg->len * sizeof(bool)); - alg->move_normal = malloc(alg->allocated * sizeof(Move)); - alg->move_inverse = malloc(alg->allocated * sizeof(Move)); - - li = ln = 0; - for (int i = 0; i < alg->len; i++) { - if (alg->inv[i]) - alg->move_inverse[li++] = alg->move[i]; - else - alg->move_normal[ln++] = alg->move[i]; - } - alg->len_inverse = li; - alg->len_normal = ln; - - append_move(alg, b->m, b->inverse); - - if (alg->len != b->len + 1) { - printf("Alg has wrong len (%d instead of %d)\n", - alg->len, b->len + 1); - goto append_move_fail; - } - if (alg->move[alg->len-1] != b->m) { - printf("Wrong last move (%s instead of %s)\n", - move_string(alg->move[alg->len-1]), - move_string(b->m)); - goto append_move_fail; - } - if (alg->inv[alg->len-1] != b->inverse) { - printf("Wrong inverse flag for last move " - "(%s instead of %s)\n", - b->inverse ? "normal" : "inverse", - b->inverse ? "inverse" : "normal"); - goto append_move_fail; - } - if (b->inverse) { - if (alg->len_inverse != li + 1 || - alg->len_normal != ln) { - printf("%d moves on normal (should be %d)" - " and %d on inverse (should be %d)\n", - alg->len_normal, ln, - alg->len_inverse, li + 1); - goto append_move_fail; - } - if (alg->move_inverse[alg->len_inverse-1] != b->m) { - printf("Wrong move on inverse (%s instead of %s)\n", - move_string(alg->move_inverse[alg->len-1]), - move_string(b->m)); - goto append_move_fail; - } - } else { - if (alg->len_inverse != li || - alg->len_normal != ln + 1) { - printf("%d moves on normal (should be %d)" - " and %d on inverse (should be %d)\n", - alg->len_normal, ln, - alg->len_inverse, li + 1); - goto append_move_fail; - } - if (alg->move_normal[alg->len_normal-1] != b->m) { - printf("Wrong move on normal (%s instead of %s)\n", - move_string(alg->move_normal[alg->len-1]), - move_string(b->m)); - goto append_move_fail; - } - } - - free(alg); - return true; - -append_move_fail: - free(alg); - return false; -} - -bool -testmethod_new_alg(void *a) -{ - new_alg_t *b = (new_alg_t *)a; - Alg *alg = new_alg(b->str); - - if (alg->len != b->len) { - printf("Algs have different length (%d instead of %d)\n", - alg->len, b->len); - goto new_alg_fail; - } - - for (int i = 0; i < alg->len; i++) { - if (alg->move[i] != b->move[i] || alg->inv[i] != b->inv[i]) { - printf("Algs differ on move %d\n", i); - printf("Expected: %s\nActual: ", b->str); - print_alg(alg, false); - goto new_alg_fail; - } - } - - if (alg->len_normal != b->len_normal) { - printf("Algs have different number of moves on normal" - " (%d instead of %d)\n", - alg->len_normal, b->len_normal); - goto new_alg_fail; - } - for (int i = 0; i < alg->len_normal; i++) { - if (alg->move_normal[i] != b->move_normal[i]) { - printf("Algs have different move %d on normal" - " (%s instead of %s)\n", i, - move_string(alg->move_normal[i]), - move_string(b->move_normal[i])); - goto new_alg_fail; - } - } - - if (alg->len_inverse != b->len_inverse) { - printf("Algs have different number of moves on inverse" - " (%d instead of %d)\n", - alg->len_inverse, b->len_inverse); - goto new_alg_fail; - } - for (int i = 0; i < alg->len_inverse; i++) { - if (alg->move_inverse[i] != b->move_inverse[i]) { - printf("Algs have different move %d on inverse:\n" - " (%s instead of %s)\n", i, - move_string(alg->move_inverse[i]), - move_string(b->move_inverse[i])); - goto new_alg_fail; - } - } - - free(alg); - return true; - -new_alg_fail: - free(alg); - return false; -} diff --git a/tests/alg_tests.h b/tests/alg_tests.h @@ -1,21 +0,0 @@ -#ifndef ALG_TESTS_H -#define ALG_TESTS_H - -#include "../src/alg.h" -#include "test_common.h" - -extern Test test_append_move; -extern Test test_new_alg; -/* -extern Test remove_last_move; -extern Test test_compose_alg; -extern Test test_inverse_alg; -extern Test test_on_inverse; -extern Test test_unniss; -*/ - -extern TestSuite alg_suite; - -extern TestSuite *alg_suites[]; - -#endif diff --git a/tests/coord_tests.c b/tests/coord_tests.c @@ -1,50 +0,0 @@ -#include "coord_tests.h" - -bool testmethod_indexes_consistent(void *); - -Test test_indexes_consistent = { - .name = "Consitency of index and anti-index", - .t = testmethod_indexes_consistent, - .cases = (void **)all_coordinates, -}; -Test *coord_pre_init[] = { - &test_indexes_consistent, - NULL -}; -TestSuite coord_pre_init_suite = { - .setup = NULL, - .tests = coord_pre_init, - .teardown = NULL, -}; - -TestSuite *coord_suites[] = { - &coord_pre_init_suite, - NULL -}; - -bool -testmethod_indexes_consistent(void *a) -{ - uint64_t ui, uj; - Cube c; - Coordinate *coord; - - coord = (Coordinate *)a; - - if (coord->type != COMP_COORD) - return true; /* Not applicable */ - - gen_coord(coord); - for (ui = 0; ui < coord->max; ui++) { - indexers_makecube(coord->i, ui, &c); - uj = indexers_getind(coord->i, &c); - if (ui != uj) { - fprintf(stderr, "Error with coordinate %s: " - "%" PRIu64 " != %" PRIu64 "\n", - coord->name, uj, ui); - return false; - } - } - - return true; -} diff --git a/tests/coord_tests.h b/tests/coord_tests.h @@ -1,13 +0,0 @@ -#ifndef COORD_TESTS_H -#define COORD_TESTS_H - -#include "../src/coord.h" -#include "test_common.h" - -extern Test test_indexes_consistent; - -extern TestSuite coord_pre_init_suite; - -extern TestSuite *coord_suites[]; - -#endif diff --git a/tests/fst_tests.c b/tests/fst_tests.c @@ -1,184 +0,0 @@ -#include "fst_tests.h" - -static bool testmethod_fst_is_consistent(void *); -static bool testmethod_cube_to_fst_to_cube(void *); -static bool testmethod_fst_move(void *); -static bool testmethod_fst_inverse(void *); -static bool check_equal_and_log(Cube *, Cube *); -static void void_to_cube(void *, Cube *); - -char *algs[] = { - "", - "U", "U2", "U'", "D", "D2", "D'", "R", "R2", "R'", - "L", "L2", "L'", "F", "F2", "F'", "B", "B2", "B'", - "U2 R2 U2 R2 U2", - "U2 F2 R2 B2 U2 D2 F2 L2 B2", - "RUR'URU2R'", - "L2 D R U2 B2 L", - "R'U'F", - "F2 U' R2 D' B2 D2 R2 D2 R2 U' F L' U' R B F2 R B' D2", - "D L2 F2 R2 D R2 U L2 U' B2 D L' F2 U2 B' L D' U' R' B2 F2", - "F' L2 F' D' R F2 L U L' D2 R2 F2 D2 R2 B' L2 B2 U2 F D2 B", - NULL, -}; - -Test test_fst_is_consistent = { - .name = "Consitency of FST (converted from cube)", - .t = testmethod_fst_is_consistent, - .cases = (void **)algs, -}; -Test test_cube_to_fst_to_cube = { - .name = "Cube to FST to cube", - .t = testmethod_cube_to_fst_to_cube, - .cases = (void **)algs, -}; -Test test_fst_move = { - .name = "FST move", - .t = testmethod_fst_move, - .cases = (void **)algs, -}; -Test test_fst_inverse = { - .name = "FST inverse", - .t = testmethod_fst_inverse, - .cases = (void **)algs, -}; - -Test *fst_pre_init[] = { - &test_fst_is_consistent, - &test_cube_to_fst_to_cube, - NULL -}; -TestSuite fst_pre_init_suite = { - .setup = NULL, - .tests = fst_pre_init, - .teardown = NULL, -}; - -Test *fst_post_init[] = { - &test_fst_move, - &test_fst_inverse, - NULL -}; -TestSuite fst_post_init_suite = { - .setup = init_fst, - .tests = fst_post_init, - .teardown = NULL, -}; - -TestSuite *fst_suites[] = { - &fst_pre_init_suite, - &fst_post_init_suite, - NULL -}; - -static bool -check_equal_and_log(Cube *c, Cube *d) -{ - bool ret = equal(c, d); - - if (!ret) { - printf("\n"); - printf("These cubes should be equal, but are not:\n\n"); - print_cube(c); - printf("\n"); - print_cube(d); - printf("\n"); - } - - return ret; -} - -static void -void_to_cube(void *a, Cube *c) -{ - char *algstr; - Alg *alg; - - algstr = (char *)a; - alg = new_alg(algstr); - make_solved(c); - apply_alg(alg, c); - free_alg(alg); -} - -bool -testmethod_fst_is_consistent(void *a) -{ - FstCube fst_uf, fst_fr, fst_rd; - Cube c, c_fr, c_rd; - bool consistent_fr, consistent_rd, result; - - void_to_cube(a, &c); - copy_cube(&c, &c_fr); - apply_trans(fr, &c_fr); - - copy_cube(&c, &c_rd); - apply_trans(rd, &c_rd); - - fst_uf = cube_to_fst(&c); - fst_fr = cube_to_fst(&c_fr); - fst_rd = cube_to_fst(&c_rd); - - consistent_fr = fst_uf.fr_eofb == fst_fr.uf_eofb && - fst_uf.fr_eposepe == fst_fr.uf_eposepe && - fst_uf.fr_coud == fst_fr.uf_coud; - - consistent_rd = fst_uf.rd_eofb == fst_rd.uf_eofb && - fst_uf.rd_eposepe == fst_rd.uf_eposepe && - fst_uf.rd_coud == fst_rd.uf_coud; - - result = consistent_fr && consistent_rd; - - if (!result) - printf("\nFailed with alg %s\n", (char *)a); - - return result; -} - -bool -testmethod_cube_to_fst_to_cube(void *a) -{ - Cube c, d; - FstCube fst; - - void_to_cube(a, &c); - fst = cube_to_fst(&c); - fst_to_cube(fst, &d); - - return check_equal_and_log(&c, &d);; -} - -bool -testmethod_fst_move(void *a) -{ - int i; - Alg *alg; - Cube c, d; - FstCube fst; - - void_to_cube(a, &c); - alg = new_alg((char *)a); - make_solved(&d); - fst = cube_to_fst(&d); - - for (i = 0; i < alg->len; i++) - fst = fst_move(alg->move[i], fst); - - fst_to_cube(fst, &d); - - free_alg(alg); - - return check_equal_and_log(&c, &d); -} - -bool -testmethod_fst_inverse(void *a) -{ - Cube c, d; - - void_to_cube(a, &c); - fst_to_cube(fst_inverse(cube_to_fst(&c)), &d); - invert_cube(&c); - - return check_equal_and_log(&c, &d); -} diff --git a/tests/fst_tests.h b/tests/fst_tests.h @@ -1,19 +0,0 @@ -#ifndef FST_TESTS_H -#define FST_TESTS_H - -#include "../src/fst.h" -#include "test_common.h" - -extern char *algs[]; - -extern Test test_fst_is_consistent; -extern Test test_cube_to_fst_to_cube; -extern Test test_fst_move; -extern Test test_fst_inverse; - -extern TestSuite fst_pre_init_suite; -extern TestSuite fst_post_init_suite; - -extern TestSuite *fst_suites[]; - -#endif diff --git a/tests/test.c b/tests/test.c @@ -1,85 +0,0 @@ -#include <stdio.h> - -#include "alg_tests.h" -#include "coord_tests.h" -#include "fst_tests.h" - -static bool run_test(Test *); -static bool run_suite(TestSuite *); - -static bool -run_test(Test *test) -{ - int i; - - printf("Running test %s...", test->name); - for (i = 0; test->cases[i] != NULL; i++) { - if (!test->t(test->cases[i])) { - printf("FAILED!\n"); - return false; - } - } - - printf("OK\n"); - return true; -} - -static bool -run_suite(TestSuite *suite) -{ - int i; - - if (suite->setup != NULL) - suite->setup(); - for (i = 0; suite->tests[i] != NULL; i++) - if(!run_test(suite->tests[i])) - return false; - if (suite->teardown != NULL) - suite->teardown(); - - return true; -} - -static bool -module_in_args(char *module, int argc, char *argv[]) -{ - for (int i = 0; i < argc; i++) - if (!strcmp(module, argv[i])) - return true; - return false; -} - -int main(int argc, char *argv[]) { - /* TODO: init should be in testsuites */ - init_env(); - init_trans(); - /**************************************/ - - TestModule alg = { .name = "alg", .suites = alg_suites }; - TestModule fst = { .name = "fst", .suites = fst_suites }; - TestModule coord = { .name = "coord", .suites = coord_suites }; - TestModule *modules[999] = { - &alg, - &fst, - &coord, - NULL - }; - - - bool all = argc == 1 || module_in_args("all", argc, argv); - int count = 0; - for (int i = 0; modules[i] != NULL; i++) { - if (all || module_in_args(modules[i]->name, argc, argv)) { - for (int j = 0; modules[i]->suites[j] != NULL; j++) { - if (!run_suite(modules[i]->suites[j])) { - return 1; - } else { - count++; - } - } - } - } - - printf("All tests passed (%d test suites).\n", count); - return 0; -} diff --git a/tests/test_common.h b/tests/test_common.h @@ -1,41 +0,0 @@ -#ifndef TEST_COMMON_H -#define TEST_COMMON_H - -/* - * Common utilities for testing. - * A VoidMethod can be used as a setup or teardown method for testing, see - * the TestSuite struct. A TestMethod takes a void pointer (usually cast - * to some data to be used for testing, but can also be ignored) and returns - * a bool: true for pass, false for fail. - * A Test consists of a name (string), a TestMethod and an array of void - * pointers that describe the test cases. Each element of this array is - * passed to the test method sequentially, and the test session stops on - * the first failure. - * A test suite is just a list of tests with a setup and a teardown method. - * The setup method is run once and for all before the first test, and the - * teardown is run at the end of the testsuite. - * Finally, a TestModule roughly corresponds to a test file. This type is - * only used in test.c to collect multiple test modules. - */ - -typedef void (*VoidMethod)(void); -typedef bool (*TestMethod)(void *); - -typedef struct { - char * name; - TestMethod t; - void ** cases; -} Test; - -typedef struct { - VoidMethod setup; - Test ** tests; - VoidMethod teardown; -} TestSuite; - -typedef struct { - char * name; - TestSuite ** suites; -} TestModule; - -#endif diff --git a/www/download/index.html b/www/download/index.html @@ -1,217 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <title> Download | Nissy </title> -<meta name="viewport" content="width=device-width" /> <link rel="stylesheet" type="text/css" href="/style-3.css"> - <link rel="icon" href="/favicon.png"> - <meta charset="utf-8"> -</head> - -<body> - -<nav class="top"> - <table class="menu"> - <tr> - <td class="logo"> <a href="/"><img src="/favicon.png" alt="logo"></a> </td> - <td class="links"> - <a href="/download/">Download</a> | - <a href="/examples/">Examples</a> - </td> - </tr> - </table> -</nav> - -<hr class="line"> - - -<h1>Get Nissy</h1> - -<table class="dltable"> -<tr> - <td></td> - <td><strong>Source code</strong></td> - <td><strong>Windows executable</strong></td> -</tr> -<tr> - <td><strong>Latest version</strong></td> - <td><a href="/nissy-2.0.3.tar.gz">nissy-2.0.3.tar.gz (67Kb)</a></td> - <td><a href="/nissy-2.0.3.exe">nissy-2.0.3.exe (780Kb)</a></td> -</tr> -</table> - -<p> -In this page you can find download links (see above), -<a href="#Installation">installation instructions</a> and -<a href="#Upgrade">upgrade instructions</a> -for nissy. If instead you wish to clone the -<a href="https://git.tronto.net/nissy/">git repository</a>, -you can use git: -</p> - -<pre> -<code>git clone https://git.tronto.net/nissy</code> -</pre> - -<p> -For a summary of changes and a list of older versions see below. Some versions -(for example 1.0) are not available directly, but can be obtained from -the git repository. -</p> - -<h2 id="Installation">Installation</h2> - -<h3>System requirements</h3> - -<p> -A full installation of nissy requires about 3.1Gb of space, -of which 2.3Gb are occupied by the huge pruning table for fast optimal solving, -and running it requires the same amount of RAM. -One can choose to never use this function and not to install the relative -pruning table. There is an alternative (slower) -optimal solving function that uses about 500Mb of RAM. - -When generating the pruning tables automatically (see the section Tables below), -at least 5.3Gb or RAM are required. -</p> - -<h3>Windows</h3> - -<p> -Try downloading and executing in a terminal the file <code>nissy.exe</code>, -then follow the instructions in the <strong>Tables</strong> section below for -installing the pruning tables. -If <code>nissy.exe</code> does not work, you can try following the UNIX instructions -in WSL (Windows Subsystem for Linux) or in a similar environment. -</p> - -<h3>UNIX (Linux, MacOS, *BSD...)</h3> -<p> -Download the source archive (.tar.gz). Extract it -with your favorite archive program, for example with -</p> -<pre><code>tar -xvzf nissy-VERSION.tar.gz</code></pre> -<p> -Open a terminal in the directory just extracted. -If you wish, edit the <code>Makefile</code> to match your local configuration -(this is usually not necessary, but you may want to change the -<code>PREFIX</code> variable to change the installation path) and run -<pre><code>make</code></pre> -<p> -followed by -</p> -<pre><code>make install</code></pre> -<p> -Then follow the instructions below to install the pruning tables. -</p> - -<h3>Tables</h3> - -<p> -Once you have installed nissy, run -</p> - -<pre><code>nissy gen</code></pre> - -<p> -to generate all the tables that Nissy will ever need. -Running this command requires around 5.3Gb of RAM, and it can take some time -(about 40 minutes on my fairly old but decent laptop, with 8 CPU threads). -</p> - -<p> -Some unnecessary technical detail: by default this command is going to use -at most 64 threads. If you want you can choose to use more threads (if your CPU -is very powerful) or fewer threads (if you for example want to run this command -in the background while you do other stuff) with the <code>-t</code> option, for -example <code>nissy gen -t 1</code>. -</p> - -<p> -Alternatively, you can -<a href="/nissy-tables-2.0.2.zip">download all the tables (1.7Gb)</a> and -copy them into the correct folder (see manual page, <code>ENVIRONMENT</code> -section). On UNIX operating systems this folder is either -<code>.nissy/tables</code> in the user's home directory or -<code>$XDG_DATA_HOME/nissy/tables</code> if the XDG variable is configured. -On Windows it is the same directory as the <code>nissy.exe</code> executable -file. -</p> - -<h2 id="Upgrade">Upgrading</h2> -<p> -If you already have nissy installed and you want to upgrade to a more -recent version, you can simply repeat the installation process: -</p> -<ul> -<li> -On Windows: simply replace nissy.exe with the new file with the same name. -</li> -<li> -On UNIX systems: download the new version of the source code, extract it in -a new folder and run <code>make</code> and <code>make install</code> again. -</li> -</ul> -<p> -Between each version new table files might have been added, or old ones -may be not used anymore. Nissy will deal with this automatically. -</p> - -<h2>Version history</h2> - -<h3>Nissy v2</h3> - -<table class=dltable> -<tr> - <td><strong>Version</strong></td> - <td><strong>Date</strong></td> - <td><strong>Comment</strong></td> -</tr> -<tr> - <td><a href="/nissy-2.0.3.tar.gz">2.0.3</a></td> - <td>2022-09-10</td> - <td>Fixed bug in scramble dr</td> -</tr> -<tr> - <td><a href="/nissy-2.0.2.tar.gz">2.0.2</a></td> - <td>2022-06-01</td> - <td>Improved table generation speed</td> -</tr> -<tr> - <td><a href="/nissy-2.0.1.tar.gz">2.0.1</a></td> - <td>2022-02-22</td> - <td>Bugfix release</td> -</tr> -<tr> - <td>2.0</td> - <td>2021-12-29</td> - <td>Rewritten from scratch; much faster optimal solver</td> -</tr> -</table> - -<h3>Nissy v1</h3> - -<p> -Nissy v1 was released in 2020. It was slow, full of bugs and the code -was quite terrible. But in practice it got its job done most of the time. -</p> - - -<hr class="line"> - -<nav class="bottom"> - <table class="footer"> - <tr> - <td class="contact"> - <a href="https://sebastiano.tronto.net">sebastiano.tronto.net</a> - </td> - <td class="hosted"> - <a href="mailto:sebastiano@tronto.net"> - sebastiano@tronto.net - </a> - </td> - </tr> - </table> -</nav> - -</body> -</html> diff --git a/www/examples/index.html b/www/examples/index.html @@ -1,49 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <title> Examples | Nissy </title> -<meta name="viewport" content="width=device-width" /> <link rel="stylesheet" type="text/css" href="/style-3.css"> - <link rel="icon" href="/favicon.png"> - <meta charset="utf-8"> -</head> - -<body> - -<nav class="top"> - <table class="menu"> - <tr> - <td class="logo"> <a href="/"><img src="/favicon.png" alt="logo"></a> </td> - <td class="links"> - <a href="/download/">Download</a> | - <a href="/examples/">Examples</a> - </td> - </tr> - </table> -</nav> - -<hr class="line"> - - -<h1>Examples</h1> - -(Coming soon...) - -<hr class="line"> - -<nav class="bottom"> - <table class="footer"> - <tr> - <td class="contact"> - <a href="https://sebastiano.tronto.net">sebastiano.tronto.net</a> - </td> - <td class="hosted"> - <a href="mailto:sebastiano@tronto.net"> - sebastiano@tronto.net - </a> - </td> - </tr> - </table> -</nav> - -</body> -</html> diff --git a/www/favicon.png b/www/favicon.png Binary files differ. diff --git a/www/index.html b/www/index.html @@ -1,93 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <title> Nissy | Nissy </title> -<meta name="viewport" content="width=device-width" /> <link rel="stylesheet" type="text/css" href="/style-3.css"> - <link rel="icon" href="/favicon.png"> - <meta charset="utf-8"> -</head> - -<body> - -<nav class="top"> - <table class="menu"> - <tr> - <td class="logo"> <a href="/"><img src="/favicon.png" alt="logo"></a> </td> - <td class="links"> - <a href="/download/">Download</a> | - <a href="/examples/">Examples</a> - </td> - </tr> - </table> -</nav> - -<hr class="line"> - - -<h1>Nissy</h1> - -<p class=subtitle>A Rubik's cube solver and FMC assistant</p> - -<img src="/screenshot.png" alt="A screenshot of nissy running in a terminal emulator"> - -<p> -Nissy is a command-line Rubik's cube solver. It can find optimal solutions -for random positions using techniques from Herbert Kociemba's -<a href="http://kociemba.org/cube.htm">Cube Explorer</a> and -Tomas Rokicki's -<a href="https://github.com/rokici/cube20src/blob/master/nxopt.md">nxopt</a>. -With 4 cores at 2.5GHz and using about 3Gb of RAM, Nissy can find an optimal -solution in about a minute on average. -</p> - -<p> -Nissy aims at being a complete tool -for FMC (Fewest Moves Challenge) practice. It can solve different steps -of Thistlethwaite's algorithm (also know as DR/HTR) and cans use NISS -(Normal-Inverse Scramble Switch). -</p> - -<p> -You should use Nissy if: -</p> -<ul> -<li> -You want to analyze your DR solutions or check for multiple optimal -(or sub-optimal) solutions for EO/DR/HTR or similar steps. -</li> -<li> -You just want a Rubik's cube solver and you like command line interfaces. -</li> -<li> -You want an alternative to Cube Explorer. -</li> -</ul> - -<p> -To get started, head to the <a href="/download/">download</a> page. -</p> - -<p> -You can also see its source code by cloning the git repository -<a href="https://git.tronto.net/nissy/">git.tronto.net/nissy</a> -</p> - -<hr class="line"> - -<nav class="bottom"> - <table class="footer"> - <tr> - <td class="contact"> - <a href="https://sebastiano.tronto.net">sebastiano.tronto.net</a> - </td> - <td class="hosted"> - <a href="mailto:sebastiano@tronto.net"> - sebastiano@tronto.net - </a> - </td> - </tr> - </table> -</nav> - -</body> -</html> diff --git a/www/screenshot.png b/www/screenshot.png Binary files differ. diff --git a/www/style-3.css b/www/style-3.css @@ -1,105 +0,0 @@ -.menu { - width: 100%; -} - -.links { - text-align: right; - font-weight: bold; -} - -.logo img { - width: 50px; - height: 50px; -} - -.footer { - font-style: italic; - width: 100%; -} - -.hosted { - text-align: right; -} - -body { - margin: auto 8px 20px 8px; -} - -img { - display: block; - margin-left: auto; - margin-right: auto; - max-width: 100%; -} - -code { - background-color: #eeeeee; - padding: 2px; -} - -pre code { - padding: 0px; -} - -pre { - background-color: #eeeeee; - border: 2px solid; - padding: 6px; -} - -#blob { - background: #ffffff; - border: none; -} - -a { - color: #0f2899; - text-decoration: none; -} - -.links a { - font-weight: bold; - color: black; -} - -.hosted a, -.contact a { - font-weight: bold; -} - -a:hover { - text-decoration: underline; -} - -html { - margin: 1em auto; - max-width: 42em; -} - -h1 { - text-align: center; -} - -td h1 { - text-align: left; - font-size: 1.5em; -} - -table { - width: 100%; -} - -.url td { - font-family: monospace; -} - -.dltable th, .dltable td { - border: 1px solid black; - padding: 3px; -} - -.subtitle { - text-align: center; - font-weight: bold; - font-size: 1.1em; -}