├── requirements.txt ├── src ├── python.h ├── env.h ├── commands.h ├── symcoord.h ├── pf.h ├── steps.h ├── solve.h ├── pruning.h ├── coord.h ├── trans.h ├── moves.h ├── alg.h ├── pf.c ├── env.c ├── cube.h ├── utils.h ├── test.c ├── utils.c ├── cubetypes.h ├── trans.c ├── pruning.c ├── solve.c ├── coord.c ├── moves.c ├── alg.c ├── symcoord.c ├── cube.c └── commands.c ├── app ├── nissy_c_bridge.py ├── shell.py ├── scramble.py ├── main.py ├── solve.py └── templates │ └── index.html ├── test.sh ├── .gitignore ├── valgrind.sh └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | waitress -------------------------------------------------------------------------------- /src/python.h: -------------------------------------------------------------------------------- 1 | bool find_xexc(char *rzps, int e, int c); -------------------------------------------------------------------------------- /app/nissy_c_bridge.py: -------------------------------------------------------------------------------- 1 | from ctypes import CDLL 2 | 3 | nissy = CDLL("./nissy.so") -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Compile the shared library for the python wrapper 4 | gcc -o test -fPIC /src/*.c -DVERSION=\"2.0.6\" 5 | 6 | ./test 7 | 8 | rm test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nissy 2 | nissy.dll 3 | nissy.so 4 | __pycache__/ 5 | app.db 6 | log.txt 7 | 8 | # Potentially sensitive info 9 | upload_tables.sh 10 | 11 | # files generated by pyinstaller 12 | build/ 13 | *.spec -------------------------------------------------------------------------------- /valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd app 4 | 5 | gcc -o test_binary -fPIC ../src/*.c -DVERSION=\"2.0.6\" 6 | 7 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./test_binary 8 | 9 | rm test_binary 10 | -------------------------------------------------------------------------------- /src/env.h: -------------------------------------------------------------------------------- 1 | #ifndef ENV_H 2 | #define ENV_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern char *tabledir; 12 | 13 | void init_env(void); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /app/shell.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from nissy_c_bridge import nissy 3 | 4 | def get_shell_result(command): 5 | ''' 6 | command: string 7 | ''' 8 | nissy.python_shell.argtypes = [ctypes.c_char_p] 9 | nissy.python_shell.restype = ctypes.c_char_p 10 | 11 | result = nissy.python_shell(command.encode()) 12 | return result.decode("utf-8") 13 | -------------------------------------------------------------------------------- /app/scramble.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from nissy_c_bridge import nissy 3 | 4 | def get_scramble(scramble_type): 5 | ''' 6 | scramble_type: string 7 | ''' 8 | nissy.python_scramble.argtypes = [ctypes.c_char_p] 9 | nissy.python_scramble.restype = ctypes.c_char_p 10 | 11 | result = nissy.python_scramble(scramble_type.encode()) 12 | return result.decode("utf-8") 13 | -------------------------------------------------------------------------------- /src/commands.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMANDS_H 2 | #define COMMANDS_H 3 | 4 | #include 5 | 6 | #include "solve.h" 7 | #include "steps.h" 8 | 9 | char* alg_to_string(Alg *alg); 10 | char** alglist_to_strings(AlgList *alglist); 11 | void free_args(CommandArgs *args); 12 | CommandArgs * new_args(void); 13 | 14 | extern Command * commands[]; 15 | 16 | #endif -------------------------------------------------------------------------------- /src/symcoord.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMCOORD_H 2 | #define SYMCOORD_H 3 | 4 | #include "coord.h" 5 | 6 | extern Coordinate coord_cp_sym16; 7 | extern Coordinate coord_eofbepos_sym16; 8 | extern Coordinate coord_drud_sym16; 9 | extern Coordinate coord_drudfin_noE_sym16; 10 | extern Coordinate coord_nxopt31; 11 | 12 | extern SymData *all_sd[]; 13 | 14 | void free_sd(SymData *sd); 15 | void init_symcoord(void); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/pf.h: -------------------------------------------------------------------------------- 1 | #ifndef PF_H 2 | #define PF_H 3 | 4 | #include "cubetypes.h" 5 | 6 | extern PieceFilter pf_all; 7 | extern PieceFilter pf_4val; 8 | extern PieceFilter pf_epcp; 9 | extern PieceFilter pf_cpos; 10 | extern PieceFilter pf_cp; 11 | extern PieceFilter pf_ep; 12 | extern PieceFilter pf_e; 13 | extern PieceFilter pf_s; 14 | extern PieceFilter pf_m; 15 | extern PieceFilter pf_eo; 16 | extern PieceFilter pf_co; 17 | extern PieceFilter pf_coud; 18 | extern PieceFilter pf_edges; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/steps.h: -------------------------------------------------------------------------------- 1 | #ifndef STEPS_H 2 | #define STEPS_H 3 | 4 | #include "pruning.h" 5 | 6 | extern Step * steps[]; 7 | 8 | /* Two steps used directly by two-phase solver */ 9 | extern Step drany_HTM; 10 | extern Step dranyfin_DR; 11 | 12 | void copy_estimatedata(EstimateData *s, EstimateData *d); 13 | void invert_estimatedata(EstimateData *ed); 14 | void reset_estimatedata(EstimateData *ed); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/solve.h: -------------------------------------------------------------------------------- 1 | #ifndef SOLVE_H 2 | #define SOLVE_H 3 | 4 | #include "moves.h" 5 | #include "steps.h" 6 | #include "trans.h" 7 | 8 | typedef struct SolveOutput { 9 | AlgList *sols; 10 | char *error_msg; 11 | } SolveOutput; 12 | 13 | SolveOutput * solve_output_new(AlgList *sols, char *error_msg); 14 | void solve_output_free(SolveOutput *so); 15 | SolveOutput * solve(struct timespec start, Cube cube, Step *step, SolveOptions *opts); 16 | Alg * solve_2phase(struct timespec start, Cube cube, int nthreads); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/pruning.h: -------------------------------------------------------------------------------- 1 | #ifndef PRUNING_H 2 | #define PRUNING_H 3 | 4 | #include "symcoord.h" 5 | 6 | extern PruneData pd_eofb_HTM; 7 | extern PruneData pd_coud_HTM; 8 | extern PruneData pd_corners_HTM; 9 | extern PruneData pd_cornershtr_HTM; 10 | extern PruneData pd_drud_sym16_HTM; 11 | extern PruneData pd_drud_eofb; 12 | extern PruneData pd_drudfin_noE_sym16_drud; 13 | extern PruneData pd_htr_drud; 14 | extern PruneData pd_cp_drud; 15 | extern PruneData pd_htrfin_htr; 16 | extern PruneData pd_nxopt31_HTM; 17 | 18 | extern PruneData * all_pd[]; 19 | 20 | void free_pd(PruneData *pd); 21 | void genptable(PruneData *pd, int nthreads); 22 | void print_ptable(PruneData *pd); 23 | uint64_t ptablesize(PruneData *pd); 24 | int ptableval(PruneData *pd, Cube cube); 25 | 26 | #endif 27 | 28 | -------------------------------------------------------------------------------- /src/coord.h: -------------------------------------------------------------------------------- 1 | #ifndef COORD_H 2 | #define COORD_H 3 | 4 | #include "trans.h" 5 | 6 | extern Coordinate coord_eofb; 7 | extern Coordinate coord_eofbepos; 8 | extern Coordinate coord_coud; 9 | extern Coordinate coord_cp; 10 | extern Coordinate coord_cphtr; 11 | extern Coordinate coord_corners; 12 | extern Coordinate coord_cornershtr; 13 | extern Coordinate coord_cornershtrfin; 14 | extern Coordinate coord_epud; 15 | extern Coordinate coord_drud; 16 | extern Coordinate coord_drud_eofb; 17 | extern Coordinate coord_htr_drud; 18 | extern Coordinate coord_htrfin; 19 | extern Coordinate coord_cpud_separate; 20 | 21 | extern int cpud_separate_ant[BINOM8ON4]; 22 | extern int cpud_separate_ind[FACTORIAL8]; 23 | extern int cornershtrfin_ant[24*24/6]; 24 | 25 | void init_coord(void); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/trans.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANS_H 2 | #define TRANS_H 3 | 4 | #include "moves.h" 5 | 6 | /* 7 | * Tables are exposed to allow faster partial transformations in some 8 | * specific cases (in symcoord) 9 | */ 10 | extern int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; 11 | extern int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; 12 | extern int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; 13 | extern int eo_ttable[NTRANS][POW2TO11]; 14 | extern int cp_ttable[NTRANS][FACTORIAL8]; 15 | extern int co_ttable[NTRANS][POW3TO7]; 16 | extern int cpos_ttable[NTRANS][FACTORIAL6]; 17 | extern Move moves_ttable[NTRANS][NMOVES]; 18 | 19 | Cube apply_trans(Trans t, Cube cube); 20 | Trans inverse_trans(Trans t); 21 | Alg * rotation_alg(Trans i); 22 | void transform_alg(Trans i, Alg *alg); 23 | 24 | void init_trans(void); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is Web Nissy? 2 | Web Nissy is just like Nissy, except it runs in a website. 3 | 4 | # Why use Web Nissy? 5 | - Don't have to generate or download massive tables 6 | - Easier to use 7 | - Chain together multiple steps 8 | - RZP and JZP 9 | 10 | # What is Nissy? 11 | Nissy is a tool for the Fewest Moves Challenge (FMC). It helps you find good EOs, DRs, and HTRs while using niss. 12 | 13 | NISS stands for Normal Inverse Scramble Switch, and it is a very common technique for FMC. This tool is essentially a Rubik's Cube solver that can get you to a given state using niss. 14 | 15 | Sebastiano Tronto created the original nissy that this website is based on. You can learn more here. 16 | 17 | # Who should use Web Nissy? 18 | Web Nissy is helpful for FMC solvers who want to improve their skills and verify whether their solution is efficient. 19 | 20 | For example, you can put in a scramble and see if there are any good EOs that you missed. Or you can put in a scramble and a partial solution, then see what the best continuations are. 21 | -------------------------------------------------------------------------------- /src/moves.h: -------------------------------------------------------------------------------- 1 | #ifndef MOVES_H 2 | #define MOVES_H 3 | 4 | #include "alg.h" 5 | #include "cube.h" 6 | #include "env.h" 7 | 8 | /* 9 | * Tables are exposed to allow for faster moves in some cases. 10 | */ 11 | extern int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; 12 | extern int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; 13 | extern int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; 14 | extern int eofb_mtable[NMOVES][POW2TO11]; 15 | extern int eorl_mtable[NMOVES][POW2TO11]; 16 | extern int eoud_mtable[NMOVES][POW2TO11]; 17 | extern int cp_mtable[NMOVES][FACTORIAL8]; 18 | extern int coud_mtable[NMOVES][POW3TO7]; 19 | extern int cofb_mtable[NMOVES][POW3TO7]; 20 | extern int corl_mtable[NMOVES][POW3TO7]; 21 | extern int cpos_mtable[NMOVES][FACTORIAL6]; 22 | 23 | Cube apply_alg(Alg *alg, Cube cube); 24 | Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); 25 | Cube apply_move(Move m, Cube cube); 26 | Alg * cleanup(Alg *alg); 27 | 28 | void init_moves(void); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/alg.h: -------------------------------------------------------------------------------- 1 | #ifndef ALG_H 2 | #define ALG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cubetypes.h" 9 | #include "utils.h" 10 | 11 | extern Moveset moveset_HTM; 12 | extern Moveset moveset_URF; 13 | extern Moveset moveset_eofb; 14 | extern Moveset moveset_drud; 15 | extern Moveset moveset_htr; 16 | 17 | void append_alg(AlgList *l, Alg *alg); 18 | void append_move(Alg *alg, Move m, bool inverse); 19 | Move base_move(Move m); 20 | void compose_alg(Alg *alg1, Alg *alg2); 21 | bool commute(Move m1, Move m2); 22 | void copy_alg(Alg *src, Alg *dst); 23 | void free_alg(Alg *alg); 24 | void free_alglist(AlgList *l); 25 | void inplace(Alg * (*f)(Alg *), Alg *alg); 26 | Alg * inverse_alg(Alg *alg); 27 | Move inverse_move(Move m); 28 | char * move_string(Move m); 29 | void movelist_to_position(Move *ml, int *pos); 30 | void moveset_to_list(Moveset ms, Move *lst); 31 | Alg * new_alg(char *str); 32 | AlgList * new_alglist(void); 33 | void print_alg(Alg *alg, bool l); 34 | void print_alglist(AlgList *al, bool l); 35 | void sort_alglist(AlgList *al); 36 | void swapmove(Move *m1, Move *m2); 37 | Alg * unniss(Alg *alg); 38 | 39 | void init_moveset(Moveset *ms); 40 | void init_all_movesets(void); 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /src/pf.c: -------------------------------------------------------------------------------- 1 | #include "pf.h" 2 | 3 | PieceFilter 4 | pf_all = { 5 | .epose = true, 6 | .eposs = true, 7 | .eposm = true, 8 | .eofb = true, 9 | .eorl = true, 10 | .eoud = true, 11 | .cp = true, 12 | .cofb = true, 13 | .corl = true, 14 | .coud = true, 15 | .cpos = true 16 | }; 17 | 18 | PieceFilter 19 | pf_4val = { 20 | .epose = true, 21 | .eposs = true, 22 | .eposm = true, 23 | .eofb = true, 24 | .coud = true, 25 | .cp = true 26 | }; 27 | 28 | PieceFilter 29 | pf_epcp = { 30 | .epose = true, 31 | .eposs = true, 32 | .eposm = true, 33 | .cp = true 34 | }; 35 | 36 | PieceFilter 37 | pf_cpos = { 38 | .cpos = true 39 | }; 40 | 41 | PieceFilter 42 | pf_cp = { 43 | .cp = true 44 | }; 45 | 46 | PieceFilter 47 | pf_ep = { 48 | .epose = true, 49 | .eposs = true, 50 | .eposm = true 51 | }; 52 | 53 | PieceFilter 54 | pf_e = { 55 | .epose = true 56 | }; 57 | 58 | PieceFilter 59 | pf_s = { 60 | .eposs = true 61 | }; 62 | 63 | PieceFilter 64 | pf_m = { 65 | .eposm = true 66 | }; 67 | 68 | PieceFilter 69 | pf_eo = { 70 | .eofb = true, 71 | .eorl = true, 72 | .eoud = true 73 | }; 74 | 75 | PieceFilter 76 | pf_co = { 77 | .cofb = true, 78 | .corl = true, 79 | .coud = true 80 | }; 81 | 82 | PieceFilter 83 | pf_coud = { 84 | .coud = true 85 | }; 86 | 87 | PieceFilter 88 | pf_edges = { 89 | .epose = true, 90 | .eposs = true, 91 | .eposm = true, 92 | .eofb = true, 93 | .eorl = true, 94 | .eoud = true 95 | }; 96 | -------------------------------------------------------------------------------- /src/env.c: -------------------------------------------------------------------------------- 1 | #include "env.h" 2 | 3 | bool initialized_env = false; 4 | char *tabledir; 5 | 6 | void 7 | mymkdir(char *d, int m) 8 | { 9 | #ifdef _WIN32 10 | mkdir(d); 11 | #else 12 | mkdir(d, m); 13 | #endif 14 | } 15 | 16 | void 17 | init_env(void) 18 | { 19 | char *nissydata = getenv("NISSYDATA"); 20 | char *localdata = getenv("XDG_DATA_HOME"); 21 | char *home = getenv("HOME"); 22 | bool read, write; 23 | 24 | if (initialized_env) 25 | return; 26 | 27 | if (nissydata != NULL) { 28 | tabledir = malloc((strlen(nissydata) + 20) * sizeof(char)); 29 | strcpy(tabledir, nissydata); 30 | } else if (localdata != NULL) { 31 | tabledir = malloc((strlen(localdata) + 20) * sizeof(char)); 32 | strcpy(tabledir, localdata); 33 | strcat(tabledir, "/nissy"); 34 | } else if (home != NULL) { 35 | tabledir = malloc((strlen(home) + 20) * sizeof(char)); 36 | strcpy(tabledir, home); 37 | strcat(tabledir, "/.nissy"); 38 | } else { 39 | tabledir = malloc(20 * sizeof(char)); 40 | strcpy(tabledir, "."); 41 | } 42 | 43 | mymkdir(tabledir, 0777); 44 | strcat(tabledir, "/tables"); 45 | mymkdir(tabledir, 0777); 46 | 47 | read = !access(tabledir, R_OK); 48 | write = !access(tabledir, W_OK); 49 | 50 | if (!read) { 51 | fprintf(stderr, "Table files cannot be read.\n"); 52 | } else if (!write) { 53 | fprintf(stderr, "Data directory not writable: "); 54 | fprintf(stderr, "tables can be loaded, but not saved.\n"); 55 | } 56 | 57 | initialized_env = true; 58 | } 59 | -------------------------------------------------------------------------------- /src/cube.h: -------------------------------------------------------------------------------- 1 | #ifndef CUBE_H 2 | #define CUBE_H 3 | 4 | #include 5 | 6 | #include "env.h" 7 | #include "pf.h" 8 | #include "utils.h" 9 | 10 | Cube admissible_ep(Cube cube, PieceFilter f); 11 | int array_ep_to_epos(int *ep, int *eps_solved); 12 | Cube arrays_to_cube(CubeArray *arr, PieceFilter f); 13 | Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ 14 | Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); 15 | void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); 16 | int edge_slice(Edge e); /* E=0, S=1, M=2 */ 17 | bool equal(Cube c1, Cube c2); 18 | Cube inverse_cube(Cube cube); 19 | bool is_admissible(Cube cube); 20 | bool is_solved(Cube cube); 21 | void epos_to_partial_ep(int epos, int *ep, int *ss); 22 | void epos_to_compatible_ep(int epos, int *ep, int *ss); 23 | void fix_eorleoud(CubeArray *arr); 24 | void fix_cofbcorl(CubeArray *arr); 25 | Cube fourval_to_cube(int eofb, int ep, int coud, int cp); 26 | void free_cubearray(CubeArray *arr, PieceFilter f); 27 | Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); 28 | CubeArray * new_cubearray(Cube cube, PieceFilter f); 29 | Center what_center_at(Cube cube, Center c); 30 | Corner what_corner_at(Cube cube, Corner c); 31 | Edge what_edge_at(Cube cube, Edge e); 32 | Edge where_is_edge(Cube cube, Edge e); 33 | 34 | void init_cube(void); 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /app/main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify, render_template, request 2 | from scramble import get_scramble 3 | from shell import get_shell_result 4 | from solve import solve 5 | import sqlite3 6 | 7 | class DB: 8 | def __init__(self): 9 | with sqlite3.connect('app.db') as conn: 10 | c = conn.cursor() 11 | c.execute('''CREATE TABLE IF NOT EXISTS logs ( 12 | id INTEGER PRIMARY KEY AUTOINCREMENT, 13 | timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, 14 | data TEXT)''') 15 | 16 | def log(self, data): 17 | '''data should be str or dict''' 18 | data = str(data) 19 | with sqlite3.connect('app.db') as conn: 20 | c = conn.cursor() 21 | c.execute('INSERT INTO logs (data) VALUES (?)', (data,)) 22 | 23 | app = Flask(__name__) 24 | db = DB() 25 | 26 | @app.route('/', methods=['GET']) 27 | def index(): 28 | return render_template('index.html') 29 | 30 | @app.route('/nissy_methods', methods=['POST']) 31 | def nissy_methods(): 32 | body = request.get_json() 33 | db.log(body) 34 | 35 | if body['method'] == 'scramble': 36 | scramble = get_scramble(body['scrambleType']) 37 | return { 'scramble': scramble } 38 | elif body['method'] == 'shell': 39 | result = get_shell_result(body['command']) 40 | return { 'result': result } 41 | elif body['method'] == 'solve': 42 | return solve(body) 43 | 44 | @app.errorhandler(Exception) 45 | def handle_generic_error(e): 46 | response = jsonify({"error": "Internal server error."}) 47 | response.status_code = 500 48 | return response 49 | 50 | if __name__ == '__main__': 51 | app.run(debug=False, host='0.0.0.0', port=80) 52 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "time.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define TIME_LIMIT 1000 // 1 second 11 | #define TIMEOUT_MSG "Timed out after 1 second. Try using fewer moves or fewer solutions." 12 | #define POW2TO6 64ULL 13 | #define POW2TO11 2048ULL 14 | #define POW2TO12 4096ULL 15 | #define POW3TO7 2187ULL 16 | #define POW3TO8 6561ULL 17 | #define FACTORIAL4 24ULL 18 | #define FACTORIAL6 720ULL 19 | #define FACTORIAL7 5040ULL 20 | #define FACTORIAL8 40320ULL 21 | #define FACTORIAL12 479001600ULL 22 | #define BINOM12ON4 495ULL 23 | #define BINOM8ON4 70ULL 24 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) 25 | #define MAX(a,b) (((a) > (b)) ? (a) : (b)) 26 | 27 | int elapsed_ms(struct timespec start); 28 | void apply_permutation(int *perm, int *set, int n); 29 | int binomial(int n, int k); 30 | int digit_array_to_int(int *a, int n, int b); 31 | int factorial(int n); 32 | void index_to_perm(int p, int n, int *r); 33 | void index_to_subset(int s, int n, int k, int *r); 34 | void int_to_digit_array(int a, int b, int n, int *r); 35 | void int_to_sum_zero_array(int x, int b, int n, int *a); 36 | int invert_digits(int a, int b, int n); 37 | bool is_perm(int *a, int n); 38 | bool is_subset(int *a, int n, int k); 39 | int perm_sign(int *a, int n); 40 | int perm_to_index(int *a, int n); 41 | int powint(int a, int b); 42 | int subset_to_index(int *a, int n, int k); 43 | void sum_arrays_mod(int *src, int *dst, int n, int m); 44 | void swap(int *a, int *b); 45 | void swapu64(uint64_t *a, uint64_t *b); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "python.h" 5 | 6 | int main(int argc, char **argv) { 7 | int num_correct = 0; 8 | int num_tests = 0; 9 | 10 | // Single group 11 | num_tests++; 12 | if (find_xexc("4e4c", 4, 4)) num_correct++; 13 | 14 | num_tests++; 15 | if (!find_xexc("4e4c", 3, 3)) num_correct++; 16 | 17 | // No separator 18 | num_tests++; 19 | if (find_xexc("4e4c2e3c2e4c", 4, 4)) num_correct++; 20 | 21 | num_tests++; 22 | if (find_xexc("4e4c2e3c2e4c", 2, 3)) num_correct++; 23 | 24 | num_tests++; 25 | if (find_xexc("4e4c2e3c2e4c", 2, 4)) num_correct++; 26 | 27 | num_tests++; 28 | if (!find_xexc("4e4c2e3c2e4c", 3, 3)) num_correct++; 29 | 30 | // Comma separator 31 | num_tests++; 32 | if (find_xexc("4e4c,2e3c,2e4c", 4, 4)) num_correct++; 33 | 34 | num_tests++; 35 | if (find_xexc("4e4c,2e3c,2e4c", 2, 3)) num_correct++; 36 | 37 | num_tests++; 38 | if (find_xexc("4e4c,2e3c,2e4c", 2, 4)) num_correct++; 39 | 40 | num_tests++; 41 | if (!find_xexc("4e4c,2e3c,2e4c", 3, 3)) num_correct++; 42 | 43 | // Space separator 44 | num_tests++; 45 | if (find_xexc("4e4c 2e3c 2e4c", 4, 4)) num_correct++; 46 | 47 | num_tests++; 48 | if (find_xexc("4e4c 2e3c 2e4c", 2, 3)) num_correct++; 49 | 50 | num_tests++; 51 | if (find_xexc("4e4c 2e3c 2e4c", 2, 4)) num_correct++; 52 | 53 | num_tests++; 54 | if (!find_xexc("4e4c 2e3c 2e4c", 3, 3)) num_correct++; 55 | 56 | // Command and space separator 57 | num_tests++; 58 | if (find_xexc("4e4c, 2e3c, 2e4c", 4, 4)) num_correct++; 59 | 60 | num_tests++; 61 | if (find_xexc("4e4c, 2e3c, 2e4c", 2, 3)) num_correct++; 62 | 63 | num_tests++; 64 | if (find_xexc("4e4c, 2e3c, 2e4c", 2, 4)) num_correct++; 65 | 66 | num_tests++; 67 | if (!find_xexc("4e4c, 2e3c, 2e4c", 3, 3)) num_correct++; 68 | 69 | printf("Tests passed: %d/%d\n", num_correct, num_tests); 70 | } 71 | -------------------------------------------------------------------------------- /app/solve.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | from nissy_c_bridge import nissy 3 | 4 | class StepData(ctypes.Structure): 5 | _fields_ = [ 6 | ("key", ctypes.c_char_p), 7 | ("value", ctypes.c_char_p) 8 | ] 9 | 10 | class SolveStep(ctypes.Structure): 11 | _fields_ = [ 12 | ("name", ctypes.c_char_p), 13 | ("shortname", ctypes.c_char_p), 14 | ("data", ctypes.POINTER(StepData)), 15 | ("datalen", ctypes.c_int), 16 | ] 17 | 18 | class SolveArgs(ctypes.Structure): 19 | _fields_ = [ 20 | ("steps", ctypes.POINTER(SolveStep)), 21 | ("num_steps", ctypes.c_int), 22 | ("scramble", ctypes.c_char_p), 23 | ("nisstype", ctypes.c_int), 24 | ] 25 | 26 | def decode_strings(result): 27 | solutions = [] 28 | index = 0 29 | while result[index] != None: 30 | solutions.append(result[index].decode("utf-8")) 31 | index += 1 32 | return solutions 33 | 34 | def solve(json): 35 | 36 | nissy.python_solve.argtypes = [SolveArgs] 37 | nissy.python_solve.restype = ctypes.POINTER(ctypes.c_char_p) 38 | 39 | def encode_step(step): 40 | step_data_list = [ 41 | StepData( 42 | key=stepData['key'].encode(), 43 | value=stepData['value'].encode(), 44 | ) for stepData in step['data'] 45 | ] 46 | 47 | data = (StepData * len(step_data_list))(*step_data_list) 48 | 49 | solve_step = SolveStep( 50 | name=step['name'].encode(), 51 | shortname=step['shortname'].encode(), 52 | data=data, 53 | datalen=len(data), 54 | ) 55 | 56 | return solve_step 57 | 58 | step_list = [encode_step(step) for step in json['data']] 59 | step_list = (SolveStep * len(step_list))(*step_list) 60 | 61 | scramble = json['scramble'] 62 | nisstype = int(json['nisstype']) if json['nisstype'] else 0 63 | 64 | solve_args = SolveArgs( 65 | steps=step_list, 66 | num_steps=len(step_list), 67 | scramble=scramble.encode(), 68 | nisstype=nisstype, 69 | ) 70 | 71 | result = nissy.python_solve(solve_args) 72 | 73 | solutions = decode_strings(result) 74 | 75 | return solutions 76 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | int elapsed_ms(struct timespec start) { 4 | struct timespec end; 5 | clock_gettime(CLOCK_MONOTONIC, &end); 6 | return (end.tv_sec - start.tv_sec) * 1000 + (end.tv_nsec - start.tv_nsec) / 1000000; 7 | } 8 | 9 | void 10 | apply_permutation(int *perm, int *set, int n) 11 | { 12 | int i; 13 | int aux[n]; 14 | 15 | if (!is_perm(perm, n)) 16 | return; 17 | 18 | for (i = 0; i < n; i++) 19 | aux[i] = set[perm[i]]; 20 | 21 | memcpy(set, aux, n * sizeof(int)); 22 | } 23 | 24 | int 25 | binomial(int n, int k) 26 | { 27 | if (n < 0 || k < 0 || k > n) 28 | return 0; 29 | 30 | return factorial(n) / (factorial(k) * factorial(n-k)); 31 | } 32 | 33 | int 34 | digit_array_to_int(int *a, int n, int b) 35 | { 36 | int i, ret = 0, p = 1; 37 | 38 | for (i = 0; i < n; i++, p *= b) 39 | ret += a[i] * p; 40 | 41 | return ret; 42 | } 43 | 44 | int 45 | factorial(int n) 46 | { 47 | int i, ret = 1; 48 | 49 | if (n < 0) 50 | return 0; 51 | 52 | for (i = 1; i <= n; i++) 53 | ret *= i; 54 | 55 | return ret; 56 | } 57 | 58 | void 59 | index_to_perm(int p, int n, int *r) 60 | { 61 | int i, j, c; 62 | int a[n]; 63 | 64 | for (i = 0; i < n; i++) 65 | a[i] = 0; 66 | 67 | if (p < 0 || p >= factorial(n)) 68 | for (i = 0; i < n; i++) 69 | r[i] = -1; 70 | 71 | for (i = 0; i < n; i++) { 72 | c = 0; 73 | j = 0; 74 | while (c <= p / factorial(n-i-1)) 75 | c += a[j++] ? 0 : 1; 76 | r[i] = j-1; 77 | a[j-1] = 1; 78 | p %= factorial(n-i-1); 79 | } 80 | } 81 | 82 | void 83 | index_to_subset(int s, int n, int k, int *r) 84 | { 85 | int i, j, v; 86 | 87 | if (s < 0 || s >= binomial(n, k)) { 88 | for (i = 0; i < n; i++) 89 | r[i] = -1; 90 | return; 91 | } 92 | 93 | for (i = 0; i < n; i++) { 94 | if (k == n-i) { 95 | for (j = i; j < n; j++) 96 | r[j] = 1; 97 | return; 98 | } 99 | 100 | if (k == 0) { 101 | for (j = i; j < n; j++) 102 | r[j] = 0; 103 | return; 104 | } 105 | 106 | v = binomial(n-i-1, k); 107 | if (s >= v) { 108 | r[i] = 1; 109 | k--; 110 | s -= v; 111 | } else { 112 | r[i] = 0; 113 | } 114 | } 115 | } 116 | 117 | void 118 | int_to_digit_array(int a, int b, int n, int *r) 119 | { 120 | int i; 121 | 122 | if (b <= 1) 123 | for (i = 0; i < n; i++) 124 | r[i] = 0; 125 | else 126 | for (i = 0; i < n; i++, a /= b) 127 | r[i] = a % b; 128 | } 129 | 130 | void 131 | int_to_sum_zero_array(int x, int b, int n, int *a) 132 | { 133 | int i, s = 0; 134 | 135 | if (b <= 1) { 136 | for (i = 0; i < n; i++) 137 | a[i] = 0; 138 | } else { 139 | int_to_digit_array(x, b, n-1, a); 140 | for (i = 0; i < n - 1; i++) 141 | s = (s + a[i]) % b; 142 | a[n-1] = (b - s) % b; 143 | } 144 | } 145 | 146 | int 147 | invert_digits(int a, int b, int n) 148 | { 149 | int i, ret; 150 | int r[n]; 151 | 152 | int_to_digit_array(a, b, n, r); 153 | for (i = 0; i < n; i++) 154 | r[i] = (b-r[i]) % b; 155 | 156 | ret = digit_array_to_int(r, n, b); 157 | return ret; 158 | } 159 | 160 | bool 161 | is_perm(int *a, int n) 162 | { 163 | int i; 164 | int aux[n]; 165 | 166 | for (i = 0; i < n; i++) 167 | aux[i] = 0; 168 | 169 | for (i = 0; i < n; i++) { 170 | if (a[i] < 0 || a[i] >= n) 171 | return false; 172 | else 173 | aux[a[i]] = 1; 174 | } 175 | 176 | for (i = 0; i < n; i++) 177 | if (!aux[i]) 178 | return false; 179 | 180 | return true; 181 | } 182 | 183 | bool 184 | is_subset(int *a, int n, int k) 185 | { 186 | int i, sum = 0; 187 | 188 | for (i = 0; i < n; i++) 189 | sum += a[i] ? 1 : 0; 190 | 191 | return sum == k; 192 | } 193 | 194 | int 195 | perm_sign(int *a, int n) 196 | { 197 | int i, j, ret = 0; 198 | 199 | if (!is_perm(a,n)) 200 | return -1; 201 | 202 | for (i = 0; i < n; i++) 203 | for (j = i+1; j < n; j++) 204 | ret += (a[i] > a[j]) ? 1 : 0; 205 | 206 | return ret % 2; 207 | } 208 | 209 | int 210 | perm_to_index(int *a, int n) 211 | { 212 | int i, j, c, ret = 0; 213 | 214 | if (!is_perm(a, n)) 215 | return -1; 216 | 217 | for (i = 0; i < n; i++) { 218 | c = 0; 219 | for (j = i+1; j < n; j++) 220 | c += (a[i] > a[j]) ? 1 : 0; 221 | ret += factorial(n-i-1) * c; 222 | } 223 | 224 | return ret; 225 | } 226 | 227 | int 228 | powint(int a, int b) 229 | { 230 | if (b < 0) 231 | return 0; 232 | if (b == 0) 233 | return 1; 234 | 235 | if (b % 2) 236 | return a * powint(a, b-1); 237 | else 238 | return powint(a*a, b/2); 239 | } 240 | 241 | int 242 | subset_to_index(int *a, int n, int k) 243 | { 244 | int i, ret = 0; 245 | 246 | if (!is_subset(a, n, k)) 247 | return binomial(n, k); 248 | 249 | for (i = 0; i < n; i++) { 250 | if (k == n-i) 251 | return ret; 252 | if (a[i]) { 253 | ret += binomial(n-i-1, k); 254 | k--; 255 | } 256 | } 257 | 258 | return ret; 259 | } 260 | 261 | void 262 | sum_arrays_mod(int *src, int *dst, int n, int m) 263 | { 264 | int i; 265 | 266 | for (i = 0; i < n; i++) 267 | dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; 268 | } 269 | 270 | void 271 | swap(int *a, int *b) 272 | { 273 | int aux; 274 | 275 | aux = *a; 276 | *a = *b; 277 | *b = aux; 278 | } 279 | 280 | void 281 | swapu64(uint64_t *a, uint64_t *b) 282 | { 283 | uint64_t aux; 284 | 285 | aux = *a; 286 | *a = *b; 287 | *b = aux; 288 | } 289 | 290 | -------------------------------------------------------------------------------- /src/cubetypes.h: -------------------------------------------------------------------------------- 1 | #ifndef CUBETYPES_H 2 | #define CUBETYPES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define NMOVES 55 /* Actually 54, but one is NULLMOVE */ 9 | #define NTRANS 48 10 | #define NROTATIONS 24 11 | #define entry_group_t uint8_t /* For pruning tables */ 12 | 13 | /* Enums *********************************************************************/ 14 | 15 | typedef enum 16 | center 17 | { 18 | U_center, D_center, 19 | R_center, L_center, 20 | F_center, B_center 21 | } Center; 22 | 23 | typedef enum 24 | corner 25 | { 26 | UFR, UFL, UBL, UBR, 27 | DFR, DFL, DBL, DBR 28 | } Corner; 29 | 30 | typedef enum 31 | edge 32 | { 33 | UF, UL, UB, UR, 34 | DF, DL, DB, DR, 35 | FR, FL, BL, BR 36 | } Edge; 37 | 38 | typedef enum 39 | move 40 | { 41 | NULLMOVE, 42 | U, U2, U3, D, D2, D3, 43 | R, R2, R3, L, L2, L3, 44 | F, F2, F3, B, B2, B3, 45 | Uw, Uw2, Uw3, Dw, Dw2, Dw3, 46 | Rw, Rw2, Rw3, Lw, Lw2, Lw3, 47 | Fw, Fw2, Fw3, Bw, Bw2, Bw3, 48 | M, M2, M3, 49 | S, S2, S3, 50 | E, E2, E3, 51 | x, x2, x3, 52 | y, y2, y3, 53 | z, z2, z3, 54 | } Move; 55 | 56 | typedef enum 57 | trans 58 | { 59 | uf, ur, ub, ul, 60 | df, dr, db, dl, 61 | rf, rd, rb, ru, 62 | lf, ld, lb, lu, 63 | fu, fr, fd, fl, 64 | bu, br, bd, bl, 65 | uf_mirror, ur_mirror, ub_mirror, ul_mirror, 66 | df_mirror, dr_mirror, db_mirror, dl_mirror, 67 | rf_mirror, rd_mirror, rb_mirror, ru_mirror, 68 | lf_mirror, ld_mirror, lb_mirror, lu_mirror, 69 | fu_mirror, fr_mirror, fd_mirror, fl_mirror, 70 | bu_mirror, br_mirror, bd_mirror, bl_mirror, 71 | } Trans; 72 | 73 | typedef enum 74 | niss_type 75 | { 76 | NORMAL, LINEAR, NISS 77 | } NissType; 78 | 79 | 80 | /* Typedefs ******************************************************************/ 81 | 82 | typedef struct alg Alg; 83 | typedef struct alglist AlgList; 84 | typedef struct alglistnode AlgListNode; 85 | typedef struct block Block; 86 | typedef struct command Command; 87 | typedef struct commandargs CommandArgs; 88 | typedef struct coordinate Coordinate; 89 | typedef struct cube Cube; 90 | typedef struct cubearray CubeArray; 91 | typedef struct dfsarg DfsArg; 92 | typedef struct estimatedata EstimateData; 93 | typedef struct moveset Moveset; 94 | typedef struct piecefilter PieceFilter; 95 | typedef struct prunedata PruneData; 96 | typedef struct solveoptions SolveOptions; 97 | typedef struct step Step; 98 | typedef struct symdata SymData; 99 | typedef struct threaddatasolve ThreadDataSolve; 100 | typedef struct threaddatagenpt ThreadDataGenpt; 101 | 102 | typedef Cube (*AntiIndexer) (uint64_t); 103 | typedef bool (*Checker) (Cube); 104 | typedef uint64_t (*CoordMover) (Move, uint64_t); 105 | typedef uint64_t (*CoordTransformer) (Trans, uint64_t); 106 | typedef int (*Estimator) (DfsArg *); 107 | typedef bool (*Validator) (Alg *); 108 | typedef char* (*Exec) (CommandArgs *); 109 | typedef uint64_t (*Indexer) (Cube); 110 | typedef CommandArgs * (*ArgParser) (int, char **); 111 | typedef int (*TransDetector) (Cube, Trans *); 112 | typedef int (*TransFinder) (uint64_t, Trans *); 113 | 114 | 115 | /* Structs *******************************************************************/ 116 | 117 | struct 118 | alg 119 | { 120 | Move * move; 121 | bool * inv; 122 | int len; 123 | int allocated; 124 | }; 125 | 126 | struct 127 | alglist 128 | { 129 | AlgListNode * first; 130 | AlgListNode * last; 131 | int len; 132 | }; 133 | 134 | struct 135 | alglistnode 136 | { 137 | Alg * alg; 138 | AlgListNode * next; 139 | }; 140 | 141 | struct 142 | block 143 | { 144 | bool edge[12]; 145 | bool corner[8]; 146 | bool center[6]; 147 | }; 148 | 149 | struct 150 | command 151 | { 152 | char * name; 153 | char * usage; 154 | char * description; 155 | ArgParser parse_args; 156 | Exec exec; 157 | }; 158 | 159 | struct 160 | commandargs 161 | { 162 | bool success; 163 | Alg * scramble; 164 | SolveOptions * opts; 165 | Step * step; 166 | Command * command; /* For help */ 167 | int n; 168 | char scrtype[20]; 169 | bool scrstdin; 170 | bool header; 171 | PruneData * pd; 172 | struct timespec start; 173 | }; 174 | 175 | struct 176 | coordinate 177 | { 178 | Indexer index; 179 | uint64_t max; 180 | CoordMover move; 181 | CoordTransformer transform; 182 | SymData * sd; 183 | TransFinder tfind; /* TODO: should be easy to remove */ 184 | Coordinate * base; /* TODO: part of refactor */ 185 | }; 186 | 187 | struct 188 | cube 189 | { 190 | int epose; 191 | int eposs; 192 | int eposm; 193 | int eofb; 194 | int eorl; 195 | int eoud; 196 | int cp; 197 | int coud; 198 | int cofb; 199 | int corl; 200 | int cpos; 201 | }; 202 | 203 | struct 204 | cubearray 205 | { 206 | int * ep; 207 | int * eofb; 208 | int * eorl; 209 | int * eoud; 210 | int * cp; 211 | int * coud; 212 | int * corl; 213 | int * cofb; 214 | int * cpos; 215 | }; 216 | 217 | struct 218 | dfsarg 219 | { 220 | Step * step; 221 | SolveOptions * opts; 222 | Trans t; 223 | Cube cube; 224 | Cube inverse; 225 | int d; 226 | uint64_t badmoves; 227 | uint64_t badmovesinv; 228 | bool niss; 229 | Move last1; 230 | Move last2; 231 | Move last1inv; 232 | Move last2inv; 233 | EstimateData * ed; 234 | AlgList * sols; 235 | pthread_mutex_t * sols_mutex; 236 | Alg * current_alg; 237 | }; 238 | 239 | struct 240 | estimatedata 241 | { 242 | int corners; 243 | int normal_ud; 244 | int normal_fb; 245 | int normal_rl; 246 | int inverse_ud; 247 | int inverse_fb; 248 | int inverse_rl; 249 | int oldret; 250 | }; 251 | 252 | struct 253 | moveset 254 | { 255 | bool (*allowed)(Move); 256 | bool (*allowed_next)(Move, Move, Move); 257 | Move sorted_moves[NMOVES+1]; 258 | uint64_t mask[NMOVES][NMOVES]; 259 | }; 260 | 261 | struct 262 | piecefilter 263 | { 264 | bool epose; 265 | bool eposs; 266 | bool eposm; 267 | bool eofb; 268 | bool eorl; 269 | bool eoud; 270 | bool cp; 271 | bool coud; 272 | bool cofb; 273 | bool corl; 274 | bool cpos; 275 | }; 276 | 277 | struct 278 | prunedata 279 | { 280 | char * filename; 281 | entry_group_t * ptable; 282 | bool generated; 283 | uint64_t n; 284 | Coordinate * coord; 285 | Moveset * moveset; 286 | bool compact; 287 | int base; 288 | uint64_t count[16]; 289 | PruneData * fallback; 290 | uint64_t fbmod; 291 | }; 292 | 293 | struct 294 | solveoptions 295 | { 296 | int min_moves; 297 | int max_moves; 298 | int max_solutions; 299 | int nthreads; 300 | int optimal; 301 | NissType nisstype; 302 | bool verbose; 303 | bool all; 304 | bool print_number; 305 | bool count_only; 306 | char * rzps; 307 | bool jzp; 308 | }; 309 | 310 | struct 311 | step 312 | { 313 | char * shortname; 314 | char * name; 315 | bool final; 316 | Checker is_done; 317 | Estimator estimate; 318 | Checker ready; 319 | char * ready_msg; 320 | Validator is_valid; 321 | Moveset * moveset; 322 | Trans pre_trans; 323 | TransDetector detect; 324 | int ntables; 325 | PruneData * tables[10]; 326 | }; 327 | 328 | struct 329 | symdata 330 | { 331 | char * filename; 332 | bool generated; 333 | Coordinate * coord; 334 | Coordinate * sym_coord; 335 | int ntrans; 336 | Trans * trans; 337 | uint64_t * class; 338 | uint64_t * unsym; 339 | Trans * transtorep; 340 | uint64_t * selfsim; 341 | CoordTransformer transform; /* TODO: remove, use that of base coord */ 342 | }; 343 | 344 | struct 345 | threaddatasolve 346 | { 347 | int thid; 348 | Trans t; 349 | Cube cube; 350 | Step * step; 351 | int depth; 352 | SolveOptions * opts; 353 | AlgList * start; 354 | AlgListNode ** node; 355 | AlgList * sols; 356 | pthread_mutex_t * start_mutex; 357 | pthread_mutex_t * sols_mutex; 358 | }; 359 | 360 | struct 361 | threaddatagenpt 362 | { 363 | int thid; 364 | int nthreads; 365 | PruneData * pd; 366 | int d; 367 | int nchunks; 368 | pthread_mutex_t ** mutex; 369 | pthread_mutex_t * upmutex; 370 | }; 371 | 372 | #endif 373 | -------------------------------------------------------------------------------- /src/trans.c: -------------------------------------------------------------------------------- 1 | #include "trans.h" 2 | 3 | /* Local functions ***********************************************************/ 4 | 5 | static bool read_ttables_file(void); 6 | static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); 7 | static bool write_ttables_file(void); 8 | 9 | /* Tables and other data *****************************************************/ 10 | 11 | static int ep_mirror[12] = { 12 | [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL, 13 | [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL, 14 | [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL 15 | }; 16 | 17 | static int cp_mirror[8] = { 18 | [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL, 19 | [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL 20 | }; 21 | 22 | static int cpos_mirror[6] = { 23 | [U_center] = U_center, [D_center] = D_center, 24 | [R_center] = L_center, [L_center] = R_center, 25 | [F_center] = F_center, [B_center] = B_center 26 | }; 27 | 28 | static char rotation_alg_string[100][NROTATIONS] = { 29 | [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3", 30 | [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2", 31 | [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3", 32 | [lf] = "z", [ld] = "z y3", [lb] = "z y2", [lu] = "z y", 33 | [fu] = "x y2", [fr] = "x y", [fd] = "x", [fl] = "x y3", 34 | [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3", 35 | }; 36 | 37 | static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ 38 | static int eposs_source[NTRANS]; 39 | static int eposm_source[NTRANS]; 40 | static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ 41 | static int eorl_source[NTRANS]; 42 | static int eoud_source[NTRANS]; 43 | static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ 44 | static int cofb_source[NTRANS]; 45 | static int corl_source[NTRANS]; 46 | 47 | int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; 48 | int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; 49 | int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; 50 | int eo_ttable[NTRANS][POW2TO11]; 51 | int cp_ttable[NTRANS][FACTORIAL8]; 52 | int co_ttable[NTRANS][POW3TO7]; 53 | int cpos_ttable[NTRANS][FACTORIAL6]; 54 | Move moves_ttable[NTRANS][NMOVES]; 55 | 56 | /* Local functions implementation ********************************************/ 57 | 58 | static bool 59 | read_ttables_file(void) 60 | { 61 | init_env(); 62 | 63 | FILE *f; 64 | char fname[strlen(tabledir)+20]; 65 | int b = sizeof(int); 66 | bool r = true; 67 | Move m; 68 | 69 | /* Table sizes, used for reading and writing files */ 70 | uint64_t me[11] = { 71 | [0] = FACTORIAL12/FACTORIAL8, 72 | [1] = FACTORIAL12/FACTORIAL8, 73 | [2] = FACTORIAL12/FACTORIAL8, 74 | [3] = POW2TO11, 75 | [4] = FACTORIAL8, 76 | [5] = POW3TO7, 77 | [6] = FACTORIAL6, 78 | [7] = NMOVES 79 | }; 80 | 81 | strcpy(fname, tabledir); 82 | strcat(fname, "/"); 83 | strcat(fname, "ttables"); 84 | 85 | if ((f = fopen(fname, "rb")) == NULL) 86 | return false; 87 | 88 | for (m = 0; m < NTRANS; m++) { 89 | r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; 90 | r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; 91 | r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; 92 | r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; 93 | r = r && fread(cp_ttable[m], b, me[4], f) == me[4]; 94 | r = r && fread(co_ttable[m], b, me[5], f) == me[5]; 95 | r = r && fread(cpos_ttable[m], b, me[6], f) == me[6]; 96 | r = r && fread(moves_ttable[m], b, me[7], f) == me[7]; 97 | } 98 | 99 | fclose(f); 100 | return r; 101 | } 102 | 103 | static Cube 104 | rotate_via_compose(Trans r, Cube c, PieceFilter f) 105 | { 106 | static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 107 | static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 108 | static CubeArray ma = { 109 | .ep = ep_mirror, 110 | .eofb = zero12, 111 | .eorl = zero12, 112 | .eoud = zero12, 113 | .cp = cp_mirror, 114 | .coud = zero8, 115 | .corl = zero8, 116 | .cofb = zero8, 117 | .cpos = cpos_mirror 118 | }; 119 | 120 | Alg *inv = inverse_alg(rotation_alg(r)); 121 | Cube ret = {0}; 122 | 123 | if (r >= NROTATIONS) 124 | ret = move_via_arrays(&ma, ret, f); 125 | ret = apply_alg_generic(inv, ret, f, true); 126 | 127 | ret = compose_filtered(c, ret, f); 128 | 129 | ret = apply_alg_generic(rotation_alg(r), ret, f, true); 130 | if (r >= NROTATIONS) 131 | ret = move_via_arrays(&ma, ret, f); 132 | 133 | free_alg(inv); 134 | return ret; 135 | } 136 | 137 | static bool 138 | write_ttables_file(void) 139 | { 140 | init_env(); 141 | 142 | FILE *f; 143 | char fname[strlen(tabledir)+20]; 144 | bool r = true; 145 | int b = sizeof(int); 146 | Move m; 147 | 148 | /* Table sizes, used for reading and writing files */ 149 | uint64_t me[11] = { 150 | [0] = FACTORIAL12/FACTORIAL8, 151 | [1] = FACTORIAL12/FACTORIAL8, 152 | [2] = FACTORIAL12/FACTORIAL8, 153 | [3] = POW2TO11, 154 | [4] = FACTORIAL8, 155 | [5] = POW3TO7, 156 | [6] = FACTORIAL6, 157 | [7] = NMOVES 158 | }; 159 | 160 | strcpy(fname, tabledir); 161 | strcat(fname, "/ttables"); 162 | 163 | if ((f = fopen(fname, "wb")) == NULL) 164 | return false; 165 | 166 | for (m = 0; m < NTRANS; m++) { 167 | r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; 168 | r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; 169 | r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; 170 | r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; 171 | r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4]; 172 | r = r && fwrite(co_ttable[m], b, me[5], f) == me[5]; 173 | r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6]; 174 | r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7]; 175 | } 176 | 177 | fclose(f); 178 | return r; 179 | } 180 | 181 | /* Public functions **********************************************************/ 182 | 183 | Cube 184 | apply_trans(Trans t, Cube cube) 185 | { 186 | /*init_trans();*/ 187 | 188 | int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; 189 | int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; 190 | int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; 191 | 192 | return (Cube) { 193 | .epose = epose_ttable[t][aux_epos[epose_source[t]]], 194 | .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], 195 | .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], 196 | .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], 197 | .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], 198 | .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], 199 | .coud = co_ttable[t][aux_co[coud_source[t]]], 200 | .corl = co_ttable[t][aux_co[corl_source[t]]], 201 | .cofb = co_ttable[t][aux_co[cofb_source[t]]], 202 | .cp = cp_ttable[t][cube.cp], 203 | .cpos = cpos_ttable[t][cube.cpos] 204 | }; 205 | } 206 | 207 | Trans 208 | inverse_trans(Trans t) 209 | { 210 | static Trans inverse_trans_aux[NTRANS] = { 211 | [uf] = uf, [ur] = ul, [ul] = ur, [ub] = ub, 212 | [df] = df, [dr] = dr, [dl] = dl, [db] = db, 213 | [rf] = lf, [rd] = bl, [rb] = rb, [ru] = fr, 214 | [lf] = rf, [ld] = br, [lb] = lb, [lu] = fl, 215 | [fu] = fu, [fr] = ru, [fd] = bu, [fl] = lu, 216 | [bu] = fd, [br] = ld, [bd] = bd, [bl] = rd, 217 | 218 | [uf_mirror] = uf_mirror, [ur_mirror] = ur_mirror, 219 | [ul_mirror] = ul_mirror, [ub_mirror] = ub_mirror, 220 | [df_mirror] = df_mirror, [dr_mirror] = dl_mirror, 221 | [dl_mirror] = dr_mirror, [db_mirror] = db_mirror, 222 | [rf_mirror] = rf_mirror, [rd_mirror] = br_mirror, 223 | [rb_mirror] = lb_mirror, [ru_mirror] = fl_mirror, 224 | [lf_mirror] = lf_mirror, [ld_mirror] = bl_mirror, 225 | [lb_mirror] = rb_mirror, [lu_mirror] = fr_mirror, 226 | [fu_mirror] = fu_mirror, [fr_mirror] = lu_mirror, 227 | [fd_mirror] = bu_mirror, [fl_mirror] = ru_mirror, 228 | [bu_mirror] = fd_mirror, [br_mirror] = rd_mirror, 229 | [bd_mirror] = bd_mirror, [bl_mirror] = ld_mirror 230 | }; 231 | 232 | return inverse_trans_aux[t]; 233 | } 234 | 235 | Alg * 236 | rotation_alg(Trans t) 237 | { 238 | int i; 239 | 240 | static Alg *rotation_alg_arr[NROTATIONS]; 241 | static bool initialized = false; 242 | 243 | if (!initialized) { 244 | for (i = 0; i < NROTATIONS; i++) 245 | rotation_alg_arr[i] = new_alg(rotation_alg_string[i]); 246 | 247 | initialized = true; 248 | } 249 | 250 | return rotation_alg_arr[t % NROTATIONS]; 251 | } 252 | 253 | void 254 | transform_alg(Trans t, Alg *alg) 255 | { 256 | int i; 257 | 258 | /*init_trans();*/ 259 | 260 | for (i = 0; i < alg->len; i++) 261 | alg->move[i] = moves_ttable[t][alg->move[i]]; 262 | } 263 | 264 | void 265 | init_trans(void) 266 | { 267 | static bool initialized = false; 268 | if (initialized) 269 | return; 270 | initialized = true; 271 | 272 | init_moves(); 273 | 274 | Cube aux, cube, c[3]; 275 | CubeArray epcp; 276 | int i, eparr[12], eoarr[12], cparr[8], coarr[8]; 277 | unsigned int ui; 278 | Move mi, move; 279 | Trans m; 280 | 281 | /* Compute sources */ 282 | for (i = 0; i < NTRANS; i++) { 283 | cube = apply_alg(rotation_alg(i), (Cube){0}); 284 | 285 | epose_source[i] = edge_slice(what_edge_at(cube, FR)); 286 | eposs_source[i] = edge_slice(what_edge_at(cube, UR)); 287 | eposm_source[i] = edge_slice(what_edge_at(cube, UF)); 288 | eofb_source[i] = what_center_at(cube, F_center)/2; 289 | eorl_source[i] = what_center_at(cube, R_center)/2; 290 | eoud_source[i] = what_center_at(cube, U_center)/2; 291 | coud_source[i] = what_center_at(cube, U_center)/2; 292 | cofb_source[i] = what_center_at(cube, F_center)/2; 293 | corl_source[i] = what_center_at(cube, R_center)/2; 294 | } 295 | 296 | if (read_ttables_file()) 297 | return; 298 | 299 | fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); 300 | 301 | /* Initialize tables */ 302 | for (m = 0; m < NTRANS; m++) { 303 | epcp = (CubeArray){ .ep = eparr, .cp = cparr }; 304 | cube = apply_alg(rotation_alg(m), (Cube){0}); 305 | cube_to_arrays(cube, &epcp, pf_epcp); 306 | if (m >= NROTATIONS) { 307 | apply_permutation(ep_mirror, eparr, 12); 308 | apply_permutation(cp_mirror, cparr, 8); 309 | } 310 | 311 | for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { 312 | c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); 313 | c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); 314 | c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); 315 | 316 | cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); 317 | epose_ttable[m][ui] = cube.epose; 318 | 319 | cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); 320 | eposs_ttable[m][ui] = cube.eposs; 321 | 322 | cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); 323 | eposm_ttable[m][ui] = cube.eposm; 324 | } 325 | for (ui = 0; ui < POW2TO11; ui++ ) { 326 | int_to_sum_zero_array(ui, 2, 12, eoarr); 327 | apply_permutation(eparr, eoarr, 12); 328 | eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); 329 | } 330 | for (ui = 0; ui < POW3TO7; ui++) { 331 | int_to_sum_zero_array(ui, 3, 8, coarr); 332 | apply_permutation(cparr, coarr, 8); 333 | co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); 334 | if (m >= NROTATIONS) 335 | co_ttable[m][ui] = 336 | invert_digits(co_ttable[m][ui], 3, 7); 337 | } 338 | for (ui = 0; ui < FACTORIAL8; ui++) { 339 | cube = (Cube){ .cp = ui }; 340 | cube = rotate_via_compose(m, cube, pf_cp); 341 | cp_ttable[m][ui] = cube.cp; 342 | } 343 | for (ui = 0; ui < FACTORIAL6; ui++) { 344 | cube = (Cube){ .cpos = ui }; 345 | cube = rotate_via_compose(m, cube, pf_cpos); 346 | cpos_ttable[m][ui] = cube.cpos; 347 | } 348 | for (mi = 0; mi < NMOVES; mi++) { 349 | /* Old version: 350 | * 351 | aux = apply_trans(m, apply_move(mi, (Cube){0})); 352 | for (move = 0; move < NMOVES; move++) { 353 | cube = apply_move(inverse_move(move), aux); 354 | mirr = apply_trans(uf_mirror, cube); 355 | if (is_solved(cube) || is_solved(mirr)) 356 | moves_ttable[m][mi] = move; 357 | } 358 | */ 359 | 360 | aux = apply_trans(m, apply_move(mi, (Cube){0})); 361 | for (move = 0; move < NMOVES; move++) { 362 | cube = apply_move(inverse_move(move), aux); 363 | if (is_solved(cube)) { 364 | moves_ttable[m][mi] = move; 365 | break; 366 | } 367 | } 368 | } 369 | } 370 | 371 | if (!write_ttables_file()) 372 | fprintf(stderr, "Error writing ttables\n"); 373 | } 374 | 375 | -------------------------------------------------------------------------------- /src/pruning.c: -------------------------------------------------------------------------------- 1 | #include "pruning.h" 2 | 3 | #define ENTRIES_PER_GROUP (2*sizeof(entry_group_t)) 4 | #define ENTRIES_PER_GROUP_COMPACT (4*sizeof(entry_group_t)) 5 | 6 | static int findchunk(PruneData *pd, int nchunks, uint64_t i); 7 | static void genptable_bfs(PruneData *pd, int d, int nt, int nc); 8 | static void genptable_compress(PruneData *pd); 9 | static void genptable_fixnasty(PruneData *pd, int d, int nthreads); 10 | static void genptable_setbase(PruneData *pd); 11 | static void * instance_bfs(void *arg); 12 | static void * instance_fixnasty(void *arg); 13 | static void ptable_update(PruneData *pd, Cube cube, int m); 14 | static void ptable_update_index(PruneData *pd, uint64_t ind, int m); 15 | static int ptableval_index(PruneData *pd, uint64_t ind); 16 | static bool read_ptable_file(PruneData *pd); 17 | static bool write_ptable_file(PruneData *pd); 18 | 19 | PruneData 20 | pd_eofb_HTM = { 21 | .filename = "pt_eofb_HTM", 22 | .coord = &coord_eofb, 23 | .moveset = &moveset_HTM, 24 | }; 25 | 26 | PruneData 27 | pd_coud_HTM = { 28 | .filename = "pt_coud_HTM", 29 | .coord = &coord_coud, 30 | .moveset = &moveset_HTM, 31 | }; 32 | 33 | PruneData 34 | pd_cornershtr_HTM = { 35 | .filename = "pt_cornershtr_HTM", 36 | .coord = &coord_cornershtr, 37 | .moveset = &moveset_HTM, 38 | }; 39 | 40 | PruneData 41 | pd_corners_HTM = { 42 | .filename = "pt_corners_HTM", 43 | .coord = &coord_corners, 44 | .moveset = &moveset_HTM, 45 | }; 46 | 47 | PruneData 48 | pd_drud_sym16_HTM = { 49 | .filename = "pt_drud_sym16_HTM", 50 | .coord = &coord_drud_sym16, 51 | .moveset = &moveset_HTM, 52 | }; 53 | 54 | PruneData 55 | pd_drud_eofb = { 56 | .filename = "pt_drud_eofb", 57 | .coord = &coord_drud_eofb, 58 | .moveset = &moveset_eofb, 59 | }; 60 | 61 | PruneData 62 | pd_drudfin_noE_sym16_drud = { 63 | .filename = "pt_drudfin_noE_sym16_drud", 64 | .coord = &coord_drudfin_noE_sym16, 65 | .moveset = &moveset_drud, 66 | }; 67 | 68 | PruneData 69 | pd_htr_drud = { 70 | .filename = "pt_htr_drud", 71 | .coord = &coord_htr_drud, 72 | .moveset = &moveset_drud, 73 | }; 74 | 75 | PruneData 76 | pd_cp_drud = { 77 | .filename = "pt_cp_drud", 78 | .coord = &coord_cp, 79 | .moveset = &moveset_drud, 80 | }; 81 | 82 | PruneData 83 | pd_htrfin_htr = { 84 | .filename = "pt_htrfin_htr", 85 | .coord = &coord_htrfin, 86 | .moveset = &moveset_htr, 87 | }; 88 | 89 | PruneData 90 | pd_nxopt31_HTM = { 91 | .filename = "pt_nxopt31_HTM", 92 | .coord = &coord_nxopt31, 93 | .moveset = &moveset_HTM, 94 | 95 | .compact = true, 96 | .fallback = &pd_drud_sym16_HTM, 97 | .fbmod = BINOM8ON4, 98 | }; 99 | 100 | PruneData * all_pd[] = { 101 | &pd_eofb_HTM, 102 | &pd_coud_HTM, 103 | &pd_cornershtr_HTM, 104 | &pd_corners_HTM, 105 | &pd_drud_sym16_HTM, 106 | &pd_drud_eofb, 107 | &pd_drudfin_noE_sym16_drud, 108 | &pd_htr_drud, 109 | &pd_cp_drud, 110 | &pd_htrfin_htr, 111 | &pd_nxopt31_HTM, 112 | NULL 113 | }; 114 | 115 | /* Functions *****************************************************************/ 116 | 117 | int 118 | findchunk(PruneData *pd, int nchunks, uint64_t i) 119 | { 120 | uint64_t chunksize; 121 | 122 | chunksize = pd->coord->max / (uint64_t)nchunks; 123 | chunksize += ENTRIES_PER_GROUP - (chunksize % ENTRIES_PER_GROUP); 124 | 125 | return MIN(nchunks-1, (int)(i / chunksize)); 126 | } 127 | 128 | void 129 | free_pd(PruneData *pd) 130 | { 131 | if (pd->generated) 132 | free(pd->ptable); 133 | 134 | pd->generated = false; 135 | } 136 | 137 | void 138 | genptable(PruneData *pd, int nthreads) 139 | { 140 | bool compact; 141 | int d, nchunks; 142 | uint64_t oldn, sz; 143 | 144 | if (pd->generated) 145 | return; 146 | 147 | /* TODO: check if memory is enough, otherwise maybe exit gracefully? */ 148 | sz = ptablesize(pd) * (pd->compact ? 2 : 1); 149 | pd->ptable = malloc(sz * sizeof(entry_group_t)); 150 | 151 | if (read_ptable_file(pd)) { 152 | pd->generated = true; 153 | return; 154 | } 155 | 156 | if (nthreads < 4) { 157 | fprintf(stderr, 158 | "--- Warning ---\n" 159 | "You are using only %d threads to generate the pruning" 160 | "tables. This can take a while." 161 | "Unless you did this intentionally, you should re-run" 162 | "this command with `-t 4' or more.\n" 163 | "---------------\n\n", nthreads 164 | ); 165 | } 166 | 167 | 168 | /* For the first steps we proceed the same way for compact and not */ 169 | compact = pd->compact; 170 | pd->compact = false; 171 | pd->generated = true; 172 | 173 | nchunks = MIN(ptablesize(pd), 100000); 174 | fprintf(stderr, "Cannot load %s, generating it with %d threads\n", 175 | pd->filename, nthreads); 176 | 177 | 178 | memset(pd->ptable, ~(uint8_t)0, ptablesize(pd)*sizeof(entry_group_t)); 179 | 180 | ptable_update(pd, (Cube){0}, 0); 181 | pd->n = 1; 182 | oldn = 0; 183 | genptable_fixnasty(pd, 0, nthreads); 184 | fprintf(stderr, "Depth %d done, generated %" 185 | PRIu64 "\t(%" PRIu64 "/%" PRIu64 ")\n", 186 | 0, pd->n - oldn, pd->n, pd->coord->max); 187 | oldn = pd->n; 188 | pd->count[0] = pd->n; 189 | for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { 190 | genptable_bfs(pd, d, nthreads, nchunks); 191 | genptable_fixnasty(pd, d+1, nthreads); 192 | fprintf(stderr, "Depth %d done, generated %" 193 | PRIu64 "\t(%" PRIu64 "/%" PRIu64 ")\n", 194 | d+1, pd->n - oldn, pd->n, pd->coord->max); 195 | pd->count[d+1] = pd->n - oldn; 196 | oldn = pd->n; 197 | } 198 | fprintf(stderr, "Pruning table generated!\n"); 199 | 200 | genptable_setbase(pd); 201 | if (compact) 202 | genptable_compress(pd); 203 | 204 | if (!write_ptable_file(pd)) 205 | fprintf(stderr, "Error writing ptable file\n"); 206 | } 207 | 208 | static void 209 | genptable_bfs(PruneData *pd, int d, int nthreads, int nchunks) 210 | { 211 | int i; 212 | pthread_t t[nthreads]; 213 | ThreadDataGenpt td[nthreads]; 214 | pthread_mutex_t *mtx[nchunks], *upmtx; 215 | 216 | upmtx = malloc(sizeof(pthread_mutex_t)); 217 | pthread_mutex_init(upmtx, NULL); 218 | for (i = 0; i < nchunks; i++) { 219 | mtx[i] = malloc(sizeof(pthread_mutex_t)); 220 | pthread_mutex_init(mtx[i], NULL); 221 | } 222 | 223 | for (i = 0; i < nthreads; i++) { 224 | td[i].thid = i; 225 | td[i].nthreads = nthreads; 226 | td[i].pd = pd; 227 | td[i].d = d; 228 | td[i].nchunks = nchunks; 229 | td[i].mutex = mtx; 230 | td[i].upmutex = upmtx; 231 | pthread_create(&t[i], NULL, instance_bfs, &td[i]); 232 | } 233 | 234 | for (i = 0; i < nthreads; i++) 235 | pthread_join(t[i], NULL); 236 | 237 | free(upmtx); 238 | for (i = 0; i < nchunks; i++) 239 | free(mtx[i]); 240 | } 241 | 242 | static void 243 | genptable_compress(PruneData *pd) 244 | { 245 | int val; 246 | uint64_t i, j; 247 | entry_group_t mask, v; 248 | 249 | fprintf(stderr, "Compressing table to 2 bits per entry\n"); 250 | 251 | for (i = 0; i < pd->coord->max; i += ENTRIES_PER_GROUP_COMPACT) { 252 | mask = (entry_group_t)0; 253 | for (j = 0; j < ENTRIES_PER_GROUP_COMPACT; j++) { 254 | if (i+j >= pd->coord->max) 255 | break; 256 | val = ptableval_index(pd, i+j) - pd->base; 257 | v = (entry_group_t)MIN(3, MAX(0, val)); 258 | mask |= v << (2*j); 259 | } 260 | pd->ptable[i/ENTRIES_PER_GROUP_COMPACT] = mask; 261 | } 262 | 263 | pd->compact = true; 264 | pd->ptable = realloc(pd->ptable, sizeof(entry_group_t)*ptablesize(pd)); 265 | } 266 | 267 | static void 268 | genptable_fixnasty(PruneData *pd, int d, int nthreads) 269 | { 270 | int i; 271 | pthread_t t[nthreads]; 272 | ThreadDataGenpt td[nthreads]; 273 | pthread_mutex_t *upmtx; 274 | 275 | if (pd->coord->tfind == NULL) 276 | return; 277 | 278 | upmtx = malloc(sizeof(pthread_mutex_t)); 279 | pthread_mutex_init(upmtx, NULL); 280 | for (i = 0; i < nthreads; i++) { 281 | td[i].thid = i; 282 | td[i].nthreads = nthreads; 283 | td[i].pd = pd; 284 | td[i].d = d; 285 | td[i].upmutex = upmtx; 286 | pthread_create(&t[i], NULL, instance_fixnasty, &td[i]); 287 | } 288 | 289 | for (i = 0; i < nthreads; i++) 290 | pthread_join(t[i], NULL); 291 | 292 | free(upmtx); 293 | } 294 | 295 | static void 296 | genptable_setbase(PruneData *pd) 297 | { 298 | int i; 299 | uint64_t sum, newsum; 300 | 301 | pd->base = 0; 302 | sum = pd->count[0] + pd->count[1] + pd->count[2]; 303 | for (i = 3; i < 16; i++) { 304 | newsum = sum + pd->count[i] - pd->count[i-3]; 305 | if (newsum > sum) 306 | pd->base = i-3; 307 | sum = newsum; 308 | } 309 | } 310 | 311 | static void * 312 | instance_bfs(void *arg) 313 | { 314 | ThreadDataGenpt *td; 315 | uint64_t i, ii, blocksize, rmin, rmax, updated; 316 | int j, pval, ichunk; 317 | Move *ms; 318 | 319 | td = (ThreadDataGenpt *)arg; 320 | ms = td->pd->moveset->sorted_moves; 321 | blocksize = td->pd->coord->max / (uint64_t)td->nthreads; 322 | rmin = ((uint64_t)td->thid) * blocksize; 323 | rmax = td->thid == td->nthreads - 1 ? 324 | td->pd->coord->max : 325 | ((uint64_t)td->thid + 1) * blocksize; 326 | 327 | updated = 0; 328 | for (i = rmin; i < rmax; i++) { 329 | ichunk = findchunk(td->pd, td->nchunks, i); 330 | pthread_mutex_lock(td->mutex[ichunk]); 331 | pval = ptableval_index(td->pd, i); 332 | pthread_mutex_unlock(td->mutex[ichunk]); 333 | if (pval == td->d) { 334 | for (j = 0; ms[j] != NULLMOVE; j++) { 335 | ii = td->pd->coord->move(ms[j], i); 336 | ichunk = findchunk(td->pd, td->nchunks, ii); 337 | pthread_mutex_lock(td->mutex[ichunk]); 338 | pval = ptableval_index(td->pd, ii); 339 | if (pval > td->d+1) { 340 | ptable_update_index(td->pd, 341 | ii, td->d+1); 342 | updated++; 343 | } 344 | pthread_mutex_unlock(td->mutex[ichunk]); 345 | } 346 | } 347 | } 348 | 349 | pthread_mutex_lock(td->upmutex); 350 | td->pd->n += updated; 351 | pthread_mutex_unlock(td->upmutex); 352 | 353 | return NULL; 354 | } 355 | 356 | static void * 357 | instance_fixnasty(void *arg) 358 | { 359 | ThreadDataGenpt *td; 360 | uint64_t i, ii, nb, blocksize, rmin, rmax, updated; 361 | int j, n; 362 | Trans t, aux[NTRANS]; 363 | 364 | td = (ThreadDataGenpt *)arg; 365 | nb = td->pd->coord->max / td->pd->coord->base->max; 366 | blocksize = (td->pd->coord->base->max / td->nthreads) * nb; 367 | rmin = ((uint64_t)td->thid) * blocksize; 368 | rmax = td->thid == td->nthreads - 1 ? 369 | td->pd->coord->max : 370 | ((uint64_t)td->thid + 1) * blocksize; 371 | 372 | updated = 0; 373 | for (i = rmin; i < rmax; i++) { 374 | if (ptableval_index(td->pd, i) == td->d) { 375 | if ((n = td->pd->coord->tfind(i, aux)) == 1) 376 | continue; 377 | 378 | for (j = 0; j < n; j++) { 379 | if ((t = aux[j]) == uf) 380 | continue; 381 | ii = td->pd->coord->transform(t, i); 382 | if (ptableval_index(td->pd, ii) > td->d) { 383 | ptable_update_index(td->pd, ii, td->d); 384 | updated++; 385 | } 386 | } 387 | } 388 | } 389 | 390 | pthread_mutex_lock(td->upmutex); 391 | td->pd->n += updated; 392 | pthread_mutex_unlock(td->upmutex); 393 | 394 | return NULL; 395 | } 396 | 397 | void 398 | print_ptable(PruneData *pd) 399 | { 400 | uint64_t i; 401 | 402 | if (!pd->generated) 403 | genptable(pd, 1); /* TODO: set default nthreads somewhere */ 404 | 405 | printf("Table %s\n", pd->filename); 406 | printf("Base value: %d\n", pd->base); 407 | for (i = 0; i < 16; i++) 408 | if (pd->count[i] != 0) 409 | printf("%2" PRIu64 "\t%10" PRIu64 "\n", 410 | i, pd->count[i]); 411 | printf("Total: %" PRIu64 "\n", pd->coord->max); 412 | } 413 | 414 | uint64_t 415 | ptablesize(PruneData *pd) 416 | { 417 | uint64_t e; 418 | 419 | e = pd->compact ? ENTRIES_PER_GROUP_COMPACT : ENTRIES_PER_GROUP; 420 | 421 | return (pd->coord->max + e - 1) / e; 422 | } 423 | 424 | static void 425 | ptable_update(PruneData *pd, Cube cube, int n) 426 | { 427 | ptable_update_index(pd, pd->coord->index(cube), n); 428 | } 429 | 430 | static void 431 | ptable_update_index(PruneData *pd, uint64_t ind, int n) 432 | { 433 | int sh; 434 | entry_group_t mask; 435 | uint64_t i; 436 | 437 | sh = 4 * (ind % ENTRIES_PER_GROUP); 438 | mask = ((entry_group_t)15) << sh; 439 | i = ind/ENTRIES_PER_GROUP; 440 | 441 | pd->ptable[i] &= ~mask; 442 | pd->ptable[i] |= (((entry_group_t)n)&15) << sh; 443 | } 444 | 445 | int 446 | ptableval(PruneData *pd, Cube cube) 447 | { 448 | return ptableval_index(pd, pd->coord->index(cube)); 449 | } 450 | 451 | static int 452 | ptableval_index(PruneData *pd, uint64_t ind) 453 | { 454 | int sh, ret; 455 | entry_group_t mask; 456 | uint64_t i, e; 457 | entry_group_t m; 458 | 459 | if (!pd->generated) { 460 | fprintf(stderr, "Warning: request pruning table value" 461 | " for uninitialized table %s.\n It's fine, but it" 462 | " should not happen. Please report bug.\n", 463 | pd->filename); 464 | genptable(pd, 1); /* TODO: set default or remove this case */ 465 | } 466 | 467 | if (pd->compact) { 468 | e = ENTRIES_PER_GROUP_COMPACT; 469 | m = 3; 470 | sh = (ind % e) * 2; 471 | } else { 472 | e = ENTRIES_PER_GROUP; 473 | m = 15; 474 | sh = (ind % e) * 4; 475 | } 476 | 477 | mask = m << sh; 478 | i = ind/e; 479 | 480 | ret = (pd->ptable[i] & mask) >> sh; 481 | 482 | if (pd->compact) { 483 | if (ret) 484 | ret += pd->base; 485 | else 486 | ret = ptableval_index(pd->fallback, ind / pd->fbmod); 487 | } 488 | 489 | return ret; 490 | } 491 | 492 | static bool 493 | read_ptable_file(PruneData *pd) 494 | { 495 | init_env(); 496 | 497 | FILE *f; 498 | char fname[strlen(tabledir)+100]; 499 | int i; 500 | uint64_t r; 501 | 502 | strcpy(fname, tabledir); 503 | strcat(fname, "/"); 504 | strcat(fname, pd->filename); 505 | 506 | if ((f = fopen(fname, "rb")) == NULL) 507 | return false; 508 | 509 | r = fread(&(pd->base), sizeof(int), 1, f); 510 | for (i = 0; i < 16; i++) 511 | r += fread(&(pd->count[i]), sizeof(uint64_t), 1, f); 512 | r += fread(pd->ptable, sizeof(entry_group_t), ptablesize(pd), f); 513 | 514 | fclose(f); 515 | 516 | return r == 17 + ptablesize(pd); 517 | } 518 | 519 | static bool 520 | write_ptable_file(PruneData *pd) 521 | { 522 | init_env(); 523 | 524 | FILE *f; 525 | char fname[strlen(tabledir)+100]; 526 | int i; 527 | uint64_t w; 528 | 529 | strcpy(fname, tabledir); 530 | strcat(fname, "/"); 531 | strcat(fname, pd->filename); 532 | 533 | if ((f = fopen(fname, "wb")) == NULL) 534 | return false; 535 | 536 | w = fwrite(&(pd->base), sizeof(int), 1, f); 537 | for (i = 0; i < 16; i++) 538 | w += fwrite(&(pd->count[i]), sizeof(uint64_t), 1, f); 539 | w += fwrite(pd->ptable, sizeof(entry_group_t), ptablesize(pd), f); 540 | fclose(f); 541 | 542 | return w == 17 + ptablesize(pd); 543 | } 544 | 545 | -------------------------------------------------------------------------------- /src/solve.c: -------------------------------------------------------------------------------- 1 | #include "solve.h" 2 | 3 | /* Local functions ***********************************************************/ 4 | 5 | static bool allowed_next(Move move, DfsArg *arg); 6 | static bool cancel_niss(DfsArg *arg); 7 | static void copy_dfsarg(DfsArg *src, DfsArg *dst); 8 | static void dfs(DfsArg *arg); 9 | static void dfs_branch(DfsArg *arg); 10 | static bool dfs_check_solved(DfsArg *arg); 11 | static bool dfs_switch(DfsArg *arg); 12 | static void dfs_niss(DfsArg *arg); 13 | static bool dfs_stop(DfsArg *arg); 14 | static void * instance_thread(void *arg); 15 | static void invert_branch(DfsArg *arg); 16 | static void multidfs(Cube c, Trans t, Step *s, SolveOptions *opts, 17 | AlgList *sols, int d); 18 | static bool niss_makes_sense(DfsArg *arg); 19 | static bool solvestop(int d, int op, SolveOptions *opts, AlgList *sols); 20 | 21 | /* Local functions ***********************************************************/ 22 | 23 | static bool 24 | allowed_next(Move m, DfsArg *arg) 25 | { 26 | bool bad, allowed, order; 27 | uint64_t mbit; 28 | 29 | mbit = ((uint64_t)1) << m; 30 | bad = mbit & arg->badmoves; 31 | allowed = mbit & arg->step->moveset->mask[arg->last2][arg->last1]; 32 | order = !commute(arg->last1, m) || arg->last1 < m; 33 | 34 | return allowed && !bad && order; 35 | } 36 | 37 | static bool 38 | cancel_niss(DfsArg *arg) 39 | { 40 | Moveset *ms; 41 | Move i1, i2; 42 | bool p, p1, p2, q, q1, q2; 43 | 44 | if (arg->last1inv == NULLMOVE) 45 | return false; 46 | 47 | ms = arg->step->moveset; 48 | i1 = inverse_move(arg->last1inv); 49 | i2 = inverse_move(arg->last2inv); 50 | 51 | p1 = !ms->allowed_next(arg->last2, arg->last1, i1); 52 | p2 = !ms->allowed_next(arg->last2, i1, arg->last1); 53 | p = p1 || (commute(i1, arg->last1) && p2); 54 | 55 | q1 = !ms->allowed_next(arg->last2, arg->last1, i2); 56 | q2 = !ms->allowed_next(arg->last2, i2, arg->last1); 57 | q = q1 || (commute(i2, arg->last1) && q2); 58 | 59 | return p || (commute(i1, i2) && q); 60 | } 61 | 62 | static void 63 | copy_dfsarg(DfsArg *src, DfsArg *dst) 64 | { 65 | dst->step = src->step; 66 | dst->opts = src->opts; 67 | dst->t = src->t; 68 | dst->cube = src->cube; 69 | dst->inverse = src->inverse; 70 | dst->d = src->d; 71 | dst->badmoves = src->badmoves; 72 | dst->badmovesinv = src->badmovesinv; 73 | dst->niss = src->niss; 74 | dst->last1 = src->last1; 75 | dst->last2 = src->last2; 76 | dst->last1inv = src->last1inv; 77 | dst->last2inv = src->last2inv; 78 | dst->sols = src->sols; 79 | dst->sols_mutex = src->sols_mutex; 80 | dst->current_alg = src->current_alg; 81 | 82 | copy_estimatedata(src->ed, dst->ed); 83 | } 84 | 85 | static void 86 | dfs(DfsArg *arg) 87 | { 88 | bool sw = false; 89 | 90 | if (dfs_stop(arg)) 91 | return; 92 | 93 | if (dfs_check_solved(arg)) 94 | return; 95 | 96 | if (arg->step->final && (sw = dfs_switch(arg))) 97 | invert_branch(arg); 98 | dfs_branch(arg); 99 | 100 | if (arg->opts->nisstype == NISS && !arg->niss && niss_makes_sense(arg)) 101 | dfs_niss(arg); 102 | 103 | if (sw) 104 | invert_branch(arg); 105 | } 106 | 107 | static void 108 | dfs_branch(DfsArg *arg) 109 | { 110 | int i; 111 | Move m; 112 | DfsArg *newarg; 113 | 114 | newarg = malloc(sizeof(DfsArg)); 115 | newarg->ed = malloc(sizeof(EstimateData)); 116 | 117 | for (i = 0; arg->step->moveset->sorted_moves[i] != NULLMOVE; i++) { 118 | m = arg->step->moveset->sorted_moves[i]; 119 | if (allowed_next(m, arg)) { 120 | copy_dfsarg(arg, newarg); 121 | newarg->last2 = arg->last1; 122 | newarg->last1 = m; 123 | newarg->cube = apply_move(m, arg->cube); 124 | append_move(arg->current_alg, m, newarg->niss); 125 | 126 | dfs(newarg); 127 | 128 | arg->current_alg->len--; 129 | } 130 | } 131 | 132 | free(newarg->ed); 133 | free(newarg); 134 | } 135 | 136 | static bool 137 | dfs_check_solved(DfsArg *arg) 138 | { 139 | if (!arg->step->is_done(arg->cube)) 140 | return false; 141 | 142 | if (arg->current_alg->len == arg->d) { 143 | if ((arg->step->is_valid(arg->current_alg) || arg->opts->all) 144 | && (!arg->step->final || !cancel_niss(arg))) { 145 | 146 | pthread_mutex_lock(arg->sols_mutex); 147 | 148 | if (arg->sols->len < arg->opts->max_solutions) { 149 | append_alg(arg->sols, arg->current_alg); 150 | 151 | transform_alg( 152 | inverse_trans(arg->t), 153 | arg->sols->last->alg 154 | ); 155 | if (arg->step->final) 156 | inplace(unniss, arg->sols->last->alg); 157 | 158 | if (arg->opts->verbose) 159 | print_alg(arg->sols->last->alg, false); 160 | } 161 | 162 | pthread_mutex_unlock(arg->sols_mutex); 163 | } 164 | } 165 | 166 | return true; 167 | } 168 | 169 | static void 170 | dfs_niss(DfsArg *arg) 171 | { 172 | DfsArg *newarg; 173 | 174 | newarg = malloc(sizeof(DfsArg)); 175 | newarg->ed = malloc(sizeof(EstimateData)); 176 | 177 | copy_dfsarg(arg, newarg); 178 | swapmove(&(newarg->last1), &(newarg->last1inv)); 179 | swapmove(&(newarg->last2), &(newarg->last2inv)); 180 | newarg->niss = !(arg->niss); 181 | newarg->cube = inverse_cube(arg->cube); 182 | 183 | dfs(newarg); 184 | 185 | free(newarg->ed); 186 | free(newarg); 187 | } 188 | 189 | static bool 190 | dfs_stop(DfsArg *arg) 191 | { 192 | int lowerbound; 193 | bool b; 194 | 195 | lowerbound = arg->step->estimate(arg); 196 | if (arg->opts->nisstype == NISS && !arg->niss) 197 | lowerbound = MIN(1, lowerbound); 198 | 199 | if (arg->current_alg->len + lowerbound > arg->d) { 200 | b = true; 201 | } else { 202 | pthread_mutex_lock(arg->sols_mutex); 203 | b = arg->sols->len >= arg->opts->max_solutions; 204 | pthread_mutex_unlock(arg->sols_mutex); 205 | } 206 | 207 | return b; 208 | } 209 | 210 | static bool 211 | dfs_switch(DfsArg *arg) 212 | { 213 | int i, bn, bi; 214 | 215 | bn = 0; 216 | for (i = 0; arg->step->moveset->sorted_moves[i] != NULLMOVE; i++) 217 | if (allowed_next(arg->step->moveset->sorted_moves[i], arg)) 218 | bn++; 219 | 220 | swapmove(&(arg->last1), &(arg->last1inv)); 221 | swapmove(&(arg->last2), &(arg->last2inv)); 222 | swapu64(&(arg->badmoves), &(arg->badmovesinv)); 223 | 224 | bi = 0; 225 | for (i = 0; arg->step->moveset->sorted_moves[i] != NULLMOVE; i++) 226 | if (allowed_next(arg->step->moveset->sorted_moves[i], arg)) 227 | bi++; 228 | 229 | swapmove(&(arg->last1), &(arg->last1inv)); 230 | swapmove(&(arg->last2), &(arg->last2inv)); 231 | swapu64(&(arg->badmoves), &(arg->badmovesinv)); 232 | 233 | return bi < bn; 234 | } 235 | 236 | static void * 237 | instance_thread(void *arg) 238 | { 239 | bool b; 240 | Cube c; 241 | ThreadDataSolve *td; 242 | AlgListNode *node; 243 | DfsArg darg; 244 | 245 | td = (ThreadDataSolve *)arg; 246 | 247 | while (1) { 248 | b = false; 249 | 250 | pthread_mutex_lock(td->start_mutex); 251 | if ((node = *(td->node)) == NULL) 252 | b = true; 253 | else 254 | *(td->node) = (*(td->node))->next; 255 | pthread_mutex_unlock(td->start_mutex); 256 | 257 | if (b) 258 | break; 259 | 260 | c = node->alg->inv[0] ? 261 | apply_move(node->alg->move[0], inverse_cube(td->cube)) : 262 | apply_move(node->alg->move[0], td->cube); 263 | 264 | darg.step = td->step; 265 | darg.opts = td->opts; 266 | darg.t = td->t; 267 | darg.cube = c; 268 | darg.d = td->depth; 269 | darg.niss = node->alg->inv[0]; 270 | darg.last1 = node->alg->move[0]; 271 | darg.last2 = NULLMOVE; 272 | darg.last1inv = NULLMOVE; 273 | darg.last2inv = NULLMOVE; 274 | darg.sols = td->sols; 275 | darg.sols_mutex = td->sols_mutex; 276 | darg.current_alg = new_alg(""); 277 | append_move(darg.current_alg, node->alg->move[0], 278 | node->alg->inv[0]); 279 | darg.ed = malloc(sizeof(EstimateData)); 280 | reset_estimatedata(darg.ed); 281 | darg.badmoves = 0; 282 | darg.badmovesinv = 0; 283 | 284 | dfs(&darg); 285 | 286 | free_alg(darg.current_alg); 287 | free(darg.ed); 288 | } 289 | 290 | return NULL; 291 | } 292 | 293 | static void 294 | invert_branch(DfsArg *arg) 295 | { 296 | Cube aux; 297 | 298 | aux = arg->cube; 299 | arg->cube = is_solved(arg->inverse) ? 300 | inverse_cube(arg->cube) : arg->inverse; 301 | arg->inverse = aux; 302 | 303 | swapu64(&(arg->badmoves), &(arg->badmovesinv)); 304 | arg->niss = !(arg->niss); 305 | swapmove(&(arg->last1), &(arg->last1inv)); 306 | swapmove(&(arg->last2), &(arg->last2inv)); 307 | invert_estimatedata(arg->ed); 308 | } 309 | 310 | static void 311 | multidfs(Cube c, Trans tr, Step *s, SolveOptions *opts, AlgList *sols, int d) 312 | { 313 | int i; 314 | Alg *alg; 315 | AlgList *start; 316 | AlgListNode **node; 317 | pthread_t t[opts->nthreads]; 318 | ThreadDataSolve td[opts->nthreads]; 319 | pthread_mutex_t *start_mutex, *sols_mutex; 320 | 321 | node = malloc(sizeof(AlgListNode *)); 322 | start_mutex = malloc(sizeof(pthread_mutex_t)); 323 | sols_mutex = malloc(sizeof(pthread_mutex_t)); 324 | 325 | start = new_alglist(); 326 | pthread_mutex_init(start_mutex, NULL); 327 | pthread_mutex_init(sols_mutex, NULL); 328 | 329 | for (i = 0; s->moveset->sorted_moves[i] != NULLMOVE; i++) { 330 | alg = new_alg(""); 331 | append_move(alg, s->moveset->sorted_moves[i], false); 332 | append_alg(start, alg); 333 | if (opts->nisstype == NISS || opts->nisstype == LINEAR) { 334 | alg->inv[0] = true; 335 | append_alg(start, alg); 336 | } 337 | free_alg(alg); 338 | } 339 | *node = start->first; 340 | 341 | for (i = 0; i < opts->nthreads; i++) { 342 | td[i].thid = i; 343 | td[i].t = tr; 344 | td[i].cube = c; 345 | td[i].step = s; 346 | td[i].depth = d; 347 | td[i].opts = opts; 348 | td[i].start = start; 349 | td[i].node = node; 350 | td[i].sols = sols; 351 | td[i].start_mutex = start_mutex; 352 | td[i].sols_mutex = sols_mutex; 353 | pthread_create(&t[i], NULL, instance_thread, &td[i]); 354 | } 355 | 356 | for (i = 0; i < opts->nthreads; i++) 357 | pthread_join(t[i], NULL); 358 | 359 | free_alglist(start); 360 | free(node); 361 | free(start_mutex); 362 | free(sols_mutex); 363 | } 364 | 365 | static bool 366 | niss_makes_sense(DfsArg *arg) 367 | { 368 | Cube testcube; 369 | 370 | testcube = apply_move(inverse_move(arg->last1), (Cube){0}); 371 | return arg->current_alg->len == 0 || !arg->step->is_done(testcube); 372 | } 373 | 374 | static bool 375 | solvestop(int d, int op, SolveOptions *opts, AlgList *sols) 376 | { 377 | bool opt_done, max_moves_exceeded, max_sols_exceeded; 378 | 379 | opt_done = opts->optimal != -1 && op != -1 && d > opts->optimal + op; 380 | max_moves_exceeded = d > opts->max_moves; 381 | max_sols_exceeded = sols->len >= opts->max_solutions; 382 | 383 | return opt_done || max_moves_exceeded || max_sols_exceeded; 384 | } 385 | 386 | /* Public functions **********************************************************/ 387 | 388 | SolveOutput *solve_output_new(AlgList *sols, char *error_msg) { 389 | SolveOutput *so = malloc(sizeof(SolveOutput)); 390 | so->sols = sols; 391 | so->error_msg = error_msg; 392 | return so; 393 | } 394 | 395 | void solve_output_free(SolveOutput *so) { 396 | free_alglist(so->sols); 397 | // Can't free error_msg because it could be a static string 398 | free(so); 399 | } 400 | 401 | void 402 | prepare_step(Step *step, SolveOptions *opts) 403 | { 404 | if (step->final && opts->nisstype != NORMAL) { 405 | opts->nisstype = NORMAL; 406 | } 407 | 408 | for (int i = 0; i < step->ntables; i++) { 409 | genptable(step->tables[i], opts->nthreads); 410 | if (step->tables[i]->compact) 411 | genptable(step->tables[i]->fallback, opts->nthreads); 412 | } 413 | } 414 | 415 | SolveOutput *solve(struct timespec start, Cube cube, Step *step, SolveOptions *opts) { 416 | bool ready; 417 | int i, d, op, nt; 418 | Alg *algaux; 419 | AlgList *sols; 420 | Cube c; 421 | Trans tt[NTRANS]; 422 | 423 | prepare_step(step, opts); 424 | 425 | if (step->detect != NULL) { 426 | nt = step->detect(cube, tt); 427 | } else { 428 | tt[0] = step->pre_trans; 429 | ready = step->ready == NULL || 430 | step->ready(apply_trans(tt[0], cube)); 431 | nt = ready ? 1 : 0; 432 | } 433 | 434 | sols = new_alglist(); 435 | 436 | if (nt == 0) { 437 | char *msg = malloc(sizeof(char) * 100); 438 | sprintf(msg, "Step is not ready to be solved. %s\n", step->ready_msg); 439 | fprintf(stderr, "%s", msg); 440 | return solve_output_new(sols, msg); 441 | } 442 | 443 | if (opts->min_moves == 0) { 444 | for (i = 0; i < nt; i++) { 445 | c = apply_trans(tt[i], cube); 446 | if (step->is_done(c)) { 447 | algaux = new_alg(""); 448 | append_alg(sols, algaux); 449 | free_alg(algaux); 450 | return solve_output_new(sols, NULL); 451 | } 452 | } 453 | } 454 | 455 | op = -1; 456 | for (d = opts->min_moves; !solvestop(d, op, opts, sols); d++) { 457 | if (elapsed_ms(start) > TIME_LIMIT) { 458 | return solve_output_new(sols, TIMEOUT_MSG); 459 | } 460 | 461 | if (opts->verbose) 462 | fprintf(stderr, "Searching depth %d\n", d); 463 | 464 | for (i = 0; i < nt && !solvestop(d, op, opts, sols); i++) { 465 | c = apply_trans(tt[i], cube); 466 | multidfs(c, tt[i], step, opts, sols, d); 467 | if (sols->len > 0 && op == -1) 468 | op = d; 469 | } 470 | } 471 | 472 | return solve_output_new(sols, NULL); 473 | } 474 | 475 | /* TODO: make more general! */ 476 | Alg * 477 | solve_2phase(struct timespec start, Cube cube, int nthreads) 478 | { 479 | int bestlen, newb; 480 | Alg *bestalg, *ret; 481 | AlgList *sols1, *sols2; 482 | AlgListNode *i; 483 | Cube c; 484 | SolveOptions opts1, opts2; 485 | 486 | opts1.min_moves = 0; 487 | opts1.max_moves = 13; 488 | opts1.max_solutions = 20; 489 | opts1.nthreads = nthreads; 490 | opts1.optimal = 3; 491 | opts1.nisstype = NORMAL; 492 | opts1.verbose = false; 493 | opts1.all = true; 494 | 495 | opts2.min_moves = 0; 496 | opts2.max_moves = 19; 497 | opts2.max_solutions = 1; 498 | opts2.nthreads = nthreads; 499 | opts2.nisstype = NORMAL; 500 | opts2.verbose = false; 501 | 502 | /* We skip step1 if it is solved on any axis */ 503 | if (drany_HTM.is_done(cube)) { 504 | sols1 = new_alglist(); 505 | append_alg(sols1, new_alg("")); 506 | } else { 507 | SolveOutput *so = solve(start, cube, &drany_HTM, &opts1); 508 | sols1 = so->sols; 509 | free(so); 510 | } 511 | bestalg = new_alg(""); 512 | bestlen = 999; 513 | for (i = sols1->first; i != NULL; i = i->next) { 514 | c = apply_alg(i->alg, cube); 515 | SolveOutput *so = solve(start, c, &dranyfin_DR, &opts2); 516 | sols2 = so->sols; 517 | free(so); 518 | 519 | if (sols2->len > 0) { 520 | newb = i->alg->len + sols2->first->alg->len; 521 | if (newb < bestlen) { 522 | bestlen = newb; 523 | copy_alg(i->alg, bestalg); 524 | compose_alg(bestalg, sols2->first->alg); 525 | } 526 | } 527 | 528 | free_alglist(sols2); 529 | } 530 | 531 | free_alglist(sols1); 532 | 533 | ret = cleanup(bestalg); 534 | free_alg(bestalg); 535 | 536 | return ret; 537 | } 538 | -------------------------------------------------------------------------------- /src/coord.c: -------------------------------------------------------------------------------- 1 | #include "coord.h" 2 | 3 | static uint64_t index_eofb(Cube cube); 4 | static uint64_t index_eofbepos(Cube cube); 5 | static uint64_t index_epud(Cube cube); 6 | static uint64_t index_coud(Cube cube); 7 | static uint64_t index_corners(Cube cube); 8 | static uint64_t index_cp(Cube cube); 9 | static uint64_t index_cphtr(Cube cube); 10 | static uint64_t index_cornershtr(Cube cube); 11 | static uint64_t index_cornershtrfin(Cube cube); 12 | static uint64_t index_drud(Cube cube); 13 | static uint64_t index_drud_eofb(Cube cube); 14 | static uint64_t index_htr_drud(Cube cube); 15 | static uint64_t index_htrfin(Cube cube); 16 | static uint64_t index_cpud_separate(Cube cube); 17 | 18 | static uint64_t move_eofb(Move m, uint64_t ind); 19 | static uint64_t move_eofbepos(Move m, uint64_t ind); 20 | static uint64_t move_epud(Move m, uint64_t ind); 21 | static uint64_t move_coud(Move m, uint64_t ind); 22 | static uint64_t move_corners(Move m, uint64_t ind); 23 | static uint64_t move_cp(Move m, uint64_t ind); 24 | static uint64_t move_cphtr(Move m, uint64_t ind); 25 | static uint64_t move_cornershtr(Move m, uint64_t ind); 26 | static uint64_t move_cornershtrfin(Move m, uint64_t ind); 27 | static uint64_t move_drud(Move m, uint64_t ind); 28 | static uint64_t move_drud_eofb(Move m, uint64_t ind); 29 | static uint64_t move_htr_drud(Move m, uint64_t ind); 30 | static uint64_t move_htrfin(Move m, uint64_t ind); 31 | static uint64_t move_cpud_separate(Move m, uint64_t ind); 32 | 33 | static void init_cphtr_cosets(void); 34 | static void init_cphtr_left_cosets_bfs(int i, int c); 35 | static void init_cphtr_right_cosets_color(int i, int c); 36 | static void init_cpud_separate(void); 37 | static void init_cornershtrfin(void); 38 | static void init_htr_eposs(void); 39 | static void init_move_epud(void); 40 | static void init_move_cphtr(void); 41 | 42 | 43 | /* All sorts of useful costants and tables **********************************/ 44 | 45 | static int cphtr_left_cosets[FACTORIAL8]; 46 | static int cphtr_right_cosets[FACTORIAL8]; 47 | static int cphtr_right_rep[BINOM8ON4*6]; 48 | int cpud_separate_ind[FACTORIAL8]; 49 | int cpud_separate_ant[BINOM8ON4]; 50 | static int cornershtrfin_ind[FACTORIAL8]; 51 | int cornershtrfin_ant[24*24/6]; 52 | static int htr_eposs_ind[BINOM12ON4]; 53 | static int htr_eposs_ant[BINOM8ON4]; 54 | static int move_cphtr_aux[NMOVES][BINOM8ON4*6]; 55 | static int move_epud_aux[NMOVES][FACTORIAL8]; 56 | 57 | /* Coordinates and their implementation **************************************/ 58 | 59 | Coordinate 60 | coord_eofb = { 61 | .index = index_eofb, 62 | .max = POW2TO11, 63 | .move = move_eofb, 64 | }; 65 | 66 | Coordinate 67 | coord_eofbepos = { 68 | .index = index_eofbepos, 69 | .max = POW2TO11 * BINOM12ON4, 70 | .move = move_eofbepos, 71 | }; 72 | 73 | Coordinate 74 | coord_coud = { 75 | .index = index_coud, 76 | .max = POW3TO7, 77 | .move = move_coud, 78 | }; 79 | 80 | Coordinate 81 | coord_corners = { 82 | .index = index_corners, 83 | .max = POW3TO7 * FACTORIAL8, 84 | .move = move_corners, 85 | }; 86 | 87 | Coordinate 88 | coord_cp = { 89 | .index = index_cp, 90 | .max = FACTORIAL8, 91 | .move = move_cp, 92 | }; 93 | 94 | Coordinate 95 | coord_cphtr = { 96 | .index = index_cphtr, 97 | .max = BINOM8ON4 * 6, 98 | .move = move_cphtr, 99 | }; 100 | 101 | Coordinate 102 | coord_cornershtr = { 103 | .index = index_cornershtr, 104 | .max = POW3TO7 * BINOM8ON4 * 6, 105 | .move = move_cornershtr, 106 | }; 107 | 108 | Coordinate 109 | coord_cornershtrfin = { 110 | .index = index_cornershtrfin, 111 | .max = 24*24/6, 112 | .move = move_cornershtrfin, 113 | }; 114 | 115 | Coordinate 116 | coord_epud = { 117 | .index = index_epud, 118 | .max = FACTORIAL8, 119 | .move = move_epud, 120 | }; 121 | 122 | Coordinate 123 | coord_drud = { 124 | .index = index_drud, 125 | .max = POW2TO11 * POW3TO7 * BINOM12ON4, 126 | .move = move_drud, 127 | }; 128 | 129 | Coordinate 130 | coord_htr_drud = { 131 | .index = index_htr_drud, 132 | .max = BINOM8ON4 * 6 * BINOM8ON4, 133 | .move = move_htr_drud, 134 | }; 135 | 136 | Coordinate 137 | coord_htrfin = { 138 | .index = index_htrfin, 139 | .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */ 140 | .move = move_htrfin, 141 | }; 142 | 143 | Coordinate 144 | coord_drud_eofb = { 145 | .index = index_drud_eofb, 146 | .max = POW3TO7 * BINOM12ON4, 147 | .move = move_drud_eofb, 148 | }; 149 | 150 | Coordinate 151 | coord_cpud_separate = { 152 | .index = index_cpud_separate, 153 | .max = BINOM8ON4, 154 | .move = move_cpud_separate, 155 | }; 156 | 157 | /* Indexers ******************************************************************/ 158 | 159 | static uint64_t 160 | index_eofb(Cube cube) 161 | { 162 | return cube.eofb; 163 | } 164 | 165 | static uint64_t 166 | index_eofbepos(Cube cube) 167 | { 168 | return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; 169 | } 170 | 171 | static uint64_t 172 | index_epud(Cube cube) 173 | { 174 | uint64_t ret; 175 | CubeArray *arr = new_cubearray(cube, pf_ep); 176 | 177 | ret = perm_to_index(arr->ep, 8); 178 | free_cubearray(arr, pf_ep); 179 | 180 | return ret; 181 | } 182 | 183 | static uint64_t 184 | index_coud(Cube cube) 185 | { 186 | return cube.coud; 187 | } 188 | 189 | static uint64_t 190 | index_corners(Cube cube) 191 | { 192 | return cube.coud * FACTORIAL8 + cube.cp; 193 | } 194 | 195 | static uint64_t 196 | index_cp(Cube cube) 197 | { 198 | return cube.cp; 199 | } 200 | 201 | static uint64_t 202 | index_cphtr(Cube cube) 203 | { 204 | return cphtr_right_cosets[cube.cp]; 205 | } 206 | 207 | static uint64_t 208 | index_cornershtr(Cube cube) 209 | { 210 | return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube); 211 | } 212 | 213 | static uint64_t 214 | index_cornershtrfin(Cube cube) 215 | { 216 | return cornershtrfin_ind[cube.cp]; 217 | } 218 | 219 | static uint64_t 220 | index_drud(Cube cube) 221 | { 222 | uint64_t a, b, c; 223 | 224 | a = cube.eofb; 225 | b = cube.coud; 226 | c = cube.epose / FACTORIAL4; 227 | 228 | b *= POW2TO11; 229 | c *= POW2TO11 * POW3TO7; 230 | 231 | return a + b + c; 232 | } 233 | 234 | static uint64_t 235 | index_drud_eofb(Cube cube) 236 | { 237 | return index_drud(cube) / POW2TO11; 238 | } 239 | 240 | static uint64_t 241 | index_htr_drud(Cube cube) 242 | { 243 | uint64_t a, b; 244 | 245 | a = index_cphtr(cube); 246 | b = htr_eposs_ind[cube.eposs/24]; 247 | 248 | return a * BINOM8ON4 + b; 249 | } 250 | 251 | static uint64_t 252 | index_htrfin(Cube cube) 253 | { 254 | uint64_t epe, eps, epm, cp, ep; 255 | 256 | epe = cube.epose % 24; 257 | eps = cube.eposs % 24; 258 | epm = cube.eposm % 24; 259 | ep = (epe * 24 + eps) *24 + epm; 260 | cp = index_cornershtrfin(cube); 261 | 262 | return cp * 24 * 24 * 24 + ep; 263 | } 264 | 265 | static uint64_t 266 | index_cpud_separate(Cube cube) 267 | { 268 | return cpud_separate_ind[cube.cp]; 269 | } 270 | 271 | /* Coordinate movers *********************************************************/ 272 | 273 | static uint64_t 274 | move_eofb(Move m, uint64_t ind) 275 | { 276 | return eofb_mtable[m][ind]; 277 | } 278 | 279 | static uint64_t 280 | move_eofbepos(Move m, uint64_t ind) 281 | { 282 | uint64_t a, b; 283 | 284 | a = epose_mtable[m][(ind / POW2TO11)*24]; 285 | b = eofb_mtable[m][ind % POW2TO11]; 286 | 287 | return (a/24) * POW2TO11 + b; 288 | } 289 | 290 | static uint64_t 291 | move_epud(Move m, uint64_t ind) 292 | { 293 | static int shortlist[NMOVES] = { 294 | [U] = 0, [U2] = 1, [U3] = 2, [D] = 3, [D2] = 4, [D3] = 5, 295 | [R2] = 6, [L2] = 7, [F2] = 8, [B2] = 9 296 | }; 297 | 298 | return move_epud_aux[shortlist[m]][ind]; 299 | } 300 | 301 | static uint64_t 302 | move_coud(Move m, uint64_t ind) 303 | { 304 | return coud_mtable[m][ind]; 305 | } 306 | 307 | static uint64_t 308 | move_corners(Move m, uint64_t ind) 309 | { 310 | uint64_t a, b; 311 | 312 | a = coud_mtable[m][ind / FACTORIAL8]; 313 | b = cp_mtable[m][ind % FACTORIAL8]; 314 | 315 | return a * FACTORIAL8 + b; 316 | } 317 | 318 | static uint64_t 319 | move_cp(Move m, uint64_t ind) 320 | { 321 | return cp_mtable[m][ind]; 322 | } 323 | 324 | static uint64_t 325 | move_cphtr(Move m, uint64_t ind) 326 | { 327 | return move_cphtr_aux[m][ind]; 328 | } 329 | 330 | static uint64_t 331 | move_cornershtr(Move m, uint64_t ind) 332 | { 333 | uint64_t a, b; 334 | 335 | a = coud_mtable[m][ind/(BINOM8ON4 * 6)]; 336 | b = move_cphtr(m, ind % (BINOM8ON4 * 6)); 337 | 338 | return a * BINOM8ON4 * 6 + b; 339 | } 340 | 341 | static uint64_t 342 | move_cornershtrfin(Move m, uint64_t ind) 343 | { 344 | int a; 345 | 346 | a = cp_mtable[m][cornershtrfin_ant[ind]]; 347 | 348 | return cornershtrfin_ind[a]; 349 | } 350 | 351 | static uint64_t 352 | move_drud(Move m, uint64_t ind) 353 | { 354 | uint64_t a, b, c; 355 | 356 | a = eofb_mtable[m][ind % POW2TO11]; 357 | b = coud_mtable[m][(ind / POW2TO11) % POW3TO7]; 358 | c = epose_mtable[m][ind / (POW2TO11 * POW3TO7)]; 359 | 360 | return a + (b + c * POW3TO7) * POW2TO11; 361 | } 362 | 363 | static uint64_t 364 | move_drud_eofb(Move m, uint64_t ind) 365 | { 366 | uint64_t a, b; 367 | 368 | a = coud_mtable[m][ind % POW3TO7]; 369 | b = epose_mtable[m][(ind / POW3TO7) * 24] / 24; 370 | 371 | return a + b * POW3TO7; 372 | } 373 | 374 | static uint64_t 375 | move_htr_drud(Move m, uint64_t ind) 376 | { 377 | uint64_t a, b; 378 | 379 | a = move_cphtr(m, ind/BINOM8ON4); 380 | b = eposs_mtable[m][htr_eposs_ant[ind%BINOM8ON4]]; 381 | 382 | return a*BINOM8ON4 + htr_eposs_ind[b/24]; 383 | } 384 | 385 | static uint64_t 386 | move_htrfin(Move m, uint64_t ind) 387 | { 388 | uint64_t a, b, bm, bs, be; 389 | 390 | a = move_cornershtrfin(m, ind / (24*24*24)); 391 | bm = eposm_mtable[m][ind%24] % 24; 392 | bs = eposs_mtable[m][(ind/24)%24] % 24; 393 | be = epose_mtable[m][(ind/(24*24))%24] % 24; 394 | b = (be * 24 + bs) * 24 + bm; 395 | 396 | return a * (24*24*24) + b; 397 | } 398 | 399 | static uint64_t 400 | move_cpud_separate(Move m, uint64_t ind) 401 | { 402 | return cpud_separate_ind[cp_mtable[m][cpud_separate_ant[ind]]]; 403 | } 404 | 405 | /* Init functions implementation *********************************************/ 406 | 407 | /* 408 | * There is certainly a better way to do this, but for now I just use 409 | * a "graph coloring" algorithm to compute the left cosets, and I compose 410 | * with every possible cp to get the right cosets (it is possible that I am 411 | * mixing up left and right). 412 | * 413 | * For doing it better "Mathematically", we need 3 things: 414 | * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other 415 | * This is easy and it is done in the commented function cphtr_cp(). 416 | * - Check that there is no ep/cp parity 417 | * - Check that we are not in the "3c" case; this is the part I don't 418 | * know how to do. 419 | */ 420 | static void 421 | init_cphtr_cosets(void) 422 | { 423 | unsigned int i; 424 | int c = 0, d = 0; 425 | 426 | for (i = 0; i < FACTORIAL8; i++) { 427 | cphtr_left_cosets[i] = -1; 428 | cphtr_right_cosets[i] = -1; 429 | } 430 | 431 | /* First we compute left cosets with a bfs */ 432 | for (i = 0; i < FACTORIAL8; i++) 433 | if (cphtr_left_cosets[i] == -1) 434 | init_cphtr_left_cosets_bfs(i, c++); 435 | 436 | /* Then we compute right cosets using compose() */ 437 | for (i = 0; i < FACTORIAL8; i++) 438 | if (cphtr_right_cosets[i] == -1) 439 | init_cphtr_right_cosets_color(i, d++); 440 | } 441 | 442 | static void 443 | init_cphtr_left_cosets_bfs(int i, int c) 444 | { 445 | int j, jj, next[FACTORIAL8], next2[FACTORIAL8], n, n2; 446 | 447 | Move k; 448 | 449 | n = 1; 450 | next[0] = i; 451 | cphtr_left_cosets[i] = c; 452 | 453 | while (n != 0) { 454 | for (j = 0, n2 = 0; j < n; j++) { 455 | for (k = U2; k < B3; k++) { 456 | if (!moveset_htr.allowed(k)) 457 | continue; 458 | jj = apply_move(k, (Cube){ .cp = next[j] }).cp; 459 | 460 | if (cphtr_left_cosets[jj] == -1) { 461 | cphtr_left_cosets[jj] = c; 462 | next2[n2++] = jj; 463 | } 464 | } 465 | } 466 | 467 | for (j = 0; j < n2; j++) 468 | next[j] = next2[j]; 469 | n = n2; 470 | } 471 | } 472 | 473 | static void 474 | init_cphtr_right_cosets_color(int i, int d) 475 | { 476 | int cp; 477 | unsigned int j; 478 | 479 | cphtr_right_rep[d] = i; 480 | for (j = 0; j < FACTORIAL8; j++) { 481 | if (cphtr_left_cosets[j] == 0) { 482 | cp = compose_filtered( 483 | (Cube){.cp = i}, (Cube){.cp = j}, pf_cp).cp; 484 | cphtr_right_cosets[cp] = d; 485 | } 486 | } 487 | } 488 | 489 | static void 490 | init_cpud_separate(void) 491 | { 492 | unsigned int ui; 493 | int i, co[8]; 494 | 495 | for (ui = 0; ui < FACTORIAL8; ui++) { 496 | for (i = 0; i < 8; i++) 497 | co[i] = what_corner_at((Cube){.cp=ui},i)>UBR ? 1 : 0; 498 | cpud_separate_ind[ui] = subset_to_index(co, 8, 4); 499 | cpud_separate_ant[cpud_separate_ind[ui]] = ui; 500 | } 501 | } 502 | 503 | static void 504 | init_cornershtrfin(void) 505 | { 506 | unsigned int i, j; 507 | int n, c; 508 | Move m; 509 | 510 | for (i = 0; i < FACTORIAL8; i++) 511 | cornershtrfin_ind[i] = -1; 512 | cornershtrfin_ind[0] = 0; 513 | 514 | /* 10-pass, I think 5 is enough, but just in case */ 515 | n = 1; 516 | for (i = 0; i < 10; i++) { 517 | for (j = 0; j < FACTORIAL8; j++) { 518 | if (cornershtrfin_ind[j] == -1) 519 | continue; 520 | for (m = U; m < NMOVES; m++) { 521 | if (moveset_htr.allowed(m)) { 522 | c = cp_mtable[m][j]; 523 | if (cornershtrfin_ind[c] == -1) { 524 | cornershtrfin_ind[c] = n; 525 | cornershtrfin_ant[n] = c; 526 | n++; 527 | } 528 | } 529 | } 530 | } 531 | } 532 | } 533 | 534 | void 535 | init_htr_eposs(void) 536 | { 537 | int ep[12], ep2[12]; 538 | int eps_solved[4] = {UL, UR, DL, DR}; 539 | unsigned int i, j; 540 | 541 | /* NOTE: we loop over all possible positions of S-slice edges, * 542 | * although some of them are not possible (because the E-slice * 543 | * edges are already in the E-slice). Then we discard the invalid * 544 | * configurations by checking that the value returned by * 545 | * subset_to_index() is acceptable. */ 546 | for (i = 0; i < BINOM12ON4; i++) { 547 | for (j = 0; j < 12; j++) 548 | ep[j] = ep2[j] = 0; 549 | epos_to_partial_ep(i*24, ep, eps_solved); 550 | for (j = 0; j < 8; j++) 551 | ep2[j/2 + 4*(j%2)] = ep[j] ? 1 : 0; 552 | htr_eposs_ind[i] = subset_to_index(ep2, 8, 4); 553 | if (htr_eposs_ind[i] < (int)BINOM8ON4) 554 | htr_eposs_ant[htr_eposs_ind[i]] = i*24; 555 | } 556 | } 557 | 558 | static void 559 | init_move_epud(void) 560 | { 561 | /* TODO: save to file? */ 562 | static int a[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 }; 563 | static int shortlist[NMOVES] = { 564 | [U] = 0, [U2] = 1, [U3] = 2, [D] = 3, [D2] = 4, [D3] = 5, 565 | [R2] = 6, [L2] = 7, [F2] = 8, [B2] = 9 566 | }; 567 | uint64_t ui; 568 | int j; 569 | Move mj; 570 | Cube c; 571 | CubeArray *arr, *auxarr; 572 | 573 | auxarr = malloc(sizeof(CubeArray)); 574 | auxarr->ep = a; 575 | for (ui = 0; ui < coord_epud.max; ui++) { 576 | index_to_perm(ui, 8, a); 577 | c = arrays_to_cube(auxarr, pf_ep); 578 | for (j = 0; moveset_drud.sorted_moves[j] != NULLMOVE; 579 | j++) { 580 | mj = moveset_drud.sorted_moves[j]; 581 | arr = new_cubearray(apply_move(mj, c), pf_ep); 582 | move_epud_aux[shortlist[mj]][ui] = 583 | perm_to_index(arr->ep, 8); 584 | free_cubearray(arr, pf_ep); 585 | } 586 | } 587 | free(auxarr); 588 | } 589 | 590 | static void 591 | init_move_cphtr(void) 592 | { 593 | uint64_t ui; 594 | Move j; 595 | 596 | for (ui = 0; ui < BINOM8ON4*6; ui++) 597 | for (j = U; j < NMOVES; j++) 598 | move_cphtr_aux[j][ui] = cphtr_right_cosets[ 599 | cp_mtable[j][cphtr_right_rep[ui]]]; 600 | } 601 | 602 | 603 | void 604 | init_coord(void) 605 | { 606 | static bool initialized = false; 607 | if (initialized) 608 | return; 609 | initialized = true; 610 | 611 | init_trans(); 612 | 613 | init_cphtr_cosets(); 614 | init_cornershtrfin(); 615 | init_htr_eposs(); 616 | init_cpud_separate(); 617 | 618 | init_move_epud(); 619 | init_move_cphtr(); 620 | } 621 | 622 | -------------------------------------------------------------------------------- /src/moves.c: -------------------------------------------------------------------------------- 1 | #include "moves.h" 2 | 3 | /* Local functions ***********************************************************/ 4 | 5 | static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); 6 | static void cleanup_aux(Alg *alg, Alg *ret, bool inv); 7 | static bool read_mtables_file(void); 8 | static bool write_mtables_file(void); 9 | 10 | /* Tables and other data *****************************************************/ 11 | 12 | /* Every move is translated to a an alg before filling the 13 | transition tables, see init_moves() */ 14 | 15 | static int edge_cycle[NMOVES][12] = 16 | { 17 | [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, 18 | [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, 19 | [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL } 20 | }; 21 | 22 | static int corner_cycle[NMOVES][8] = 23 | { 24 | [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR }, 25 | [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR }, 26 | [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL } 27 | }; 28 | 29 | static int center_cycle[NMOVES][6] = 30 | { 31 | [x] = { F_center, B_center, R_center, L_center, D_center, U_center }, 32 | [y] = { U_center, D_center, B_center, F_center, R_center, L_center } 33 | }; 34 | 35 | static int eofb_flipped[NMOVES][12] = { 36 | [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, 37 | [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } 38 | }; 39 | 40 | static int eorl_flipped[NMOVES][12] = { 41 | [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 42 | [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } 43 | }; 44 | 45 | static int eoud_flipped[NMOVES][12] = { 46 | [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, 47 | [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, 48 | [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } 49 | }; 50 | 51 | static int coud_flipped[NMOVES][8] = { 52 | [x] = { 53 | [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, 54 | [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 55 | } 56 | }; 57 | 58 | static int corl_flipped[NMOVES][8] = { 59 | [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, 60 | [y] = { 61 | [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, 62 | [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 63 | } 64 | }; 65 | 66 | static int cofb_flipped[NMOVES][8] = { 67 | [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, 68 | [x] = { 69 | [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, 70 | [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 71 | }, 72 | [y] = { 73 | [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1, 74 | [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2 75 | } 76 | }; 77 | 78 | static char equiv_alg_string[100][NMOVES] = { 79 | [NULLMOVE] = "", 80 | 81 | [U] = " U ", 82 | [U2] = " UU ", 83 | [U3] = " UUU ", 84 | [D] = " xx U xx ", 85 | [D2] = " xx UU xx ", 86 | [D3] = " xx UUU xx ", 87 | [R] = " yx U xxxyyy ", 88 | [R2] = " yx UU xxxyyy ", 89 | [R3] = " yx UUU xxxyyy ", 90 | [L] = " yyyx U xxxy ", 91 | [L2] = " yyyx UU xxxy ", 92 | [L3] = " yyyx UUU xxxy ", 93 | [F] = " x U xxx ", 94 | [F2] = " x UU xxx ", 95 | [F3] = " x UUU xxx ", 96 | [B] = " xxx U x ", 97 | [B2] = " xxx UU x ", 98 | [B3] = " xxx UUU x ", 99 | 100 | [Uw] = " xx U xx y ", 101 | [Uw2] = " xx UU xx yy ", 102 | [Uw3] = " xx UUU xx yyy ", 103 | [Dw] = " U yyy ", 104 | [Dw2] = " UU yy ", 105 | [Dw3] = " UUU y ", 106 | [Rw] = " yyyx U xxxy x ", 107 | [Rw2] = " yyyx UU xxxy xx ", 108 | [Rw3] = " yyyx UUU xxxy xxx ", 109 | [Lw] = " yx U xxxyyy xxx ", 110 | [Lw2] = " yx UU xxxyyy xx ", 111 | [Lw3] = " yx UUU xxxyyy x ", 112 | [Fw] = " xxx U x yxxxyyy ", 113 | [Fw2] = " xxx UU x yxxyyy ", 114 | [Fw3] = " xxx UUU x yxyyy ", 115 | [Bw] = " x U xxx yxyyy ", 116 | [Bw2] = " x UU xxx yxxyyy ", 117 | [Bw3] = " x UUU xxx yxxxyyy ", 118 | 119 | [M] = " yx U xx UUU yxyyy ", 120 | [M2] = " yx UU xx UU xxxy ", 121 | [M3] = " yx UUU xx U yxxxy ", 122 | [S] = " x UUU xx U yyyx ", 123 | [S2] = " x UU xx UU yyx ", 124 | [S3] = " x U xx UUU yx ", 125 | [E] = " U xx UUU xxyyy ", 126 | [E2] = " UU xx UU xxyy ", 127 | [E3] = " UUU xx U xxy ", 128 | 129 | [x] = " x ", 130 | [x2] = " xx ", 131 | [x3] = " xxx ", 132 | [y] = " y ", 133 | [y2] = " yy ", 134 | [y3] = " yyy ", 135 | [z] = " yyy x y ", 136 | [z2] = " yy xx ", 137 | [z3] = " y x yyy " 138 | }; 139 | 140 | /* Transition tables, to be loaded up at the beginning */ 141 | int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; 142 | int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; 143 | int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; 144 | int eofb_mtable[NMOVES][POW2TO11]; 145 | int eorl_mtable[NMOVES][POW2TO11]; 146 | int eoud_mtable[NMOVES][POW2TO11]; 147 | int cp_mtable[NMOVES][FACTORIAL8]; 148 | int coud_mtable[NMOVES][POW3TO7]; 149 | int cofb_mtable[NMOVES][POW3TO7]; 150 | int corl_mtable[NMOVES][POW3TO7]; 151 | int cpos_mtable[NMOVES][FACTORIAL6]; 152 | 153 | 154 | /* Local functions implementation ********************************************/ 155 | 156 | static Cube 157 | apply_move_cubearray(Move m, Cube cube, PieceFilter f) 158 | { 159 | /*init_moves();*/ 160 | 161 | CubeArray m_arr = { 162 | edge_cycle[m], 163 | eofb_flipped[m], 164 | eorl_flipped[m], 165 | eoud_flipped[m], 166 | corner_cycle[m], 167 | coud_flipped[m], 168 | corl_flipped[m], 169 | cofb_flipped[m], 170 | center_cycle[m] 171 | }; 172 | 173 | return move_via_arrays(&m_arr, cube, f); 174 | } 175 | 176 | /* Public functions **********************************************************/ 177 | 178 | Cube 179 | apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) 180 | { 181 | Cube ret = {0}; 182 | int i; 183 | 184 | for (i = 0; i < alg->len; i++) 185 | if (alg->inv[i]) 186 | ret = a ? apply_move(alg->move[i], ret) : 187 | apply_move_cubearray(alg->move[i], ret, f); 188 | 189 | ret = compose_filtered(c, inverse_cube(ret), f); 190 | 191 | for (i = 0; i < alg->len; i++) 192 | if (!alg->inv[i]) 193 | ret = a ? apply_move(alg->move[i], ret) : 194 | apply_move_cubearray(alg->move[i], ret, f); 195 | 196 | return ret; 197 | } 198 | 199 | Cube 200 | apply_alg(Alg *alg, Cube cube) 201 | { 202 | return apply_alg_generic(alg, cube, pf_all, true); 203 | } 204 | 205 | Cube 206 | apply_move(Move m, Cube cube) 207 | { 208 | /*init_moves();*/ 209 | 210 | return (Cube) { 211 | .epose = epose_mtable[m][cube.epose], 212 | .eposs = eposs_mtable[m][cube.eposs], 213 | .eposm = eposm_mtable[m][cube.eposm], 214 | .eofb = eofb_mtable[m][cube.eofb], 215 | .eorl = eorl_mtable[m][cube.eorl], 216 | .eoud = eoud_mtable[m][cube.eoud], 217 | .coud = coud_mtable[m][cube.coud], 218 | .cofb = cofb_mtable[m][cube.cofb], 219 | .corl = corl_mtable[m][cube.corl], 220 | .cp = cp_mtable[m][cube.cp], 221 | .cpos = cpos_mtable[m][cube.cpos] 222 | }; 223 | } 224 | 225 | Alg * 226 | cleanup(Alg *alg) 227 | { 228 | int i, j, k, b[2], n, L; 229 | Move bb, m; 230 | Alg *ret; 231 | 232 | ret = new_alg(""); 233 | cleanup_aux(alg, ret, false); 234 | cleanup_aux(alg, ret, true); 235 | 236 | do { 237 | for (i = 0, j = 0, n = 0; i < ret->len; i = j) { 238 | if (ret->move[i] > B3) { 239 | ret->move[n] = ret->move[i]; 240 | ret->inv[n] = ret->inv[i]; 241 | n++; 242 | j++; 243 | continue; 244 | } 245 | 246 | bb = 1 + ((base_move(ret->move[i]) - 1)/6)*6; 247 | while (j < ret->len && 248 | ret->move[j] <= B3 && 249 | ret->inv[j] == ret->inv[i] && 250 | 1 + ((base_move(ret->move[j]) - 1)/6)*6 == bb) 251 | j++; 252 | 253 | for (k = i, b[0] = 0, b[1] = 0; k < j; k++) { 254 | m = ret->move[k]; 255 | if (base_move(m) == bb) 256 | b[0] = (b[0]+1+m-base_move(m)) % 4; 257 | else 258 | b[1] = (b[1]+1+m-base_move(m)) % 4; 259 | } 260 | 261 | for (k = 0; k < 2; k++) { 262 | if (b[k] != 0) { 263 | ret->move[n] = bb + b[k] - 1 + 3*k; 264 | ret->inv[n] = ret->inv[i]; 265 | n++; 266 | } 267 | } 268 | } 269 | 270 | L = ret->len; 271 | ret->len = n; 272 | } while (L != n); 273 | 274 | return ret; 275 | } 276 | 277 | static void 278 | cleanup_aux(Alg *alg, Alg *ret, bool inv) 279 | { 280 | int i, j; 281 | Cube c, d; 282 | Move m, mm; 283 | Alg *equiv_alg; 284 | 285 | c = (Cube){0}; 286 | for (i = 0; i < alg->len; i++) { 287 | if (alg->inv[i] != inv) 288 | continue; 289 | 290 | equiv_alg = new_alg(equiv_alg_string[alg->move[i]]); 291 | 292 | for (j = 0; j < equiv_alg->len; j++) { 293 | m = equiv_alg->move[j]; 294 | if (m == U) { 295 | mm = 3*what_center_at(c, U_center) + 1; 296 | append_move(ret, mm, inv); 297 | } else { 298 | c = apply_move(m, c); 299 | } 300 | } 301 | 302 | free_alg(equiv_alg); 303 | } 304 | 305 | m = NULLMOVE; 306 | switch (what_center_at(c, F_center)) { 307 | case U_center: 308 | m = x3; 309 | break; 310 | case D_center: 311 | m = x; 312 | break; 313 | case R_center: 314 | m = y; 315 | break; 316 | case L_center: 317 | m = y3; 318 | break; 319 | case B_center: 320 | if (what_center_at(c, U_center) == U_center) 321 | m = y2; 322 | else 323 | m = x2; 324 | break; 325 | default: 326 | break; 327 | } 328 | d = apply_move(m, (Cube){0}); 329 | if (m != NULLMOVE) 330 | append_move(ret, m, inv); 331 | 332 | m = NULLMOVE; 333 | if (what_center_at(c, U_center) == what_center_at(d, D_center)) { 334 | m = z2; 335 | } else if (what_center_at(c, U_center) == what_center_at(d, R_center)) { 336 | m = z3; 337 | } else if (what_center_at(c, U_center) == what_center_at(d, L_center)) { 338 | m = z; 339 | } 340 | if (m != NULLMOVE) 341 | append_move(ret, m, inv); 342 | } 343 | 344 | static bool 345 | read_mtables_file(void) 346 | { 347 | init_env(); 348 | 349 | FILE *f; 350 | char fname[strlen(tabledir)+20]; 351 | int m, b = sizeof(int); 352 | bool r = true; 353 | 354 | /* Table sizes, used for reading and writing files */ 355 | uint64_t me[11] = { 356 | [0] = FACTORIAL12/FACTORIAL8, 357 | [1] = FACTORIAL12/FACTORIAL8, 358 | [2] = FACTORIAL12/FACTORIAL8, 359 | [3] = POW2TO11, 360 | [4] = POW2TO11, 361 | [5] = POW2TO11, 362 | [6] = FACTORIAL8, 363 | [7] = POW3TO7, 364 | [8] = POW3TO7, 365 | [9] = POW3TO7, 366 | [10] = FACTORIAL6 367 | }; 368 | 369 | strcpy(fname, tabledir); 370 | strcat(fname, "/mtables"); 371 | 372 | if ((f = fopen(fname, "rb")) == NULL) 373 | return false; 374 | 375 | for (m = 0; m < NMOVES; m++) { 376 | r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; 377 | r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; 378 | r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; 379 | r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; 380 | r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; 381 | r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; 382 | r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; 383 | r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; 384 | r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; 385 | r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; 386 | r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; 387 | } 388 | 389 | fclose(f); 390 | return r; 391 | } 392 | 393 | static bool 394 | write_mtables_file(void) 395 | { 396 | init_env(); 397 | 398 | FILE *f; 399 | char fname[strlen(tabledir)+20]; 400 | int m, b = sizeof(int); 401 | bool r = true; 402 | 403 | /* Table sizes, used for reading and writing files */ 404 | uint64_t me[11] = { 405 | [0] = FACTORIAL12/FACTORIAL8, 406 | [1] = FACTORIAL12/FACTORIAL8, 407 | [2] = FACTORIAL12/FACTORIAL8, 408 | [3] = POW2TO11, 409 | [4] = POW2TO11, 410 | [5] = POW2TO11, 411 | [6] = FACTORIAL8, 412 | [7] = POW3TO7, 413 | [8] = POW3TO7, 414 | [9] = POW3TO7, 415 | [10] = FACTORIAL6 416 | }; 417 | 418 | strcpy(fname, tabledir); 419 | strcat(fname, "/mtables"); 420 | 421 | if ((f = fopen(fname, "wb")) == NULL) 422 | return false; 423 | 424 | for (m = 0; m < NMOVES; m++) { 425 | r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; 426 | r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; 427 | r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; 428 | r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; 429 | r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; 430 | r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; 431 | r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; 432 | r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; 433 | r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; 434 | r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; 435 | r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; 436 | } 437 | 438 | fclose(f); 439 | return r; 440 | } 441 | 442 | void 443 | init_moves(void) 444 | { 445 | static bool initialized = false; 446 | if (initialized) 447 | return; 448 | initialized = true; 449 | 450 | Cube c; 451 | CubeArray arrs; 452 | int i; 453 | unsigned int ui; 454 | Move m; 455 | Alg *equiv_alg[NMOVES]; 456 | 457 | init_cube(); 458 | 459 | for (i = 0; i < NMOVES; i++) 460 | equiv_alg[i] = new_alg(equiv_alg_string[i]); 461 | 462 | /* Generate all move cycles and flips; I do this regardless */ 463 | for (i = 0; i < NMOVES; i++) { 464 | if (i == U || i == x || i == y) 465 | continue; 466 | 467 | c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); 468 | 469 | arrs = (CubeArray) { 470 | edge_cycle[i], 471 | eofb_flipped[i], 472 | eorl_flipped[i], 473 | eoud_flipped[i], 474 | corner_cycle[i], 475 | coud_flipped[i], 476 | corl_flipped[i], 477 | cofb_flipped[i], 478 | center_cycle[i] 479 | }; 480 | cube_to_arrays(c, &arrs, pf_all); 481 | } 482 | 483 | if (read_mtables_file()) 484 | goto init_moves_end; 485 | 486 | fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); 487 | 488 | /* Initialize transition tables */ 489 | for (m = 0; m < NMOVES; m++) { 490 | for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { 491 | c = (Cube){ .epose = ui }; 492 | c = apply_move_cubearray(m, c, pf_e); 493 | epose_mtable[m][ui] = c.epose; 494 | 495 | c = (Cube){ .eposs = ui }; 496 | c = apply_move_cubearray(m, c, pf_s); 497 | eposs_mtable[m][ui] = c.eposs; 498 | 499 | c = (Cube){ .eposm = ui }; 500 | c = apply_move_cubearray(m, c, pf_m); 501 | eposm_mtable[m][ui] = c.eposm; 502 | } 503 | for (ui = 0; ui < POW2TO11; ui++ ) { 504 | c = (Cube){ .eofb = ui }; 505 | c = apply_move_cubearray(m, c, pf_eo); 506 | eofb_mtable[m][ui] = c.eofb; 507 | 508 | c = (Cube){ .eorl = ui }; 509 | c = apply_move_cubearray(m, c, pf_eo); 510 | eorl_mtable[m][ui] = c.eorl; 511 | 512 | c = (Cube){ .eoud = ui }; 513 | c = apply_move_cubearray(m, c, pf_eo); 514 | eoud_mtable[m][ui] = c.eoud; 515 | } 516 | for (ui = 0; ui < POW3TO7; ui++) { 517 | c = (Cube){ .coud = ui }; 518 | c = apply_move_cubearray(m, c, pf_co); 519 | coud_mtable[m][ui] = c.coud; 520 | 521 | c = (Cube){ .corl = ui }; 522 | c = apply_move_cubearray(m, c, pf_co); 523 | corl_mtable[m][ui] = c.corl; 524 | 525 | c = (Cube){ .cofb = ui }; 526 | c = apply_move_cubearray(m, c, pf_co); 527 | cofb_mtable[m][ui] = c.cofb; 528 | } 529 | for (ui = 0; ui < FACTORIAL8; ui++) { 530 | c = (Cube){ .cp = ui }; 531 | c = apply_move_cubearray(m, c, pf_cp); 532 | cp_mtable[m][ui] = c.cp; 533 | } 534 | for (ui = 0; ui < FACTORIAL6; ui++) { 535 | c = (Cube){ .cpos = ui }; 536 | c = apply_move_cubearray(m, c, pf_cpos); 537 | cpos_mtable[m][ui] = c.cpos; 538 | } 539 | } 540 | 541 | if (!write_mtables_file()) 542 | fprintf(stderr, "Error writing mtables\n"); 543 | 544 | init_moves_end: 545 | for (i = 0; i < NMOVES; i++) 546 | free_alg(equiv_alg[i]); 547 | } 548 | 549 | -------------------------------------------------------------------------------- /src/alg.c: -------------------------------------------------------------------------------- 1 | #include "alg.h" 2 | 3 | /* Local functions ***********************************************************/ 4 | 5 | static bool allowed_HTM(Move m); 6 | static bool allowed_URF(Move m); 7 | static bool allowed_eofb(Move m); 8 | static bool allowed_drud(Move m); 9 | static bool allowed_htr(Move m); 10 | static bool allowed_next_HTM(Move l2, Move l1, Move m); 11 | static int axis(Move m); 12 | 13 | static void free_alglistnode(AlgListNode *aln); 14 | static void realloc_alg(Alg *alg, int n); 15 | 16 | static int niss_type(Alg *a); 17 | static void find_last_moves(Alg *a, bool inv, int *, int *, int *); 18 | static int last_move_pair(Alg *a, bool inv); 19 | static int compare_algs_firstmoves(Alg * a, Alg *b, bool inv); 20 | static int compare_algs(const void * a, const void *b); 21 | 22 | /* Movesets ******************************************************************/ 23 | 24 | Moveset 25 | moveset_HTM = { 26 | .allowed = allowed_HTM, 27 | .allowed_next = allowed_next_HTM, 28 | }; 29 | 30 | Moveset 31 | moveset_URF = { 32 | .allowed = allowed_URF, 33 | .allowed_next = allowed_next_HTM, 34 | }; 35 | 36 | Moveset 37 | moveset_eofb = { 38 | .allowed = allowed_eofb, 39 | .allowed_next = allowed_next_HTM, 40 | }; 41 | 42 | Moveset 43 | moveset_drud = { 44 | .allowed = allowed_drud, 45 | .allowed_next = allowed_next_HTM, 46 | }; 47 | 48 | Moveset 49 | moveset_htr = { 50 | .allowed = allowed_htr, 51 | .allowed_next = allowed_next_HTM, 52 | }; 53 | 54 | static int nmoveset = 5; 55 | static Moveset * all_ms[] = { 56 | &moveset_HTM, 57 | &moveset_URF, 58 | &moveset_eofb, 59 | &moveset_drud, 60 | &moveset_htr, 61 | }; 62 | 63 | /* Functions *****************************************************************/ 64 | 65 | static bool 66 | allowed_HTM(Move m) 67 | { 68 | return m >= U && m <= B3; 69 | } 70 | 71 | static bool 72 | allowed_URF(Move m) 73 | { 74 | Move b = base_move(m); 75 | 76 | return b == U || b == R || b == F; 77 | } 78 | 79 | static bool 80 | allowed_eofb(Move m) 81 | { 82 | Move b = base_move(m); 83 | 84 | return b == U || b == D || b == R || b == L || 85 | ((b == F || b == B) && m == b+1); 86 | } 87 | 88 | static bool 89 | allowed_drud(Move m) 90 | { 91 | Move b = base_move(m); 92 | 93 | return b == U || b == D || 94 | ((b == R || b == L || b == F || b == B) && m == b + 1); 95 | } 96 | 97 | static bool 98 | allowed_htr(Move m) 99 | { 100 | Move b = base_move(m); 101 | 102 | return moveset_HTM.allowed(m) && m == b + 1; 103 | } 104 | 105 | static bool 106 | allowed_next_HTM(Move l2, Move l1, Move m) 107 | { 108 | bool p, q; 109 | 110 | p = l1 != NULLMOVE && base_move(l1) == base_move(m); 111 | q = l2 != NULLMOVE && base_move(l2) == base_move(m); 112 | 113 | return !(p || (commute(l1, l2) && q)); 114 | } 115 | 116 | void 117 | append_alg(AlgList *l, Alg *alg) 118 | { 119 | AlgListNode *node = malloc(sizeof(AlgListNode)); 120 | int i; 121 | 122 | node->alg = new_alg(""); 123 | for (i = 0; i < alg->len; i++) 124 | append_move(node->alg, alg->move[i], alg->inv[i]); 125 | node->next = NULL; 126 | 127 | if (++l->len == 1) 128 | l->first = node; 129 | else 130 | l->last->next = node; 131 | l->last = node; 132 | } 133 | 134 | void 135 | append_move(Alg *alg, Move m, bool inverse) 136 | { 137 | if (alg->len == alg->allocated) 138 | realloc_alg(alg, 2*alg->len); 139 | 140 | alg->move[alg->len] = m; 141 | alg->inv [alg->len] = inverse; 142 | alg->len++; 143 | } 144 | 145 | static int 146 | axis(Move m) 147 | { 148 | if (m == NULLMOVE) 149 | return 0; 150 | 151 | if (m >= U && m <= B3) 152 | return (m-1)/6 + 1; 153 | 154 | if (m >= Uw && m <= Bw3) 155 | return (m-1)/6 - 2; 156 | 157 | if (base_move(m) == E || base_move(m) == y) 158 | return 1; 159 | 160 | if (base_move(m) == M || base_move(m) == x) 161 | return 2; 162 | 163 | if (base_move(m) == S || base_move(m) == z) 164 | return 3; 165 | 166 | return -1; 167 | } 168 | 169 | Move 170 | base_move(Move m) 171 | { 172 | if (m == NULLMOVE) 173 | return NULLMOVE; 174 | else 175 | return m - (m-1)%3; 176 | } 177 | 178 | bool 179 | commute(Move m1, Move m2) 180 | { 181 | return axis(m1) == axis(m2); 182 | } 183 | 184 | void 185 | compose_alg(Alg *alg1, Alg *alg2) 186 | { 187 | int i; 188 | 189 | for (i = 0; i < alg2->len; i++) 190 | append_move(alg1, alg2->move[i], alg2->inv[i]); 191 | } 192 | 193 | void 194 | copy_alg(Alg *src, Alg *dst) 195 | { 196 | dst->len = 0; /* Overwrites */ 197 | compose_alg(dst, src); 198 | } 199 | 200 | void 201 | free_alg(Alg *alg) 202 | { 203 | free(alg->move); 204 | free(alg->inv); 205 | free(alg); 206 | } 207 | 208 | void 209 | free_alglist(AlgList *l) 210 | { 211 | AlgListNode *aux, *i = l->first; 212 | 213 | while (i != NULL) { 214 | aux = i->next; 215 | free_alglistnode(i); 216 | i = aux; 217 | } 218 | free(l); 219 | } 220 | 221 | static void 222 | free_alglistnode(AlgListNode *aln) 223 | { 224 | free_alg(aln->alg); 225 | free(aln); 226 | } 227 | 228 | void 229 | inplace(Alg * (*f)(Alg *), Alg *alg) 230 | { 231 | Alg *aux; 232 | 233 | aux = f(alg); 234 | copy_alg(aux, alg); 235 | free_alg(aux); 236 | } 237 | 238 | Alg * 239 | inverse_alg(Alg *alg) 240 | { 241 | Alg *ret = new_alg(""); 242 | int i; 243 | 244 | for (i = alg->len-1; i >= 0; i--) 245 | append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); 246 | 247 | return ret; 248 | } 249 | 250 | Move 251 | inverse_move(Move m) 252 | { 253 | return m == NULLMOVE ? NULLMOVE : m + 2 - 2*((m-1) % 3); 254 | } 255 | 256 | char * 257 | move_string(Move m) 258 | { 259 | static char move_string_aux[NMOVES][7] = { 260 | [NULLMOVE] = "-", 261 | [U] = "U", [U2] = "U2", [U3] = "U\'", 262 | [D] = "D", [D2] = "D2", [D3] = "D\'", 263 | [R] = "R", [R2] = "R2", [R3] = "R\'", 264 | [L] = "L", [L2] = "L2", [L3] = "L\'", 265 | [F] = "F", [F2] = "F2", [F3] = "F\'", 266 | [B] = "B", [B2] = "B2", [B3] = "B\'", 267 | [Uw] = "Uw", [Uw2] = "Uw2", [Uw3] = "Uw\'", 268 | [Dw] = "Dw", [Dw2] = "Dw2", [Dw3] = "Dw\'", 269 | [Rw] = "Rw", [Rw2] = "Rw2", [Rw3] = "Rw\'", 270 | [Lw] = "Lw", [Lw2] = "Lw2", [Lw3] = "Lw\'", 271 | [Fw] = "Fw", [Fw2] = "Fw2", [Fw3] = "Fw\'", 272 | [Bw] = "Bw", [Bw2] = "Bw2", [Bw3] = "Bw\'", 273 | [M] = "M", [M2] = "M2", [M3] = "M\'", 274 | [E] = "E", [E2] = "E2", [E3] = "E\'", 275 | [S] = "S", [S2] = "S2", [S3] = "S\'", 276 | [x] = "x", [x2] = "x2", [x3] = "x\'", 277 | [y] = "y", [y2] = "y2", [y3] = "y\'", 278 | [z] = "z", [z2] = "z2", [z3] = "z\'", 279 | }; 280 | 281 | return move_string_aux[m]; 282 | } 283 | 284 | Alg * 285 | new_alg(char *str) 286 | { 287 | Alg *alg; 288 | int i; 289 | bool niss, move_read; 290 | Move j, m; 291 | 292 | alg = malloc(sizeof(Alg)); 293 | alg->move = malloc(30 * sizeof(Move)); 294 | alg->inv = malloc(30 * sizeof(bool)); 295 | alg->allocated = 30; 296 | alg->len = 0; 297 | 298 | niss = false; 299 | for (i = 0; str[i]; i++) { 300 | if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') 301 | continue; 302 | 303 | if (str[i] == '(' && niss) { 304 | fprintf(stderr, "Error reading moves: nested ( )\n"); 305 | alg->len = 0; 306 | return alg; 307 | } 308 | 309 | if (str[i] == ')' && !niss) { 310 | fprintf(stderr, "Error reading moves: unmatched )\n"); 311 | alg->len = 0; 312 | return alg; 313 | } 314 | 315 | if (str[i] == '(' || str[i] == ')') { 316 | niss = !niss; 317 | continue; 318 | } 319 | 320 | /* Single slash for comments */ 321 | if (str[i] == '/') { 322 | while (str[i] && str[i] != '\n') 323 | i++; 324 | 325 | if (!str[i]) 326 | i--; 327 | 328 | continue; 329 | } 330 | 331 | move_read = false; 332 | for (j = U; j < NMOVES; j++) { 333 | if (str[i] == move_string(j)[0] || 334 | (str[i] >= 'a' && str[i] <= 'z' && 335 | str[i] == move_string(j)[0]-('A'-'a') && j<=B)) { 336 | m = j; 337 | if (str[i] >= 'a' && str[i] <= 'z' && j<=B) { 338 | m += Uw - U; 339 | } 340 | if (m <= B && str[i+1]=='w') { 341 | m += Uw - U; 342 | i++; 343 | } 344 | if (str[i+1]=='2') { 345 | m += 1; 346 | i++; 347 | } else if (str[i+1] == '\'' || 348 | str[i+1] == '3' || 349 | str[i+1] == '`' ) { 350 | m += 2; 351 | i++; 352 | } else if ((int)str[i+1] == -62 && 353 | (int)str[i+2] == -76) { 354 | /* Weird apostrophe */ 355 | m += 2; 356 | i += 2; 357 | } else if ((int)str[i+1] == -30 && 358 | (int)str[i+2] == -128 && 359 | (int)str[i+3] == -103) { 360 | /* MacOS apostrophe */ 361 | m += 2; 362 | i += 3; 363 | } 364 | append_move(alg, m, niss); 365 | move_read = true; 366 | break; 367 | } 368 | } 369 | 370 | if (!move_read) { 371 | free_alg(alg); 372 | return new_alg(""); 373 | } 374 | } 375 | 376 | if (niss) { 377 | fprintf(stderr, "Error reading moves: unmatched (\n"); 378 | alg->len = 0; 379 | } 380 | 381 | return alg; 382 | } 383 | 384 | AlgList * 385 | new_alglist(void) 386 | { 387 | AlgList *ret = malloc(sizeof(AlgList)); 388 | 389 | ret->len = 0; 390 | ret->first = NULL; 391 | ret->last = NULL; 392 | 393 | return ret; 394 | } 395 | 396 | void 397 | print_alg(Alg *alg, bool l) 398 | { 399 | char fill[4]; 400 | int i; 401 | bool niss = false; 402 | 403 | for (i = 0; i < alg->len; i++) { 404 | if (!niss && alg->inv[i]) 405 | strcpy(fill, i == 0 ? "(" : " ("); 406 | if (niss && !alg->inv[i]) 407 | strcpy(fill, ") "); 408 | if (niss == alg->inv[i]) 409 | strcpy(fill, i == 0 ? "" : " "); 410 | 411 | printf("%s%s", fill, move_string(alg->move[i])); 412 | niss = alg->inv[i]; 413 | } 414 | 415 | if (niss) 416 | printf(")"); 417 | if (l) 418 | printf(" (%d)", alg->len); 419 | 420 | printf("\n"); 421 | } 422 | 423 | void 424 | print_alglist(AlgList *al, bool l) 425 | { 426 | AlgListNode *i; 427 | 428 | for (i = al->first; i != NULL; i = i->next) 429 | print_alg(i->alg, l); 430 | } 431 | 432 | static void 433 | realloc_alg(Alg *alg, int n) 434 | { 435 | if (alg == NULL) { 436 | fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); 437 | return; 438 | } 439 | 440 | if (n < alg->len) { 441 | fprintf(stderr, "Error: alg too long for reallocation "); 442 | fprintf(stderr, "(%d vs %d)\n", alg->len, n); 443 | return; 444 | } 445 | 446 | if (n > 1000000) { 447 | fprintf(stderr, "Warning: very long alg,"); 448 | fprintf(stderr, "something might go wrong.\n"); 449 | } 450 | 451 | alg->move = realloc(alg->move, n * sizeof(int)); 452 | alg->inv = realloc(alg->inv, n * sizeof(int)); 453 | alg->allocated = n; 454 | } 455 | 456 | static int 457 | niss_type(Alg *a) 458 | { 459 | /* 0 if all moves are on normal, 1 if all on inverse, 2 otherwise */ 460 | 461 | int i; 462 | bool found_normal = false, found_inverse = false; 463 | 464 | for (i = 0; i < a->len; i++) { 465 | found_normal = found_normal || !a->inv[i]; 466 | found_inverse = found_inverse || a->inv[i]; 467 | } 468 | 469 | if (found_normal && !found_inverse) 470 | return 0; 471 | if (!found_normal && found_inverse) 472 | return 1; 473 | return 2; 474 | } 475 | 476 | static void 477 | find_last_moves(Alg *a, bool inv, int *n, int *nlast, int *nslast) 478 | { 479 | int i; 480 | 481 | for (i = 0, *n = 0, *nlast = -1, *nslast = -1; i < a->len; i++) { 482 | if (inv == a->inv[i]) { 483 | (*n)++; 484 | *nslast = *nlast; 485 | *nlast = i; 486 | } 487 | } 488 | } 489 | 490 | static int 491 | last_move_pair(Alg *a, bool inv) 492 | { 493 | /* The number of the move in the moves enum, or a higher number 494 | * (working in base NMOVES) if the last two moves are parallel. 495 | * -1 if there are no moves on the specified side of the alg. 496 | */ 497 | 498 | int n, nlast, nslast; 499 | 500 | find_last_moves(a, inv, &n, &nlast, &nslast); 501 | 502 | if (n == 0) 503 | return -1; 504 | if (nlast == 0 || !commute(a->move[nlast], a->move[nslast])) 505 | return a->move[nlast]; 506 | return a->move[nlast] * NMOVES + a->move[nslast]; 507 | } 508 | 509 | static int 510 | compare_algs_firstmoves(Alg *a, Alg *b, bool inv) 511 | { 512 | /* Compare algs up to the last or the last two moves if parallel */ 513 | 514 | int i, j, na, nlasta, nslasta, nb, nlastb, nslastb, ma, mb, m1, m2; 515 | 516 | find_last_moves(a, inv, &na, &nlasta, &nslasta); 517 | find_last_moves(b, inv, &nb, &nlastb, &nslastb); 518 | 519 | ma = na > 0 ? ((na > 1 ? nslasta : nlasta) + 1) : 0; 520 | mb = nb > 0 ? ((nb > 1 ? nslastb : nlastb) + 1) : 0; 521 | 522 | if (ma == 0 && mb == 0) 523 | return 0; 524 | if (ma == 0) 525 | return -1; 526 | if (mb == 0) 527 | return 1; 528 | 529 | for (i = 0, j = 0; i < ma && j < mb; i++, j++) { 530 | while (a->inv[i] != inv) i++; 531 | while (a->inv[j] != inv) j++; 532 | m1 = a->move[i]; 533 | m2 = b->move[j]; 534 | if (m1 - m2) 535 | return m1 - m2; 536 | } 537 | 538 | return ma - mb; 539 | } 540 | 541 | static int 542 | compare_algs(const void *avoid, const void *bvoid) 543 | { 544 | /* We sort a list of algs in a way that makes the most sense for the 545 | * commands and steps where one usually wants many results, for 546 | * example EO and DR. We sort by, in order: 547 | * 1. Length of the solution 548 | * 2. NISS type (first algs that are all on normal, then those that 549 | * are all on inverse, then those that use NISS) 550 | * 3. Last move on normal, or last two moves if they are parallel 551 | * 4. All other moves on normal scramble 552 | * 5. Last move on inverse, or last two moves if they are parallel 553 | * 6. All other moves on inverse 554 | */ 555 | 556 | Alg *a = *(Alg **)avoid; 557 | Alg *b = *(Alg **)bvoid; 558 | 559 | int ntype_a, ntype_b, last_a, last_b, cmp; 560 | 561 | /* 1. Compare length */ 562 | if (a->len - b->len) 563 | return a->len - b->len; 564 | 565 | /* 2. Algs have the same length, compare NISS type */ 566 | ntype_a = niss_type(a); 567 | ntype_b = niss_type(b); 568 | if (ntype_a - ntype_b) 569 | return ntype_a - ntype_b; 570 | 571 | /* 3. Algs have same NISS type, compare last moves on normal */ 572 | last_a = last_move_pair(a, false); 573 | last_b = last_move_pair(b, false); 574 | if (last_a - last_b) 575 | return last_a - last_b; 576 | 577 | /* 4. Algs have same last moves on normal, compare all other moves */ 578 | cmp = compare_algs_firstmoves(a, b, false); 579 | if (cmp) 580 | return cmp; 581 | 582 | /* 5. Algs have same moves on normal, compare last on inverse */ 583 | last_a = last_move_pair(a, true); 584 | last_b = last_move_pair(b, true); 585 | if (last_a - last_b) 586 | return last_a - last_b; 587 | 588 | /* 6. Algs have same last moves on inverse, compare other */ 589 | cmp = compare_algs_firstmoves(a, b, true); 590 | if (cmp) 591 | return cmp; 592 | 593 | /* Algs are equal */ 594 | return 0; 595 | } 596 | 597 | void 598 | sort_alglist(AlgList *al) 599 | { 600 | int i, n = al->len; 601 | Alg* alg_array[n]; 602 | AlgListNode *node; 603 | 604 | for (i = 0, node = al->first; i < n; i++, node = node->next) 605 | alg_array[i] = node->alg; 606 | 607 | qsort(alg_array, n, sizeof(Alg *), &compare_algs); 608 | 609 | for (i = 0, node = al->first; i < n; i++, node = node->next) 610 | node->alg = alg_array[i]; 611 | } 612 | 613 | void 614 | swapmove(Move *m1, Move *m2) 615 | { 616 | Move aux; 617 | 618 | aux = *m1; 619 | *m1 = *m2; 620 | *m2 = aux; 621 | } 622 | 623 | Alg * 624 | unniss(Alg *alg) 625 | { 626 | int i; 627 | Alg *ret; 628 | 629 | ret = new_alg(""); 630 | 631 | for (i = 0; i < alg->len; i++) 632 | if (!alg->inv[i]) 633 | append_move(ret, alg->move[i], false); 634 | 635 | for (i = alg->len-1; i >= 0; i--) 636 | if (alg->inv[i]) 637 | append_move(ret, inverse_move(alg->move[i]), false); 638 | 639 | return ret; 640 | } 641 | 642 | void 643 | init_moveset(Moveset *ms) 644 | { 645 | int j; 646 | uint64_t l, one; 647 | Move m, l2, l1; 648 | 649 | one = 1; 650 | 651 | for (j = 0, m = U; m < NMOVES; m++) 652 | if (ms->allowed(m)) 653 | ms->sorted_moves[j++] = m; 654 | ms->sorted_moves[j] = NULLMOVE; 655 | 656 | for (l1 = 0; l1 < NMOVES; l1++) { 657 | for (l2 = 0; l2 < NMOVES; l2++) { 658 | ms->mask[l2][l1] = 0; 659 | for (l=0; ms->sorted_moves[l]!=NULLMOVE; l++) { 660 | m = ms->sorted_moves[l]; 661 | if (ms->allowed_next(l2, l1, m)) 662 | ms->mask[l2][l1] |= (one<generated) { 338 | free(sd->class); 339 | free(sd->unsym); 340 | free(sd->transtorep); 341 | } 342 | 343 | sd->generated = false; 344 | } 345 | 346 | static void 347 | gensym(SymData *sd) 348 | { 349 | uint64_t i, in, nreps = 0; 350 | Trans t; 351 | int j; 352 | 353 | if (sd->generated) 354 | return; 355 | 356 | sd->class = malloc(sd->coord->max * sizeof(uint64_t)); 357 | sd->unsym = malloc(sd->coord->max * sizeof(uint64_t)); 358 | sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); 359 | sd->selfsim = malloc(sd->coord->max * sizeof(uint64_t)); 360 | 361 | if (read_symdata_file(sd)) { 362 | sd->generated = true; 363 | return; 364 | } 365 | 366 | fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); 367 | 368 | for (i = 0; i < sd->coord->max; i++) 369 | sd->class[i] = sd->coord->max + 1; 370 | 371 | for (i = 0; i < sd->coord->max; i++) { 372 | if (sd->class[i] == sd->coord->max + 1) { 373 | sd->unsym[nreps] = i; 374 | sd->transtorep[i] = uf; 375 | sd->selfsim[nreps] = (uint64_t)0; 376 | for (j = 0; j < sd->ntrans; j++) { 377 | t = sd->trans[j]; 378 | in = sd->transform(t, i); 379 | sd->class[in] = nreps; 380 | if (in == i) 381 | sd->selfsim[nreps] |= 382 | ((uint64_t)1 << t); 383 | else 384 | sd->transtorep[in] = inverse_trans(t); 385 | } 386 | nreps++; 387 | } 388 | } 389 | 390 | sd->sym_coord->max = nreps; 391 | sd->unsym = realloc(sd->unsym, nreps * sizeof(uint64_t)); 392 | sd->selfsim = realloc(sd->selfsim, nreps * sizeof(uint64_t)); 393 | sd->generated = true; 394 | 395 | fprintf(stderr, "Found %" PRIu64 " classes\n", nreps); 396 | 397 | if (!write_symdata_file(sd)) 398 | fprintf(stderr, "Error writing SymData file\n"); 399 | 400 | return; 401 | } 402 | 403 | static bool 404 | read_symdata_file(SymData *sd) 405 | { 406 | init_env(); 407 | 408 | FILE *f; 409 | char fname[strlen(tabledir)+100]; 410 | uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; 411 | bool r = true; 412 | 413 | strcpy(fname, tabledir); 414 | strcat(fname, "/"); 415 | strcat(fname, sd->filename); 416 | 417 | if ((f = fopen(fname, "rb")) == NULL) 418 | return false; 419 | 420 | r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; 421 | r = r && fread(sd->unsym, sizeof(uint64_t), *sn, f) == *sn; 422 | r = r && fread(sd->selfsim, sizeof(uint64_t), *sn, f) == *sn; 423 | r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; 424 | r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; 425 | 426 | fclose(f); 427 | return r; 428 | } 429 | 430 | static bool 431 | read_symc_moves_file(void) 432 | { 433 | init_env(); 434 | 435 | Move m; 436 | bool r = true; 437 | FILE *f; 438 | char fname[strlen(tabledir)+100]; 439 | 440 | strcpy(fname, tabledir); 441 | strcat(fname, "/symc_moves"); 442 | 443 | if ((f = fopen(fname, "rb")) == NULL) 444 | return false; 445 | 446 | for (m = 0; m < NMOVES; m++) { 447 | r = r && fread(move_cp_16[m], sizeof(uint64_t), 448 | CLASSES_CP_16, f) == CLASSES_CP_16; 449 | r = r && fread(move_eofbepos_16[m], sizeof(uint64_t), 450 | CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; 451 | 452 | r = r && fread(ttrep_move_cp_16[m], sizeof(Trans), 453 | CLASSES_CP_16, f) == CLASSES_CP_16; 454 | r = r && fread(ttrep_move_eofbepos_16[m], sizeof(Trans), 455 | CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; 456 | } 457 | 458 | fclose(f); 459 | return r; 460 | } 461 | 462 | static bool 463 | read_symc_trans_file(void) 464 | { 465 | init_env(); 466 | 467 | Trans t; 468 | bool r = true; 469 | FILE *f; 470 | char fname[strlen(tabledir)+100]; 471 | 472 | strcpy(fname, tabledir); 473 | strcat(fname, "/symc_trans"); 474 | 475 | if ((f = fopen(fname, "rb")) == NULL) 476 | return false; 477 | 478 | for (t = 0; t < NTRANS; t++) { 479 | r = r && fread(trans_eofbepos[t], sizeof(int), 480 | POW2TO11*BINOM12ON4, f) == POW2TO11*BINOM12ON4; 481 | r = r && fread(trans_epud[t], sizeof(int), 482 | FACTORIAL8, f) == FACTORIAL8; 483 | r = r && fread(trans_cpud_separate[t], sizeof(int), 484 | BINOM8ON4, f) == BINOM8ON4; 485 | } 486 | 487 | fclose(f); 488 | return r; 489 | } 490 | 491 | static bool 492 | write_symdata_file(SymData *sd) 493 | { 494 | init_env(); 495 | 496 | FILE *f; 497 | char fname[strlen(tabledir)+100]; 498 | uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; 499 | bool r = true; 500 | 501 | strcpy(fname, tabledir); 502 | strcat(fname, "/"); 503 | strcat(fname, sd->filename); 504 | 505 | if ((f = fopen(fname, "wb")) == NULL) 506 | return false; 507 | 508 | r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; 509 | r = r && fwrite(sd->unsym, sizeof(uint64_t), *sn, f) == *sn; 510 | r = r && fwrite(sd->selfsim, sizeof(uint64_t), *sn, f) == *sn; 511 | r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; 512 | r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; 513 | 514 | fclose(f); 515 | return r; 516 | } 517 | 518 | static bool 519 | write_symc_moves_file(void) 520 | { 521 | init_env(); 522 | 523 | Move m; 524 | bool r = true; 525 | FILE *f; 526 | char fname[strlen(tabledir)+100]; 527 | 528 | strcpy(fname, tabledir); 529 | strcat(fname, "/symc_moves"); 530 | 531 | if ((f = fopen(fname, "wb")) == NULL) 532 | return false; 533 | 534 | for (m = 0; m < NMOVES; m++) { 535 | r = r && fwrite(move_cp_16[m], sizeof(uint64_t), 536 | CLASSES_CP_16, f) == CLASSES_CP_16; 537 | r = r && fwrite(move_eofbepos_16[m], sizeof(uint64_t), 538 | CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; 539 | 540 | r = r && fwrite(ttrep_move_cp_16[m], sizeof(Trans), 541 | CLASSES_CP_16, f) == CLASSES_CP_16; 542 | r = r && fwrite(ttrep_move_eofbepos_16[m], sizeof(Trans), 543 | CLASSES_EOFBEPOS_16, f) == CLASSES_EOFBEPOS_16; 544 | } 545 | 546 | fclose(f); 547 | return r; 548 | } 549 | 550 | static bool 551 | write_symc_trans_file(void) 552 | { 553 | init_env(); 554 | 555 | Trans t; 556 | bool r = true; 557 | FILE *f; 558 | char fname[strlen(tabledir)+100]; 559 | 560 | strcpy(fname, tabledir); 561 | strcat(fname, "/symc_trans"); 562 | 563 | if ((f = fopen(fname, "wb")) == NULL) 564 | return false; 565 | 566 | for (t = 0; t < NTRANS; t++) { 567 | r = r && fwrite(trans_eofbepos[t], sizeof(int), 568 | POW2TO11*BINOM12ON4, f) == POW2TO11*BINOM12ON4; 569 | r = r && fwrite(trans_epud[t], sizeof(int), 570 | FACTORIAL8, f) == FACTORIAL8; 571 | r = r && fwrite(trans_cpud_separate[t], sizeof(int), 572 | BINOM8ON4, f) == BINOM8ON4; 573 | } 574 | 575 | fclose(f); 576 | return r; 577 | } 578 | 579 | static void 580 | init_symc_moves(void) 581 | { 582 | uint64_t i, ii, coo; 583 | Move j; 584 | 585 | if (read_symc_moves_file()) 586 | return; 587 | 588 | for (i = 0; i < CLASSES_CP_16; i++) { 589 | ii = sd_cp_16.unsym[i]; 590 | for (j = 0; j < NMOVES; j++) { 591 | coo = sd_cp_16.coord->move(j, ii); 592 | move_cp_16[j][i] = sd_cp_16.class[coo]; 593 | ttrep_move_cp_16[j][i] = sd_cp_16.transtorep[coo]; 594 | } 595 | } 596 | 597 | for (i = 0; i < CLASSES_EOFBEPOS_16; i++) { 598 | ii = sd_eofbepos_16.unsym[i]; 599 | for (j = 0; j < NMOVES; j++) { 600 | coo = sd_eofbepos_16.coord->move(j, ii); 601 | move_eofbepos_16[j][i] = sd_eofbepos_16.class[coo]; 602 | ttrep_move_eofbepos_16[j][i] = 603 | sd_eofbepos_16.transtorep[coo]; 604 | } 605 | } 606 | 607 | if (!write_symc_moves_file()) 608 | fprintf(stderr, "Error writing SymMoves file\n"); 609 | } 610 | 611 | void 612 | init_symc_trans(void) 613 | { 614 | uint64_t i; 615 | int j, cp; 616 | int epe[4] = {FR, FL, BL, BR}; 617 | int a[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 }; 618 | Cube c; 619 | CubeArray *arr, *aux; 620 | Trans t; 621 | 622 | if (read_symc_trans_file()) 623 | return; 624 | 625 | for (i = 0; i < POW2TO11*BINOM12ON4; i++) { 626 | for (j = 0; j < 16; j++) { 627 | t = trans_group_udfix[j]; 628 | 629 | arr = new_cubearray((Cube){0}, pf_edges); 630 | int_to_sum_zero_array(i % POW2TO11, 2, 12, arr->eofb); 631 | epos_to_compatible_ep((i / POW2TO11)*24, arr->ep, epe); 632 | fix_eorleoud(arr); 633 | c = arrays_to_cube(arr, pf_edges); 634 | free_cubearray(arr, pf_edges); 635 | 636 | c = apply_trans(t, c); 637 | trans_eofbepos[t][i] = (c.epose/24)*POW2TO11 + c.eofb; 638 | } 639 | } 640 | 641 | aux = malloc(sizeof(CubeArray)); 642 | aux->ep = a; 643 | for (i = 0; i < FACTORIAL8; i++) { 644 | index_to_perm(i, 8, a); 645 | c = arrays_to_cube(aux, pf_ep); 646 | for (j = 0; j < 16; j++) { 647 | t = trans_group_udfix[j]; 648 | arr = new_cubearray(apply_trans(t, c), pf_ep); 649 | trans_epud[t][i] = perm_to_index(arr->ep, 8); 650 | free_cubearray(arr, pf_ep); 651 | } 652 | } 653 | free(aux); 654 | 655 | for (i = 0; i < BINOM8ON4; i++) { 656 | cp = cpud_separate_ant[i]; 657 | for (j = 0; j < 16; j++) { 658 | t = trans_group_udfix[j]; 659 | trans_cpud_separate[t][i] = 660 | cpud_separate_ind[cp_ttable[t][cp]]; 661 | } 662 | } 663 | 664 | if (!write_symc_trans_file()) 665 | fprintf(stderr, "Error writing SymTrans file\n"); 666 | } 667 | 668 | void 669 | init_symcoord(void) 670 | { 671 | int i; 672 | 673 | static bool initialized = false; 674 | if (initialized) 675 | return; 676 | initialized = true; 677 | 678 | init_coord(); 679 | 680 | init_symc_trans(); 681 | 682 | for (i = 0; all_sd[i] != NULL; i++) 683 | gensym(all_sd[i]); 684 | 685 | init_symc_moves(); 686 | } 687 | 688 | -------------------------------------------------------------------------------- /src/cube.c: -------------------------------------------------------------------------------- 1 | #include "cube.h" 2 | 3 | /* Local functions ***********************************************************/ 4 | 5 | static void init_inverse(void); 6 | static bool read_invtables_file(void); 7 | static bool write_invtables_file(void); 8 | 9 | /* Tables ********************************************************************/ 10 | 11 | static uint16_t eo_invtable_e[POW2TO11][BINOM12ON4*FACTORIAL4]; 12 | static uint16_t eo_invtable_s[POW2TO11][BINOM12ON4*FACTORIAL4]; 13 | static uint16_t eo_invtable_m[POW2TO11][BINOM12ON4*FACTORIAL4]; 14 | static uint16_t co_invtable[POW3TO7][FACTORIAL8]; 15 | static uint16_t cp_invtable[FACTORIAL8]; 16 | static uint16_t cpos_invtable[FACTORIAL6]; 17 | 18 | /* Functions implementation **************************************************/ 19 | 20 | int 21 | array_ep_to_epos(int *ep, int *ss) 22 | { 23 | int epos[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 24 | int eps[4]; 25 | int i, j, is; 26 | 27 | for (i = 0, is = 0; i < 12; i++) { 28 | for (j = 0; j < 4; j++) { 29 | if (ep[i] == ss[j]) { 30 | eps[is++] = j; 31 | epos[i] = 1; 32 | } 33 | } 34 | } 35 | 36 | for (i = 0; i < 4; i++) 37 | swap(&epos[ss[i]], &epos[i+8]); 38 | 39 | return 24 * subset_to_index(epos, 12, 4) + perm_to_index(eps, 4); 40 | } 41 | 42 | Cube 43 | arrays_to_cube(CubeArray *arr, PieceFilter f) 44 | { 45 | Cube ret = {0}; 46 | 47 | static int epe_solved[4] = {FR, FL, BL, BR}; 48 | static int eps_solved[4] = {UL, UR, DL, DR}; 49 | static int epm_solved[4] = {UF, UB, DF, DB}; 50 | 51 | if (f.epose) 52 | ret.epose = array_ep_to_epos(arr->ep, epe_solved); 53 | if (f.eposs) 54 | ret.eposs = array_ep_to_epos(arr->ep, eps_solved); 55 | if (f.eposm) 56 | ret.eposm = array_ep_to_epos(arr->ep, epm_solved); 57 | if (f.eofb) 58 | ret.eofb = digit_array_to_int(arr->eofb, 11, 2); 59 | if (f.eorl) 60 | ret.eorl = digit_array_to_int(arr->eorl, 11, 2); 61 | if (f.eoud) 62 | ret.eoud = digit_array_to_int(arr->eoud, 11, 2); 63 | if (f.cp) 64 | ret.cp = perm_to_index(arr->cp, 8); 65 | if (f.coud) 66 | ret.coud = digit_array_to_int(arr->coud, 7, 3); 67 | if (f.corl) 68 | ret.corl = digit_array_to_int(arr->corl, 7, 3); 69 | if (f.cofb) 70 | ret.cofb = digit_array_to_int(arr->cofb, 7, 3); 71 | if (f.cpos) 72 | ret.cpos = perm_to_index(arr->cpos, 6); 73 | 74 | return ret; 75 | } 76 | 77 | Cube 78 | compose_filtered(Cube c2, Cube c1, PieceFilter f) 79 | { 80 | CubeArray *arr = new_cubearray(c2, f); 81 | Cube ret; 82 | 83 | ret = move_via_arrays(arr, c1, f); 84 | free_cubearray(arr, f); 85 | 86 | return ret; 87 | } 88 | 89 | void 90 | cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) 91 | { 92 | int i; 93 | 94 | static int epe_solved[4] = {FR, FL, BL, BR}; 95 | static int eps_solved[4] = {UL, UR, DL, DR}; 96 | static int epm_solved[4] = {UF, UB, DF, DB}; 97 | 98 | if (f.epose || f.eposs || f.eposm) 99 | for (i = 0; i < 12; i++) 100 | arr->ep[i] = -1; 101 | 102 | if (f.epose) 103 | epos_to_partial_ep(cube.epose, arr->ep, epe_solved); 104 | if (f.eposs) 105 | epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); 106 | if (f.eposm) 107 | epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); 108 | if (f.eofb) 109 | int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); 110 | if (f.eorl) 111 | int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); 112 | if (f.eoud) 113 | int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); 114 | if (f.cp) 115 | index_to_perm(cube.cp, 8, arr->cp); 116 | if (f.coud) 117 | int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); 118 | if (f.corl) 119 | int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); 120 | if (f.cofb) 121 | int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); 122 | if (f.cpos) 123 | index_to_perm(cube.cpos, 6, arr->cpos); 124 | } 125 | 126 | void 127 | epos_to_compatible_ep(int epos, int *ep, int *ss) 128 | { 129 | int i, j, k, other[8]; 130 | bool flag; 131 | 132 | for (i = 0; i < 12; i++) 133 | ep[i] = -1; 134 | 135 | epos_to_partial_ep(epos, ep, ss); 136 | 137 | for (i = 0, j = 0; i < 12; i++) { 138 | flag = false; 139 | for (k = 0; k < 4; k++) 140 | flag = flag || (i == ss[k]); 141 | if (!flag) 142 | other[j++] = i; 143 | } 144 | 145 | for (i = 0, j = 0; i < 12; i++) 146 | if (ep[i] == -1) 147 | ep[i] = other[j++]; 148 | } 149 | 150 | void 151 | epos_to_partial_ep(int epos, int *ep, int *ss) 152 | { 153 | int i, is, eposs[12], eps[4]; 154 | 155 | index_to_perm(epos % FACTORIAL4, 4, eps); 156 | index_to_subset(epos / FACTORIAL4, 12, 4, eposs); 157 | 158 | for (i = 0; i < 4; i++) 159 | swap(&eposs[ss[i]], &eposs[i+8]); 160 | 161 | for (i = 0, is = 0; i < 12; i++) 162 | if (eposs[i]) 163 | ep[i] = ss[eps[is++]]; 164 | } 165 | 166 | void 167 | fix_eorleoud(CubeArray *arr) 168 | { 169 | int i; 170 | 171 | for (i = 0; i < 12; i++) { 172 | if ((edge_slice(i) == 0 && edge_slice(arr->ep[i]) != 0) || 173 | (edge_slice(i) != 0 && edge_slice(arr->ep[i]) == 0)) { 174 | arr->eorl[i] = 1 - arr->eofb[i]; 175 | } else { 176 | arr->eorl[i] = arr->eofb[i]; 177 | } 178 | 179 | if ((edge_slice(i) == 2 && edge_slice(arr->ep[i]) != 2) || 180 | (edge_slice(i) != 2 && edge_slice(arr->ep[i]) == 2)) { 181 | arr->eoud[i] = 1 - arr->eofb[i]; 182 | } else { 183 | arr->eoud[i] = arr->eofb[i]; 184 | } 185 | } 186 | } 187 | 188 | void 189 | fix_cofbcorl(CubeArray *arr) 190 | { 191 | int i; 192 | 193 | for (i = 0; i < 8; i++) { 194 | if (i % 2 == arr->cp[i] % 2) { 195 | arr->cofb[i] = arr->coud[i]; 196 | arr->corl[i] = arr->coud[i]; 197 | } else { 198 | if (arr->cp[i] % 2 == 0) { 199 | arr->cofb[i] = (arr->coud[i]+1)%3; 200 | arr->corl[i] = (arr->coud[i]+2)%3; 201 | } else { 202 | arr->cofb[i] = (arr->coud[i]+2)%3; 203 | arr->corl[i] = (arr->coud[i]+1)%3; 204 | } 205 | } 206 | } 207 | } 208 | 209 | Cube 210 | fourval_to_cube(int eofb, int ep, int coud, int cp) 211 | { 212 | CubeArray *arr; 213 | 214 | arr = new_cubearray((Cube){0}, pf_all); 215 | 216 | index_to_perm(ep, 12, arr->ep); 217 | index_to_perm(cp, 8, arr->cp); 218 | int_to_sum_zero_array(eofb, 2, 12, arr->eofb); 219 | int_to_sum_zero_array(coud, 3, 8, arr->coud); 220 | 221 | /* fix parity */ 222 | if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) 223 | swap(&(arr->ep[0]), &(arr->ep[1])); 224 | 225 | fix_eorleoud(arr); 226 | fix_cofbcorl(arr); 227 | 228 | Cube cube = arrays_to_cube(arr, pf_all); 229 | free_cubearray(arr, pf_all); 230 | return cube; 231 | } 232 | 233 | void 234 | free_cubearray(CubeArray *arr, PieceFilter f) 235 | { 236 | if (f.epose || f.eposs || f.eposm) 237 | free(arr->ep); 238 | if (f.eofb) 239 | free(arr->eofb); 240 | if (f.eorl) 241 | free(arr->eorl); 242 | if (f.eoud) 243 | free(arr->eoud); 244 | if (f.cp) 245 | free(arr->cp); 246 | if (f.coud) 247 | free(arr->coud); 248 | if (f.corl) 249 | free(arr->corl); 250 | if (f.cofb) 251 | free(arr->cofb); 252 | if (f.cpos) 253 | free(arr->cpos); 254 | 255 | free(arr); 256 | } 257 | 258 | Cube 259 | move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) 260 | { 261 | CubeArray *arrc = new_cubearray(c, f); 262 | Cube ret; 263 | 264 | if (f.epose || f.eposs || f.eposm) 265 | apply_permutation(arr->ep, arrc->ep, 12); 266 | 267 | if (f.eofb) { 268 | apply_permutation(arr->ep, arrc->eofb, 12); 269 | sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); 270 | } 271 | 272 | if (f.eorl) { 273 | apply_permutation(arr->ep, arrc->eorl, 12); 274 | sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); 275 | } 276 | 277 | if (f.eoud) { 278 | apply_permutation(arr->ep, arrc->eoud, 12); 279 | sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); 280 | } 281 | 282 | if (f.cp) 283 | apply_permutation(arr->cp, arrc->cp, 8); 284 | 285 | if (f.coud) { 286 | apply_permutation(arr->cp, arrc->coud, 8); 287 | sum_arrays_mod(arr->coud, arrc->coud, 8, 3); 288 | } 289 | 290 | if (f.corl) { 291 | apply_permutation(arr->cp, arrc->corl, 8); 292 | sum_arrays_mod(arr->corl, arrc->corl, 8, 3); 293 | } 294 | 295 | if (f.cofb) { 296 | apply_permutation(arr->cp, arrc->cofb, 8); 297 | sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); 298 | } 299 | 300 | if (f.cpos) 301 | apply_permutation(arr->cpos, arrc->cpos, 6); 302 | 303 | ret = arrays_to_cube(arrc, f); 304 | free_cubearray(arrc, f); 305 | 306 | return ret; 307 | } 308 | 309 | CubeArray * 310 | new_cubearray(Cube cube, PieceFilter f) 311 | { 312 | CubeArray *arr = malloc(sizeof(CubeArray)); 313 | 314 | if (f.epose || f.eposs || f.eposm) 315 | arr->ep = malloc(12 * sizeof(int)); 316 | if (f.eofb) 317 | arr->eofb = malloc(12 * sizeof(int)); 318 | if (f.eorl) 319 | arr->eorl = malloc(12 * sizeof(int)); 320 | if (f.eoud) 321 | arr->eoud = malloc(12 * sizeof(int)); 322 | if (f.cp) 323 | arr->cp = malloc(8 * sizeof(int)); 324 | if (f.coud) 325 | arr->coud = malloc(8 * sizeof(int)); 326 | if (f.corl) 327 | arr->corl = malloc(8 * sizeof(int)); 328 | if (f.cofb) 329 | arr->cofb = malloc(8 * sizeof(int)); 330 | if (f.cpos) 331 | arr->cpos = malloc(6 * sizeof(int)); 332 | 333 | cube_to_arrays(cube, arr, f); 334 | 335 | return arr; 336 | } 337 | 338 | Cube 339 | admissible_ep(Cube cube, PieceFilter f) 340 | { 341 | CubeArray *arr = new_cubearray(cube, f); 342 | Cube ret; 343 | bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 344 | int i, j; 345 | 346 | for (i = 0; i < 12; i++) 347 | if (arr->ep[i] != -1) 348 | used[arr->ep[i]] = true; 349 | 350 | for (i = 0, j = 0; i < 12; i++) { 351 | for ( ; j < 11 && used[j]; j++); 352 | if (arr->ep[i] == -1) 353 | arr->ep[i] = j++; 354 | } 355 | 356 | ret = arrays_to_cube(arr, pf_ep); 357 | free_cubearray(arr, f); 358 | 359 | return ret; 360 | } 361 | 362 | Cube 363 | compose(Cube c2, Cube c1) 364 | { 365 | return compose_filtered(c2, c1, pf_all); 366 | } 367 | 368 | int 369 | edge_slice(Edge e) { 370 | if (e < 0 || e > 11) 371 | return -1; 372 | 373 | if (e == FR || e == FL || e == BL || e == BR) 374 | return 0; 375 | if (e == UR || e == UL || e == DR || e == DL) 376 | return 1; 377 | 378 | return 2; 379 | } 380 | 381 | bool 382 | equal(Cube c1, Cube c2) 383 | { 384 | return c1.eofb == c2.eofb && 385 | c1.epose == c2.epose && 386 | c1.eposs == c2.eposs && 387 | c1.eposm == c2.eposm && 388 | c1.coud == c2.coud && 389 | c1.cp == c2.cp && 390 | c1.cpos == c2.cpos; 391 | } 392 | 393 | Cube 394 | inverse_cube(Cube cube) 395 | { 396 | CubeArray inv; 397 | Cube ret; 398 | int i, ep[12]; 399 | 400 | for (i = 0; i < 12; i++) 401 | ep[i] = where_is_edge(cube, i); 402 | inv = (CubeArray){.ep = ep}; 403 | ret = arrays_to_cube(&inv, pf_ep); 404 | 405 | ret.eofb = ((int)eo_invtable_e[cube.eofb][cube.epose]) | 406 | ((int)eo_invtable_m[cube.eofb][cube.eposm]) | 407 | ((int)eo_invtable_s[cube.eofb][cube.eposs]); 408 | ret.eorl = ((int)eo_invtable_e[cube.eorl][cube.epose]) | 409 | ((int)eo_invtable_m[cube.eorl][cube.eposm]) | 410 | ((int)eo_invtable_s[cube.eorl][cube.eposs]); 411 | ret.eoud = ((int)eo_invtable_e[cube.eoud][cube.epose]) | 412 | ((int)eo_invtable_m[cube.eoud][cube.eposm]) | 413 | ((int)eo_invtable_s[cube.eoud][cube.eposs]); 414 | ret.cp = cp_invtable[cube.cp]; 415 | ret.cpos = cpos_invtable[cube.cpos]; 416 | ret.coud = co_invtable[cube.coud][cube.cp]; 417 | ret.corl = co_invtable[cube.corl][cube.cp]; 418 | ret.cofb = co_invtable[cube.cofb][cube.cp]; 419 | 420 | return ret; 421 | } 422 | 423 | bool 424 | is_admissible(Cube cube) { 425 | 426 | /* TODO: this should check consistency of different orientations */ 427 | /* check also that centers are opposite and admissible */ 428 | 429 | CubeArray *a = new_cubearray(cube, pf_all); 430 | int parity; 431 | bool perm; 432 | 433 | perm = is_perm(a->ep, 12) && 434 | is_perm(a->cp, 8) && 435 | is_perm(a->cpos, 6); 436 | parity = perm_sign(a->ep, 12) + 437 | perm_sign(a->cp, 8) + 438 | perm_sign(a->cpos, 6); 439 | 440 | free_cubearray(a, pf_all); 441 | 442 | return perm && parity % 2 == 0; 443 | } 444 | 445 | bool 446 | is_solved(Cube cube) 447 | { 448 | return equal(cube, (Cube){0}); 449 | } 450 | 451 | Center 452 | what_center_at(Cube cube, Center c) 453 | { 454 | static bool initialized = false; 455 | static Center aux[FACTORIAL6][6]; 456 | static int i; 457 | static unsigned int ui; 458 | static CubeArray *arr; 459 | 460 | if (!initialized) { 461 | for (ui = 0; ui < FACTORIAL6; ui++) { 462 | arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); 463 | for (i = 0; i < 6; i++) 464 | aux[ui][i] = arr->cpos[i]; 465 | free_cubearray(arr, pf_cpos); 466 | } 467 | 468 | initialized = true; 469 | } 470 | 471 | return aux[cube.cpos][c]; 472 | } 473 | 474 | Corner 475 | what_corner_at(Cube cube, Corner c) 476 | { 477 | int i; 478 | unsigned int ui; 479 | CubeArray *arr; 480 | 481 | static bool initialized = false; 482 | static Corner aux[FACTORIAL8][8]; 483 | 484 | if (!initialized) { 485 | for (ui = 0; ui < FACTORIAL8; ui++) { 486 | arr = new_cubearray((Cube){.cp = ui}, pf_cp); 487 | for (i = 0; i < 8; i++) 488 | aux[ui][i] = arr->cp[i]; 489 | free_cubearray(arr, pf_cp); 490 | } 491 | 492 | initialized = true; 493 | } 494 | 495 | return aux[cube.cp][c]; 496 | } 497 | 498 | Edge 499 | what_edge_at(Cube cube, Edge e) 500 | { 501 | Edge ret; 502 | CubeArray *arr = new_cubearray(cube, pf_ep); 503 | 504 | ret = arr->ep[e]; 505 | 506 | free_cubearray(arr, pf_ep); 507 | return ret; 508 | } 509 | 510 | Edge 511 | where_is_edge(Cube c, Edge e) 512 | { 513 | int r0, r1, r2; 514 | 515 | static bool initialized = false; 516 | static int aux[3][BINOM12ON4*FACTORIAL4][12]; 517 | static int i, j; 518 | static unsigned int ui; 519 | static CubeArray *arr; 520 | 521 | if (!initialized) { 522 | for (ui = 0; ui < BINOM12ON4*FACTORIAL4; ui++) { 523 | for (i = 0; i < 3; i++) 524 | for (j = 0; j < 12; j++) 525 | aux[i][ui][j] = -1; 526 | 527 | arr = new_cubearray((Cube){.epose = ui}, pf_e); 528 | for (i = 0; i < 12; i++) 529 | if (edge_slice(arr->ep[i]) == 0) 530 | aux[0][ui][arr->ep[i]] = i; 531 | free_cubearray(arr, pf_e); 532 | 533 | arr = new_cubearray((Cube){.eposs = ui}, pf_s); 534 | for (i = 0; i < 12; i++) 535 | if (edge_slice(arr->ep[i]) == 1) 536 | aux[1][ui][arr->ep[i]] = i; 537 | free_cubearray(arr, pf_s); 538 | 539 | arr = new_cubearray((Cube){.eposm = ui}, pf_m); 540 | for (i = 0; i < 12; i++) 541 | if (edge_slice(arr->ep[i]) == 2) 542 | aux[2][ui][arr->ep[i]] = i; 543 | free_cubearray(arr, pf_m); 544 | } 545 | 546 | initialized = true; 547 | } 548 | 549 | r0 = aux[0][c.epose][e]; 550 | r1 = aux[1][c.eposs][e]; 551 | r2 = aux[2][c.eposm][e]; 552 | return MAX(r0, MAX(r1, r2)); 553 | } 554 | 555 | static bool 556 | read_invtables_file(void) 557 | { 558 | init_env(); 559 | 560 | FILE *f; 561 | char fname[strlen(tabledir)+20]; 562 | int b; 563 | unsigned int ui, meeo, meco, mecp, mecpos; 564 | bool r; 565 | 566 | strcpy(fname, tabledir); 567 | strcat(fname, "/invtables"); 568 | 569 | if ((f = fopen(fname, "rb")) == NULL) 570 | return false; 571 | 572 | b = sizeof(uint16_t); 573 | r = true; 574 | meeo = BINOM12ON4*FACTORIAL4; 575 | meco = FACTORIAL8; 576 | mecp = FACTORIAL8; 577 | mecpos = FACTORIAL6; 578 | 579 | for (ui = 0; ui < POW2TO11; ui++) { 580 | r = r && fread(eo_invtable_e[ui], b, meeo, f) == meeo; 581 | r = r && fread(eo_invtable_m[ui], b, meeo, f) == meeo; 582 | r = r && fread(eo_invtable_s[ui], b, meeo, f) == meeo; 583 | } 584 | 585 | for (ui = 0; ui < POW3TO7; ui++) { 586 | r = r && fread(co_invtable[ui], b, meco, f) == meco; 587 | } 588 | 589 | r = r && fread(cp_invtable, b, mecp, f) == mecp; 590 | r = r && fread(cpos_invtable, b, mecpos, f) == mecpos; 591 | 592 | fclose(f); 593 | return r; 594 | } 595 | 596 | static bool 597 | write_invtables_file(void) 598 | { 599 | init_env(); 600 | 601 | FILE *f; 602 | char fname[strlen(tabledir)+20]; 603 | unsigned int ui, meeo, meco, mecp, mecpos; 604 | int b; 605 | bool r; 606 | 607 | strcpy(fname, tabledir); 608 | strcat(fname, "/invtables"); 609 | 610 | if ((f = fopen(fname, "wb")) == NULL) 611 | return false; 612 | 613 | b = sizeof(uint16_t); 614 | r = true; 615 | meeo = BINOM12ON4*FACTORIAL4; 616 | meco = FACTORIAL8; 617 | mecp = FACTORIAL8; 618 | mecpos = FACTORIAL6; 619 | 620 | for (ui = 0; ui < POW2TO11; ui++) { 621 | r = r && fwrite(eo_invtable_e[ui], b, meeo, f) == meeo; 622 | r = r && fwrite(eo_invtable_m[ui], b, meeo, f) == meeo; 623 | r = r && fwrite(eo_invtable_s[ui], b, meeo, f) == meeo; 624 | } 625 | 626 | for (ui = 0; ui < POW3TO7; ui++) { 627 | r = r && fwrite(co_invtable[ui], b, meco, f) == meco; 628 | } 629 | 630 | r = r && fwrite(cp_invtable, b, mecp, f) == mecp; 631 | r = r && fwrite(cpos_invtable, b, mecpos, f) == mecpos; 632 | 633 | fclose(f); 634 | return r; 635 | } 636 | 637 | void 638 | init_inverse(void) 639 | { 640 | static bool initialized = false; 641 | if (initialized) 642 | return; 643 | initialized = true; 644 | 645 | if (read_invtables_file()) 646 | return; 647 | 648 | fprintf(stderr, "Cannot load invtables, generating it\n"); 649 | 650 | CubeArray *aux, *inv; 651 | Cube c; 652 | int i, j, eoaux[12], eoinv[12]; 653 | unsigned int ui, uj; 654 | 655 | aux = new_cubearray((Cube){0}, pf_all); 656 | inv = new_cubearray((Cube){0}, pf_all); 657 | 658 | for (ui = 0; ui < POW2TO11; ui++) { 659 | int_to_sum_zero_array(ui, 2, 12, eoaux); 660 | for (uj = 0; uj < BINOM12ON4*FACTORIAL4; uj++) { 661 | for (j = 0; j < 12; j++) 662 | eoinv[j] = 0; 663 | c = (Cube){.epose = uj, .eposm = 0, .eposs = 0}; 664 | eoinv[FR] = eoaux[where_is_edge(c, FR)]; 665 | eoinv[FL] = eoaux[where_is_edge(c, FL)]; 666 | eoinv[BL] = eoaux[where_is_edge(c, BL)]; 667 | eoinv[BR] = eoaux[where_is_edge(c, BR)]; 668 | eo_invtable_e[ui][uj] = digit_array_to_int(eoinv,11,2); 669 | 670 | for (j = 0; j < 12; j++) 671 | eoinv[j] = 0; 672 | c = (Cube){.epose = 0, .eposm = uj, .eposs = 0}; 673 | eoinv[UF] = eoaux[where_is_edge(c, UF)]; 674 | eoinv[UB] = eoaux[where_is_edge(c, UB)]; 675 | eoinv[DF] = eoaux[where_is_edge(c, DF)]; 676 | eoinv[DB] = eoaux[where_is_edge(c, DB)]; 677 | eo_invtable_m[ui][uj] = digit_array_to_int(eoinv,11,2); 678 | 679 | for (j = 0; j < 12; j++) 680 | eoinv[j] = 0; 681 | c = (Cube){.epose = 0, .eposm = 0, .eposs = uj}; 682 | eoinv[UL] = eoaux[where_is_edge(c, UL)]; 683 | eoinv[UR] = eoaux[where_is_edge(c, UR)]; 684 | eoinv[DL] = eoaux[where_is_edge(c, DL)]; 685 | eoinv[DR] = eoaux[where_is_edge(c, DR)]; 686 | eo_invtable_s[ui][uj] = digit_array_to_int(eoinv,11,2); 687 | } 688 | } 689 | 690 | for (ui = 0; ui < FACTORIAL8; ui++) { 691 | cube_to_arrays((Cube){.cp = ui}, aux, pf_cp); 692 | for (i = 0; i < 8; i++) 693 | inv->cp[aux->cp[i]] = i; 694 | cp_invtable[ui] = (uint16_t)arrays_to_cube(inv, pf_cp).cp; 695 | 696 | for (uj = 0; uj < POW3TO7; uj++) { 697 | cube_to_arrays((Cube){.coud = uj}, aux, pf_coud); 698 | for (i = 0; i < 8; i++) 699 | inv->coud[aux->cp[i]] = (3-aux->coud[i])%3; 700 | co_invtable[uj][ui] = 701 | (uint16_t)arrays_to_cube(inv, pf_coud).coud; 702 | } 703 | } 704 | 705 | for (ui = 0; ui < FACTORIAL6; ui++) { 706 | cube_to_arrays((Cube){.cpos = ui}, aux, pf_cpos); 707 | for (i = 0; i < 6; i++) 708 | inv->cpos[aux->cpos[i]] = i; 709 | cpos_invtable[ui] = 710 | (uint16_t)arrays_to_cube(inv, pf_cpos).cpos; 711 | } 712 | 713 | free_cubearray(aux, pf_all); 714 | free_cubearray(inv, pf_all); 715 | 716 | if (!write_invtables_file()) 717 | fprintf(stderr, "Error writing invtables\n"); 718 | } 719 | 720 | void 721 | init_cube(void) 722 | { 723 | init_inverse(); 724 | } 725 | -------------------------------------------------------------------------------- /src/commands.c: -------------------------------------------------------------------------------- 1 | #include "commands.h" 2 | 3 | /* Arg parsing functions *****************************************************/ 4 | 5 | CommandArgs * help_parse_args(int c, char **v); 6 | CommandArgs * parse_only_scramble(int c, char **v); 7 | CommandArgs * parse_no_arg(int c, char **v); 8 | CommandArgs * solve_parse_args(int c, char **v); 9 | CommandArgs * scramble_parse_args(int c, char **v); 10 | 11 | /* Exec functions ************************************************************/ 12 | 13 | static char* cleanup_exec(CommandArgs *args); 14 | static char* invert_exec(CommandArgs *args); 15 | static char* solve_exec(CommandArgs *args); 16 | static char* scramble_exec(CommandArgs *args); 17 | static char* steps_exec(CommandArgs *args); 18 | static char* commands_exec(CommandArgs *args); 19 | static char* twophase_exec(CommandArgs *args); 20 | static char* help_exec(CommandArgs *args); 21 | static char* unniss_exec(CommandArgs *args); 22 | static char* version_exec(CommandArgs *args); 23 | 24 | /* Local functions ***********************************************************/ 25 | 26 | static bool read_step(CommandArgs *args, char *str); 27 | static bool read_scrtype(CommandArgs *args, char *str); 28 | static bool read_scramble(int c, char **v, CommandArgs *args); 29 | 30 | /* Commands ******************************************************************/ 31 | 32 | Command 33 | solve_cmd = { 34 | .name = "solve", 35 | .usage = "solve STEP [OPTIONS] SCRAMBLE", 36 | .description = "Solve a step; see command steps for a list of steps", 37 | .parse_args = solve_parse_args, 38 | .exec = solve_exec 39 | }; 40 | 41 | Command 42 | scramble_cmd = { 43 | .name = "scramble", 44 | .usage = "scramble [TYPE] [-n N]", 45 | .description = "Get a random-position scramble", 46 | .parse_args = scramble_parse_args, 47 | .exec = scramble_exec, 48 | }; 49 | 50 | Command 51 | invert_cmd = { 52 | .name = "invert", 53 | .usage = "invert SCRAMBLE]", 54 | .description = "Invert a scramble", 55 | .parse_args = parse_only_scramble, 56 | .exec = invert_exec, 57 | }; 58 | 59 | Command 60 | steps_cmd = { 61 | .name = "steps", 62 | .usage = "steps", 63 | .description = "List available steps", 64 | .parse_args = parse_no_arg, 65 | .exec = steps_exec 66 | }; 67 | 68 | Command 69 | commands_cmd = { 70 | .name = "commands", 71 | .usage = "commands", 72 | .description = "List available commands", 73 | .parse_args = parse_no_arg, 74 | .exec = commands_exec 75 | }; 76 | 77 | Command 78 | help_cmd = { 79 | .name = "help", 80 | .usage = "help [COMMAND]", 81 | .description = "Display nissy manual page or help on specific command", 82 | .parse_args = help_parse_args, 83 | .exec = help_exec, 84 | }; 85 | 86 | Command 87 | twophase_cmd = { 88 | .name = "twophase", 89 | .usage = "twophase", 90 | .description = "Find a solution quickly using a 2-phase method", 91 | .parse_args = parse_only_scramble, 92 | .exec = twophase_exec, 93 | }; 94 | 95 | Command 96 | cleanup_cmd = { 97 | .name = "cleanup", 98 | .usage = "cleanup SCRAMBLE", 99 | .description = "Rewrite a scramble using only standard moves (HTM)", 100 | .parse_args = parse_only_scramble, 101 | .exec = cleanup_exec, 102 | }; 103 | 104 | Command 105 | unniss_cmd = { 106 | .name = "unniss", 107 | .usage = "unniss SCRAMBLE", 108 | .description = "Rewrite a scramble without NISS", 109 | .parse_args = parse_only_scramble, 110 | .exec = unniss_exec, 111 | }; 112 | 113 | Command 114 | version_cmd = { 115 | .name = "version", 116 | .usage = "version", 117 | .description = "print nissy version", 118 | .parse_args = parse_no_arg, 119 | .exec = version_exec, 120 | }; 121 | 122 | Command *commands[] = { 123 | &commands_cmd, 124 | &help_cmd, 125 | &invert_cmd, 126 | &solve_cmd, 127 | &scramble_cmd, 128 | &steps_cmd, 129 | &twophase_cmd, 130 | &cleanup_cmd, 131 | &unniss_cmd, 132 | &version_cmd, 133 | NULL 134 | }; 135 | 136 | /* Other constants ***********************************************************/ 137 | 138 | char *scrtypes[20] = { "eo", "corners", "edges", "fmc", "dr", "htr", NULL }; 139 | 140 | /* Arg parsing functions implementation **************************************/ 141 | 142 | CommandArgs * 143 | solve_parse_args(int c, char **v) 144 | { 145 | int i; 146 | bool infinitesols, fixedmsols; 147 | long val; 148 | 149 | CommandArgs *a = new_args(); 150 | 151 | a->opts->min_moves = 0; 152 | a->opts->max_moves = 20; 153 | a->opts->max_solutions = 1; 154 | a->opts->nthreads = 1; 155 | a->opts->optimal = -1; 156 | a->opts->nisstype = NORMAL; 157 | a->opts->verbose = false; 158 | a->opts->all = false; 159 | a->opts->print_number = true; 160 | a->opts->count_only = false; 161 | 162 | fixedmsols = false; 163 | infinitesols = false; 164 | 165 | for (i = 0; i < c; i++) { 166 | if (!strcmp(v[i], "-m") && i+1 < c) { 167 | val = strtol(v[++i], NULL, 10); 168 | if (val < 0 || val > 100) { 169 | fprintf(stderr, 170 | "Invalid min number of moves" 171 | "(0 <= N <= 100).\n"); 172 | return a; 173 | } 174 | a->opts->min_moves = val; 175 | } else if (!strcmp(v[i], "-M") && i+1 < c) { 176 | val = strtol(v[++i], NULL, 10); 177 | if (val < 0 || val > 100) { 178 | fprintf(stderr, 179 | "Invalid max number of moves" 180 | "(0 <= N <= 100).\n"); 181 | return a; 182 | } 183 | a->opts->max_moves = val; 184 | infinitesols = true; 185 | } else if (!strcmp(v[i], "-t") && i+1 < c) { 186 | val = strtol(v[++i], NULL, 10); 187 | if (val < 1 || val > 64) { 188 | fprintf(stderr, 189 | "Invalid number of threads." 190 | "1 <= t <= 64\n"); 191 | return a; 192 | } 193 | a->opts->nthreads = val; 194 | } else if (!strcmp(v[i], "-n") && i+1 < c) { 195 | val = strtol(v[++i], NULL, 10); 196 | if (val < 1 || val > 1000000) { 197 | fprintf(stderr, 198 | "Invalid number of solutions.\n"); 199 | return a; 200 | } 201 | a->opts->max_solutions = val; 202 | fixedmsols = true; 203 | } else if (!strcmp(v[i], "-o")) { 204 | a->opts->optimal = 0; 205 | infinitesols = true; 206 | } else if (!strcmp(v[i], "-O") && i+1 < c) { 207 | val = strtol(v[++i], NULL, 10); 208 | if (val < 0 || val > 100 || 209 | (val == 0 && strcmp("0", v[i]))) { 210 | fprintf(stderr, 211 | "Invalid max number of moves" 212 | " (0 <= N <= 100).\n"); 213 | return a; 214 | } 215 | a->opts->optimal = val; 216 | infinitesols = true; 217 | } else if (!strcmp(v[i], "-N")) { 218 | a->opts->nisstype = NISS; 219 | } else if (!strcmp(v[i], "-L")) { 220 | if (a->opts->nisstype != NISS) 221 | a->opts->nisstype = LINEAR; 222 | } else if (!strcmp(v[i], "-i")) { 223 | a->scrstdin = true; 224 | } else if (!strcmp(v[i], "-v")) { 225 | a->opts->verbose = true; 226 | } else if (!strcmp(v[i], "-a")) { 227 | a->opts->all = true; 228 | } else if (!strcmp(v[i], "-p")) { 229 | a->opts->print_number = false; 230 | } else if (!strcmp(v[i], "-c")) { 231 | a->opts->count_only = true; 232 | } else if (!read_step(a, v[i])) { 233 | break; 234 | } 235 | } 236 | 237 | if (infinitesols && !fixedmsols) 238 | a->opts->max_solutions = 1000000; /* 1M = +infty */ 239 | 240 | a->success = (a->scrstdin && i == c) || read_scramble(c-i, &v[i], a); 241 | return a; 242 | } 243 | 244 | CommandArgs * 245 | scramble_parse_args(int c, char **v) 246 | { 247 | int i; 248 | long val; 249 | 250 | CommandArgs *a = new_args(); 251 | 252 | a->success = true; 253 | a->n = 1; 254 | 255 | for (i = 0; i < c; i++) { 256 | if (!strcmp(v[i], "-n") && i+1 < c) { 257 | val = strtol(v[++i], NULL, 10); 258 | if (val < 1 || val > 1000000) { 259 | fprintf(stderr, 260 | "Invalid number of scrambles.\n"); 261 | a->success = false; 262 | return a; 263 | } 264 | a->n = val; 265 | } else if (!read_scrtype(a, v[i])) { 266 | a->success = false; 267 | return a; 268 | } 269 | } 270 | 271 | return a; 272 | } 273 | 274 | CommandArgs * 275 | gen_parse_args(int c, char **v) 276 | { 277 | int val; 278 | CommandArgs *a = new_args(); 279 | 280 | a->opts->nthreads = 64; 281 | a->success = false; 282 | 283 | if (c == 0) { 284 | a->success = true; 285 | } else { 286 | if (!strcmp(v[0], "-t") && c > 1) { 287 | val = strtol(v[1], NULL, 10); 288 | if (val < 1 || val > 64) { 289 | fprintf(stderr, 290 | "Invalid number of threads." 291 | "1 <= t <= 64\n"); 292 | return a; 293 | } 294 | a->opts->nthreads = val; 295 | a->success = true; 296 | } 297 | } 298 | 299 | return a; 300 | } 301 | 302 | CommandArgs * 303 | help_parse_args(int c, char **v) 304 | { 305 | int i; 306 | CommandArgs *a = new_args(); 307 | 308 | if (c == 1) { 309 | for (i = 0; commands[i] != NULL; i++) 310 | if (!strcmp(v[0], commands[i]->name)) 311 | a->command = commands[i]; 312 | if (a->command == NULL) 313 | fprintf(stderr, "%s: command not found\n", v[0]); 314 | } 315 | 316 | a->success = c == 0 || (c == 1 && a->command != NULL); 317 | return a; 318 | } 319 | 320 | CommandArgs * 321 | parse_only_scramble(int c, char **v) 322 | { 323 | CommandArgs *a = new_args(); 324 | 325 | if (!strcmp(v[0], "-i")) { 326 | a->scrstdin = true; 327 | a->success = c == 1; 328 | } else { 329 | a->success = read_scramble(c, v, a); 330 | } 331 | 332 | return a; 333 | } 334 | 335 | CommandArgs * 336 | parse_no_arg(int c, char **v) 337 | { 338 | CommandArgs *a = new_args(); 339 | 340 | a->success = true; 341 | 342 | return a; 343 | } 344 | 345 | void append_str(char **str1, const char *str2) { 346 | size_t len1 = strlen(*str1); 347 | size_t len2 = strlen(str2); 348 | 349 | // Check if more memory is needed 350 | if (len1 + len2 + 1 >= strlen(*str1)) { 351 | *str1 = realloc(*str1, (len1 + len2 + 1) * sizeof(char)); 352 | if (*str1 == NULL) { 353 | fprintf(stderr, "Memory allocation failed\n"); 354 | exit(EXIT_FAILURE); 355 | } 356 | } 357 | 358 | // Append str2 to str1 359 | strcat(*str1, str2); 360 | } 361 | 362 | /* Exec functions implementation *********************************************/ 363 | 364 | static char* 365 | solve_exec(CommandArgs *args) 366 | { 367 | Cube c; 368 | 369 | init_all_movesets(); 370 | init_symcoord(); 371 | 372 | // I don't think the timeout is working so here's a workaround. 373 | args->opts->max_solutions = 100; 374 | 375 | c = apply_alg(args->scramble, (Cube){0}); 376 | SolveOutput *solve_output = solve(args->start, c, args->step, args->opts); 377 | 378 | if (solve_output->error_msg != NULL) { 379 | char *msg = solve_output->error_msg; 380 | strcat(msg, "\n"); 381 | solve_output_free(solve_output); 382 | return msg; 383 | } else { 384 | sort_alglist(solve_output->sols); 385 | char **strings = alglist_to_strings(solve_output->sols); 386 | int memory_needed = 1; // Need 1 byte the null terminator 387 | 388 | // Add length of each string and 1 byte for newline 389 | for (int i = 0; i < solve_output->sols->len; i++) 390 | memory_needed += strlen(strings[i]) + 1; 391 | 392 | char *output = malloc(memory_needed * sizeof(char)); 393 | output[0] = '\0'; // Initialize output as an empty string 394 | for (int i = 0; i < solve_output->sols->len; i++) { 395 | strcat(output, strings[i]); 396 | strcat(output, "\n"); 397 | free(strings[i]); 398 | } 399 | free(strings); 400 | return output; 401 | } 402 | } 403 | 404 | static char* 405 | scramble_exec(CommandArgs *args) 406 | { 407 | init_all_movesets(); 408 | init_symcoord(); 409 | 410 | // Start time after initialization because initialization is slow. 411 | struct timespec start; 412 | clock_gettime(CLOCK_MONOTONIC, &start); 413 | 414 | Cube cube; 415 | CubeArray *arr; 416 | Alg *scr, *ruf, *aux; 417 | int i, j, eo, ep, co, cp, a[12]; 418 | int eparr[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 }; 419 | uint64_t ui, uj, uk; 420 | 421 | // Used to be seeded with time in seconds. 422 | // Now we seed wih nanoseconds to get more variety. 423 | srand(start.tv_nsec); 424 | 425 | if (!strcmp(args->scrtype, "dr")) { 426 | /* Warning: cube is inconsistent because of side CO * 427 | * and EO on U/D. But solve_2phase only solves drfin * 428 | * in this case, so it should be ok. * 429 | * TODO: check this properly * 430 | * Moreover we again need to fix parity after * 431 | * generating epose manually */ 432 | do { 433 | ui = rand() % FACTORIAL8; 434 | uj = rand() % FACTORIAL8; 435 | uk = rand() % FACTORIAL4; 436 | 437 | index_to_perm(ui, 8, eparr); 438 | arr = malloc(sizeof(CubeArray)); 439 | arr->ep = eparr; 440 | cube = arrays_to_cube(arr, pf_ep); 441 | free(arr); 442 | 443 | cube.cp = uj; 444 | cube.epose = uk; 445 | } while (!is_admissible(cube)); 446 | } else if (!strcmp(args->scrtype, "htr")) { 447 | /* antindex_htrfin() returns a consistent * 448 | * cube, except possibly for parity */ 449 | do { 450 | ui = rand() % (24*24/6); 451 | cube = (Cube){0}; 452 | cube.cp = cornershtrfin_ant[ui]; 453 | cube.epose = rand() % 24; 454 | cube.eposs = rand() % 24; 455 | cube.eposm = rand() % 24; 456 | } while (!is_admissible(cube)); 457 | } else { 458 | eo = rand() % POW2TO11; 459 | ep = rand() % FACTORIAL12; 460 | co = rand() % POW3TO7; 461 | cp = rand() % FACTORIAL8; 462 | 463 | if (!strcmp(args->scrtype, "eo")) { 464 | eo = 0; 465 | } else if (!strcmp(args->scrtype, "corners")) { 466 | eo = 0; 467 | ep = 0; 468 | index_to_perm(cp, 8, a); 469 | if (perm_sign(a, 8) == 1) { 470 | swap(&a[0], &a[1]); 471 | cp = perm_to_index(a, 8); 472 | } 473 | } else if (!strcmp(args->scrtype, "edges")) { 474 | co = 0; 475 | cp = 0; 476 | index_to_perm(ep, 12, a); 477 | if (perm_sign(a, 12) == 1) { 478 | swap(&a[0], &a[1]); 479 | ep = perm_to_index(a, 12); 480 | } 481 | } 482 | cube = fourval_to_cube(eo, ep, co, cp); 483 | } 484 | 485 | /* TODO: can be optimized for htr and dr using htrfin, drfin */ 486 | scr = solve_2phase(start, cube, 1); 487 | 488 | if (!strcmp(args->scrtype, "fmc")) { 489 | aux = new_alg(""); 490 | copy_alg(scr, aux); 491 | /* Trick to rufify for free: rotate the scramble * 492 | * so that it does not start with F or end with R */ 493 | for (j = 0; j < NROTATIONS; j++) { 494 | if (base_move(scr->move[0]) != F && 495 | base_move(scr->move[0]) != B && 496 | base_move(scr->move[scr->len-1]) != R && 497 | base_move(scr->move[scr->len-1]) != L) 498 | break; 499 | copy_alg(aux, scr); 500 | transform_alg(j, scr); 501 | } 502 | copy_alg(scr, aux); 503 | ruf = new_alg("R' U' F"); 504 | copy_alg(ruf, scr); 505 | compose_alg(scr, aux); 506 | compose_alg(scr, ruf); 507 | free_alg(aux); 508 | free_alg(ruf); 509 | } 510 | char *alg_string = alg_to_string(scr); 511 | strcat(alg_string, "\n"); 512 | free_alg(scr); 513 | return alg_string; 514 | } 515 | 516 | static char* 517 | invert_exec(CommandArgs *args) 518 | { 519 | Alg *inv = inverse_alg(args->scramble); 520 | char *alg_string = alg_to_string(inv); 521 | strcat(alg_string, "\n"); 522 | free_alg(inv); 523 | return alg_string; 524 | } 525 | 526 | static char* 527 | steps_exec(CommandArgs *args) 528 | { 529 | char *output = malloc(1024 * sizeof(char)); 530 | output[0] = '\0'; // Initialize output as an empty string 531 | for (int i = 0; steps[i] != NULL; i++) { 532 | printf("%d ", i); 533 | strcat(output, steps[i]->shortname); 534 | strcat(output, "\t "); 535 | strcat(output, steps[i]->name); 536 | strcat(output, "\n"); 537 | } 538 | return output; 539 | } 540 | 541 | static char* 542 | commands_exec(CommandArgs *args) 543 | { 544 | char *output = malloc(1024 * sizeof(char)); 545 | output[0] = '\0'; // Initialize output as an empty string 546 | for (int i = 0; commands[i] != NULL; i++) { 547 | strcat(output, commands[i]->usage); 548 | strcat(output, "\n"); 549 | } 550 | 551 | return output; 552 | } 553 | 554 | static char* 555 | twophase_exec(CommandArgs *args) 556 | { 557 | Cube c; 558 | Alg *sol; 559 | 560 | init_all_movesets(); 561 | init_symcoord(); 562 | 563 | c = apply_alg(args->scramble, (Cube){0}); 564 | sol = solve_2phase(args->start, c, 1); 565 | 566 | char *alg_string = alg_to_string(sol); 567 | free_alg(sol); 568 | strcat(alg_string, "\n"); 569 | return alg_string; 570 | } 571 | 572 | static char* 573 | help_exec(CommandArgs *args) 574 | { 575 | char *output = malloc(1024 * sizeof(char)); 576 | if (args->command == NULL) { 577 | sprintf(output, 578 | "Use the nissy command \"help COMMAND\" for a short " 579 | "description of a specific command.\n" 580 | "Use the nissy command \"commands\" for a list of " 581 | "available commands.\n" 582 | "See the manual page for more details. The manual" 583 | " page is available with \"man nissy\" on a UNIX" 584 | " system (such as Linux or MacOS) or in pdf and html" 585 | " format in the docs folder.\n" 586 | "Nissy is available for free at " 587 | "https://github.com/sebastianotronto/nissy\n" 588 | ); 589 | } else { 590 | sprintf(output, "Command %s: %s\nusage: %s\n", args->command->name, 591 | args->command->description, args->command->usage); 592 | } 593 | return output; 594 | } 595 | 596 | static char* 597 | cleanup_exec(CommandArgs *args) 598 | { 599 | init_moves(); 600 | 601 | Alg *alg = cleanup(args->scramble); 602 | char *alg_string = alg_to_string(alg); 603 | strcat(alg_string, "\n"); 604 | free_alg(alg); 605 | return alg_string; 606 | } 607 | 608 | static char* 609 | unniss_exec(CommandArgs *args) 610 | { 611 | Alg *unnissed = unniss(args->scramble); 612 | char *alg_string = alg_to_string(unnissed); 613 | strcat(alg_string, "\n"); 614 | free_alg(unnissed); 615 | return alg_string; 616 | } 617 | 618 | static char* 619 | version_exec(CommandArgs *args) 620 | { 621 | return VERSION"\n"; 622 | } 623 | 624 | /* Local functions implementation ********************************************/ 625 | 626 | /* Similar to print_alg defined in alg.c except this returns a string */ 627 | char* alg_to_string(Alg *alg) { 628 | char *result = malloc(1000); // Adjust the size as needed 629 | char fill[4]; 630 | int i; 631 | bool niss = false; 632 | 633 | result[0] = '\0'; // Initialize result string 634 | 635 | for (i = 0; i < alg->len; i++) { 636 | if (!niss && alg->inv[i]) 637 | strcpy(fill, i == 0 ? "(" : " ("); 638 | if (niss && !alg->inv[i]) 639 | strcpy(fill, ") "); 640 | if (niss == alg->inv[i]) 641 | strcpy(fill, i == 0 ? "" : " "); 642 | 643 | strcat(result, fill); 644 | strcat(result, move_string(alg->move[i])); 645 | 646 | niss = alg->inv[i]; 647 | } 648 | 649 | if (niss) 650 | strcat(result, ")"); 651 | 652 | char alg_len[5]; 653 | sprintf(alg_len, " (%d)", alg->len); 654 | strcat(result, alg_len); 655 | 656 | return result; 657 | } 658 | 659 | /* Similar to print_alglist defined in alg.c except it returns a list of strings */ 660 | char** alglist_to_strings(AlgList *alglist) { 661 | char **result = malloc((alglist->len + 1) * sizeof(char*)); // Adjust the size as needed 662 | 663 | int resultLen = 0; 664 | for (AlgListNode *i = alglist->first; i != NULL; i = i->next, resultLen++) { 665 | char *alg_string = alg_to_string(i->alg); 666 | result[resultLen] = alg_string; 667 | } 668 | 669 | result[resultLen] = NULL; // Add NULL to the end of the array 670 | 671 | return result; 672 | } 673 | 674 | static bool 675 | read_scramble(int c, char **v, CommandArgs *args) 676 | { 677 | int i, k, n; 678 | unsigned int j; 679 | char *algstr; 680 | 681 | if (c < 1) { 682 | fprintf(stderr, "Error: no scramble given?\n"); 683 | return false; 684 | } 685 | 686 | for(n = 0, i = 0; i < c; i++) 687 | n += strlen(v[i]); 688 | 689 | algstr = malloc((n + 1) * sizeof(char)); 690 | k = 0; 691 | for (i = 0; i < c; i++) 692 | for (j = 0; j < strlen(v[i]); j++) 693 | algstr[k++] = v[i][j]; 694 | algstr[k] = 0; 695 | 696 | args->scramble = new_alg(algstr); 697 | free(algstr); 698 | 699 | if (args->scramble->len == 0) 700 | fprintf(stderr, "Error reading scramble\n"); 701 | 702 | return args->scramble->len > 0; 703 | } 704 | 705 | static bool 706 | read_scrtype(CommandArgs *args, char *str) 707 | { 708 | int i; 709 | 710 | for (i = 0; scrtypes[i] != NULL; i++) { 711 | if (!strcmp(scrtypes[i], str)) { 712 | strcpy(args->scrtype, scrtypes[i]); 713 | return true; 714 | } 715 | } 716 | 717 | return false; 718 | } 719 | 720 | static bool 721 | read_step(CommandArgs *args, char *str) 722 | { 723 | int i; 724 | 725 | for (i = 0; steps[i] != NULL; i++) { 726 | if (!strcmp(steps[i]->shortname, str)) { 727 | args->step = steps[i]; 728 | return true; 729 | } 730 | } 731 | 732 | return false; 733 | } 734 | 735 | /* Public functions implementation *******************************************/ 736 | 737 | void 738 | free_args(CommandArgs *args) 739 | { 740 | if (args == NULL) 741 | return; 742 | 743 | if (args->scramble != NULL) 744 | free_alg(args->scramble); 745 | if (args->opts != NULL) 746 | free(args->opts); 747 | 748 | /* step and command must not be freed, they are static! */ 749 | 750 | free(args); 751 | } 752 | 753 | CommandArgs * 754 | new_args(void) 755 | { 756 | CommandArgs *args = malloc(sizeof(CommandArgs)); 757 | 758 | args->success = false; 759 | args->scrstdin = false; 760 | args->scramble = NULL; /* initialized in read_scramble */ 761 | args->opts = malloc(sizeof(SolveOptions)); 762 | 763 | /* step and command are static */ 764 | args->step = steps[0]; /* default: first step in list */ 765 | args->command = NULL; 766 | args->pd = NULL; 767 | 768 | return args; 769 | } -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Web Nissy 8 | 9 | 68 | 69 | 70 | 279 | 280 | 345 | 346 | 347 | 348 | 352 | 353 |
354 | {% if error %} 355 |

{{ error }}

361 | {% endif %} 362 |
363 |
364 |

Scram

365 | 366 |

Niss

367 | 368 | 369 |
370 |
    371 |
372 |
373 |
374 |

Scramble Type:

375 | 376 | 377 |
378 |

{{ scramble }}

379 |
380 |

This is a nissy shell for advanced users. Enter "commands" for a list of commands.

381 | 386 |
387 | 397 |

What is Web Nissy?

398 |

399 | Web Nissy is just like Nissy, except it runs in a website. 400 |

401 |

Why use Web Nissy?

402 |
    403 |
  • Don't have to generate or download massive tables
  • 404 |
  • Easier to use
  • 405 |
  • Chain together multiple steps
  • 406 |
  • RZP and JZP
  • 407 |
408 |

What is Nissy?

409 |

410 | Nissy is a tool for the Fewest Moves Challenge (FMC). 411 | It helps you find good EOs, DRs, and HTRs while using niss. 412 |

413 |

414 | NISS stands for Normal Inverse Scramble Switch, and it is a very common technique for FMC. 415 | This tool is essentially a Rubik's Cube solver that can get you to a given state using niss. 416 |

417 |

418 | Sebastiano Tronto created the original nissy that this website is based on. 419 | You can learn more here. 420 |

421 |

Who should use Web Nissy?

422 |

423 | Web Nissy is helpful for FMC solvers who want to improve their skills and verify whether their solution is efficient. 424 |

425 |

426 | For example, you can put in a scramble and see if there are any good EOs that you missed. 427 | Or you can put in a scramble and a partial solution, then see what the best continuations are. 428 |

429 |
430 |
431 | 432 | 433 | 791 | 792 | 793 | 794 | 801 | 802 | --------------------------------------------------------------------------------