/*************************************************************************/ /* JrCube 0.9.5 */ /* by */ /* Steven L. Elliott */ /* */ /* This program will form a complete database of all possible */ /* permutations of the 2x2x2 Rubik's cube, known as Rubik's Jr, and then */ /* uses that database to solve a provided cube in a minimum number of */ /* twists. The 3.6 MiB database is generated using the '-g' option. */ /* */ /* Indent : indent -gnu -nut -i4 -bli0 -npcs -nbad jrcube.c */ /*************************************************************************/ #define _GNU_SOURCE /* Needed for asprintf() */ #include <errno.h> #include <getopt.h> #include <libgen.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> #include <unistd.h> /* TODO: Come up with a better system to detect what features are available. */ #ifdef __linux__ #define HAVE_MMAP 1 #endif #ifdef HAVE_MMAP #include <fcntl.h> #include <sys/mman.h> #endif /* Types */ /* For each of the eight locations that make up the larger cube the number of * the smaller cube that is there and it's rotation. */ typedef struct { int piece; /* Piece number 0-7 */ int rot; /* 0-not rotated 1-to the left 2-to the right */ } mini_cube_t; /* Item in the sides table. See that table for details. */ typedef struct { int poss[4]; int rots[4]; } side_t; /* Item in the orienting_turns table. See that table for details. */ typedef struct { int turns[4]; } orienting_turns_t; /* The state of a particular cube as well as previous and next pointers. */ typedef struct cube_s { struct cube_s *prev; struct cube_s *next; mini_cube_t pos[8]; int index; } cube_t; /* An item in a linked list of items to be freed. */ typedef struct freeitem_s { struct freeitem_s *next; void *ptr; int is_mmap; size_t size; } freeitem_t; /* Enumerations */ /* Log level argument passed in. */ typedef enum level_e { level_debug, level_message, level_warning, level_fatal } level_t; /* Constants */ #define LOG_LEVEL level_message #define NOOP 0 #define GENERATE 1 #define SOLVE 2 #define TEST 4 #define DB_SIZE 3674160 #define MAX_CF_NAMES 200 #define MAX_TWISTS 0x7f #define LOWER ('a' - 'A') /* Add this in ASCII to convert to lower case. */ #ifndef JC_LIB_DIR #define JC_LIB_DIR "/usr/local/lib/jrcube" #endif /* Globals */ /* Linked list to be freed later. */ static freeitem_t *freeitem; /* Calculated at runtime based on other values. This is used to calculate the * index for a cube. */ static int digit_value[13]; /* Global tables */ /* When translating to and from an index the cube can be thought of as a * mixed base number. Each entry indicates the base of that digit. The * first row is for the rotations and the second row is for pieces. * The angle of the last corner is deduced. */ static const int digit_base[13] = { 3, 3, 3, 3, 3, 3, 1, 2, 3, 4, 5, 6, 7 }; /* Each side of the cube consists of four smaller cubes. When a twist is * applied to a side the four smaller cubes that make up that side are * rearranged in a way that can be described as an item being removed from * a sequence and pushed onto the other end of the sequence: * ABCD -> DABC * What the following table does is for each twist that can be done to a side * the index in the sequence (ABCD in the example above) is mapped onto the * position in the cube. */ static const side_t sides[] = { {{4, 0, 2, 6}, {2, 1, 2, 1}}, /* x+ */ {{1, 5, 7, 3}, {2, 1, 2, 1}}, /* X+ */ {{1, 0, 4, 5}, {0, 0, 0, 0}}, /* y+ */ {{2, 3, 7, 6}, {0, 0, 0, 0}}, /* Y+ */ {{0, 1, 3, 2}, {2, 1, 2, 1}}, /* z+ */ {{5, 4, 6, 7}, {2, 1, 2, 1}} /* Z+ */ }; /* A series of indexes into sides that indicates the turns required to * orient piece zero to positions zero. Terminated by -1. */ /* *INDENT-OFF* */ static const orienting_turns_t orienting_turns[] = { {{-1, -1, -1, -1}}, /* piece 0 at 0 */ {{ 3, -1, -1, -1}}, /* piece 0 at 1 */ {{ 5, -1, -1, -1}}, /* piece 0 at 2 */ {{ 5, 5, -1, -1}}, /* piece 0 at 3 */ {{ 1, -1, -1, -1}}, /* piece 0 at 4 */ {{ 3, 3, -1, -1}}, /* piece 0 at 5 */ {{ 1, 1, -1, -1}}, /* piece 0 at 6 */ {{ 1, 1, 3, -1}} /* piece 0 at 7 */ }; /* *INDENT-ON* */ /* Utility functions */ /* Add memory to a list of items that are to be freed later. */ static void freeitem_add(void *ptr, int is_mmap, size_t size) { freeitem_t *fi = malloc(sizeof(*fi)); if (!fi) { /* If memory has been exhausted then just leak this item. */ return; } fi->next = freeitem; fi->ptr = ptr; fi->is_mmap = is_mmap; fi->size = size; freeitem = fi; } /* Iterate through the freeitem linked list of items and either free or unmap * them. */ static void freeitem_free() { freeitem_t *fi; freeitem_t *fi_next; for (fi = freeitem; fi; fi = fi_next) { if (fi->is_mmap) { #ifdef HAVE_MMAP /* Don't bother with error handling since we are shutting down. */ munmap(fi->ptr, fi->size); #endif } else { free(fi->ptr); } fi_next = fi->next; free(fi); } } /* Error handling functions */ /* Logs a message to stderr. If the level is high enough then the errno * may be decoded. For fatal errors this program is aborted. */ static void log_msg(level_t level, const char *fmt, ...) { va_list ap; if (level < LOG_LEVEL) { return; } va_start(ap, fmt); vfprintf(stderr, fmt, ap); if ((level >= level_warning) && errno) { fprintf(stderr, "errno=%d - %s\n", errno, strerror(errno)); } if (level >= level_fatal) { fprintf(stderr, "FATAL ERROR. Exiting.\n"); exit(1); } va_end(ap); } /* Conversion functions */ /* Convert a list of unique items ("perms") to a mixed based number ("digits") * that is equal to the unique items' index if the list of all possible * unique items were put in reverse order. For example consider the various * permutations of 'A', 'B' and 'C' if they were arranged in reverse order and * what their "digits" would be: * * Perms Digits Index * CBA 000 0 * CAB 010 1 * BCA 100 2 * BAC 110 3 * ACB 200 4 * ABC 210 5 * * In the above the index is determined as a function of the digits where * each digit has some weighted value. The first (most significant) digit is * worth 2, the second is worth 1 and the last zero. So, BAC, for example, * is worth 2 * 1 + 1 * 1 + 0 * 1 = 3 * * Each digit is equal to the number of less significant items that follow the * item that corresponds to the digit that are greater in value. For example * For BAC the digit that corresponds to the 'B' is 1 because there is one * less significant item that follows that is greater in value (the 'C' * but not the 'A'). * * The above definition, particularly the part about it being in reverse order, * was chosen so that the solved position is at index zero. The solved position * involves placing the lowest numbered pieces in the least significant * locations */ static void digits_from_perms(int *digits, int *perms, int num) { int digit_val; int item_index; int item_scan; memset(digits, 0, num * sizeof(int)); for (item_index = num - 1; item_index >= 0; item_index--) { digit_val = 0; for (item_scan = item_index - 1; item_scan >= 0; item_scan--) { if (perms[item_scan] > perms[item_index]) { digit_val++; } } digits[item_index] = digit_val; } } /* This does the reverse of digits_from_perms(). See that functions comments * for details. */ static void perms_from_digits(int *perms, int *digits, int num) { int item_index; int perm_buff; int perm_sort; for (item_index = 0; item_index < num; item_index++) { perms[item_index] = num - item_index; } for (item_index = num - 1; item_index >= 0; item_index--) { /* Place the number indicated by the mixed case number first. */ perm_buff = perms[item_index]; perms[item_index] = perms[digits[item_index]]; perms[digits[item_index]] = perm_buff; if (digits[item_index] < (item_index - 1)) { /* Make sure that the remaining perms are in descending order. */ for (perm_sort = digits[item_index]; perm_sort < (item_index - 1); perm_sort++) { perms[perm_sort] = perms[perm_sort + 1]; } perms[item_index - 1] = perm_buff; } } } /* Converts a numeric rotation to something descriptive. */ static char rot_to_char(int rot) { switch (rot) { case 0: return '-'; case 1: return 'L'; case 2: return 'R'; default: return '?'; /* This should not happen. */ } } /* Converts a descriptive rotation to something numeric. */ static int char_to_rot(char rot) { switch (rot) { case '-': return 0; case 'L': return 1; case 'R': return 2; default: return 999; /* This should not happen. */ } } /* Converts a numeric position to something descriptive. The 8 small cubes * are numbered 0 through 7. They are enumerated by looping through the cubes * X, Y and Z axis with the X axis in the inner most loop. * */ static char * pos_to_str(int pos, char *str) { int x_front = 1 & pos; int y_front = 2 & pos; int z_front = 4 & pos; sprintf(str, "%c%c%c", 'X' + (x_front ? 0 : LOWER), 'Y' + (y_front ? 0 : LOWER), 'Z' + (z_front ? 0 : LOWER)); return str; } /* Convert a numeric piece to something descriptive (the colors that appears * on that piece / small cube). */ static char * piece_to_str(int piece, char *str) { int x_front = 1 & piece; int y_front = 2 & piece; int z_front = 4 & piece; sprintf(str, "%c%c%c", (x_front ? 'R' : 'O'), (y_front ? 'B' : 'G'), (z_front ? 'W' : 'Y')); return str; } /* Output functions */ /* Output the state of the cube. Each small cube / piece is displayed as * well as the rotation. Note that the numeric columns demonstrate the format * of input files. */ static void cube_print(cube_t * cube) { char piece_str[4]; char pos_str[4]; int pos_index; printf("Pos #\tPos\tPiece #\tPiece\tRotation\n"); for (pos_index = 0; pos_index <= 7; pos_index++) { printf("%d\t%s\t%d\t%s\t%c\n", pos_index, pos_to_str(pos_index, pos_str), cube->pos[pos_index].piece, piece_to_str(cube->pos[pos_index].piece, piece_str), rot_to_char(cube->pos[pos_index].rot)); } } static void print_twist(int front, char axis, int sign) { printf("%c%c ", axis + (front ? 0 : ('a' - 'A')), (sign > 0) ? '+' : '-'); } /* Cube functions */ /* Make sure that the rotations of the pieces are such that the cube is * solvable. Rotating one of the eight pieces without making any other changes * results in a cube that can not be solved. */ static int cube_rot_check(cube_t * cube) { int pos_index; int rot_total = 0; for (pos_index = 0; pos_index <= 7; pos_index++) { rot_total += cube->pos[pos_index].rot; } return rot_total % 3; } /* A lower level version of cube_twist(). It applies a twist by taking a * mapping for the sides and rotations (which small cube gets mapped to which * position, etc.). See cube_twist() for details. */ static void cube_rot_circulate(cube_t * cube, const int rots[], const int poss[], int num, int sign) { int item_index; mini_cube_t mc_buf; for (item_index = 0; item_index < num; item_index++) { cube->pos[poss[item_index]].rot = (cube->pos[poss[item_index]].rot + rots[item_index]) % 3; } if (sign == 1) { mc_buf = cube->pos[poss[0]]; for (item_index = 0; item_index < (num - 1); item_index++) { cube->pos[poss[item_index]] = cube->pos[poss[item_index + 1]]; } cube->pos[poss[num - 1]] = mc_buf; } else { mc_buf = cube->pos[poss[num - 1]]; for (item_index = num - 1; item_index > 0; item_index--) { cube->pos[poss[item_index]] = cube->pos[poss[item_index - 1]]; } cube->pos[poss[0]] = mc_buf; } } /* Reduce the size required for the database by taking advantage of symmetry. * Before attempting to find the index for a given cube it is first oriented * into a standard position such that the zero cube (piece 0) is in * position 0. Also, the cube is rotated so that the zero cube has no rotation. * This results in the zero cube always being it it's solved location and * rotation, so it can be ignored for the index calculation. */ static void cube_orient(cube_t * cube) { int pos_zero; /* Position of the zero cube. */ int side; int turn; int xside; /* The opposing parallel side. */ /* Find the location of the zero cube. */ for (pos_zero = 0; (pos_zero <= 7) && cube->pos[pos_zero].piece; pos_zero++); /* Rotate the entire cube so that the zero cube ends up in the zero * position. */ for (turn = 0; (side = orienting_turns[pos_zero].turns[turn]) != -1; turn++) { /* Turn the cube along the specified side. */ cube_rot_circulate(cube, sides[side].rots, sides[side].poss, 4, 1); /* Determine the opposite side and sign (ex., X+ becomes x-) and * perform that turn. The result is the entire cube is turned. */ xside = 1 ^ side; cube_rot_circulate(cube, sides[xside].rots, sides[xside].poss, 4, -1); } /* Rotate the entire cube along the diagonal axis that passes through * position 0 and position 7 so that the zero cube no longer has any * rotation. It turns out that rotating the entire cube along this * diagonal axis circulates smaller cubes around paths that are three cubes * long. */ switch ((3 - cube->pos[0].rot) % 3) { case 1: /* Zero cube at position 0 is rotated to the right. */ cube->pos[0].rot = (cube->pos[0].rot + 1) % 3; { const int rots[] = { 2, 2, 2 }; const int poss[] = { 4, 1, 2 }; cube_rot_circulate(cube, rots, poss, 3, 1); } { const int rots[] = { 1, 1, 1 }; const int poss[] = { 5, 3, 6 }; cube_rot_circulate(cube, rots, poss, 3, 1); } cube->pos[7].rot = (cube->pos[7].rot + 2) % 3; break; case 2: /* Zero cube at position 0 is rotated to the left. */ cube->pos[0].rot = (cube->pos[0].rot + 2) % 3; { const int rots[] = { 1, 1, 1 }; const int poss[] = { 2, 1, 4 }; cube_rot_circulate(cube, rots, poss, 3, 1); } { const int rots[] = { 2, 2, 2 }; const int poss[] = { 6, 3, 5 }; cube_rot_circulate(cube, rots, poss, 3, 1); } cube->pos[7].rot = (cube->pos[7].rot + 1) % 3; break; } } /* Get and index for a cube by first converting the positions and the rotations * to a mixed base number. See digits_from_perms() for details. */ static void cube_set_index(cube_t * cube) { cube_t cube_loc = *cube; int digit; int digits[13]; int pieces[7]; int pos_index; cube_orient(&cube_loc); for (pos_index = 1; pos_index <= 7; pos_index++) { pieces[pos_index - 1] = cube_loc.pos[pos_index].piece; if (pos_index != 7) { digits[pos_index - 1] = cube_loc.pos[pos_index].rot; } } digits_from_perms(digits + 6, pieces, 7); cube->index = 0; for (digit = 12; digit >= 0; digit--) { cube->index += digit_value[digit] * digits[digit]; } } /* A constructor that returns a new cube based on a specified cube. */ cube_t * cube_new_cube(cube_t * cube) { cube_t *cube_new; if (!(cube_new = malloc(sizeof(*cube_new)))) { log_msg(level_fatal, "Unable to allocate %d bytes for a cube_new\n", DB_SIZE); } *cube_new = *cube; /* Assume the caller will keep a reference to the cube_new returned. */ return cube_new; } /* A constructor that creates a new cube by loading it from a file. The cube * file has three columns: * Position Number Piece Number Rotation * Errors reading the specified file causes this program to abort. */ static cube_t * cube_new_fname(char *cf_name) { FILE *cf_fp; char rot[2]; /* Character and null terminator. */ cube_t *cube; int piece; int pos; int total_rot; if (!(cf_fp = fopen(cf_name, "r"))) { log_msg(level_fatal, "Could not open cube file for read: %s\n", cf_name); } printf("Loading %s:\n\n", cf_name); cube = calloc(1, sizeof(*cube)); while (3 == fscanf(cf_fp, "%d%d%1s\n", &pos, &piece, rot)) { if ((pos < 0) || (pos > 7)) { log_msg(level_fatal, "Position with value %d is outside the allowed " "[0, 7] range.\n", pos, pos); } if ((piece < 0) || (piece > 7)) { log_msg(level_fatal, "Piece with value %d is outside the allowed " "[0, 7] range.\n", piece, piece); } cube->pos[pos].piece = piece; cube->pos[pos].rot = char_to_rot(*rot); if (cube->pos[pos].rot == 999) { log_msg(level_fatal, "Invalid rotation of '%s' specified. Only " "'L', '-' and 'R' are allowed.\n", rot); } } fclose(cf_fp); cube_print(cube); if ((total_rot = cube_rot_check(cube))) { log_msg(level_fatal, "The pieces have an additional rotation of %d " "(the sum of the rotations mod 3 is not zero). Can not " "solve.\n", total_rot); } cube_set_index(cube); return cube; } /* A constructor that creates a new cube from a specified index. */ static cube_t * cube_new_index(int index) { cube_t *cube; int digit; int digits[13]; int pieces[7]; int pos_index; int remaining = index; cube = calloc(1, sizeof(*cube)); memset(cube, 0, sizeof(cube)); for (digit = 12; digit >= 0; digit--) { digits[digit] = remaining / digit_value[digit]; remaining -= digits[digit] * digit_value[digit]; } perms_from_digits(pieces, digits + 6, 7); for (pos_index = 1; pos_index <= 7; pos_index++) { cube->pos[pos_index].piece = pieces[pos_index - 1]; if (pos_index != 7) { cube->pos[pos_index].rot = digits[pos_index - 1]; } else { /* The rotation of the last cube can be deduced. */ cube->pos[pos_index].rot = (3 - cube_rot_check(cube)) % 3; } } cube->index = index; return cube; } /* Shallow free of the specified cube. */ static void cube_free(cube_t * cube) { if (cube) { free(cube); } } /* Deep free of the specified cube. The term "history" is used since the * linked list is traversed backwards to free the older cubes. */ static void cube_free_history(cube_t * cube) { cube_t *cube_prev; while (cube) { cube_prev = cube->prev; cube_free(cube); cube = cube_prev; } } /* Perform a given twist by causing the pieces to switch places. The rotations * are updated for pieces that cross the X-Z plane. */ static cube_t * cube_twist(cube_t * cube, int front, char axis, int sign) { cube_t *next_cube; int side; next_cube = cube_new_cube(cube); next_cube->prev = cube; cube->next = next_cube; side = 2 * (axis - 'X') + front; cube_rot_circulate(next_cube, sides[side].rots, sides[side].poss, 4, sign); cube_set_index(next_cube); return next_cube; } /* Undoes a twist by reverting to the saved cube and index. */ static cube_t * cube_untwist(cube_t * cube, int do_free) { cube_t *prev_cube; /* This assumes that when a twist is undone it is not redone again later. */ prev_cube = cube->prev; if (do_free) { cube_free(cube); } prev_cube->next = NULL; return prev_cube; } /* Try each possible twist and find the twist / twist that works best (that * gets the cube closer to being solved). */ cube_t * cube_best_twist(cube_t * cube, char *db, int mode, int *num_good_twists) { char axis; char axis_best; cube_t *cube_best = NULL; int num_twists; int score; int score_best; int sign; int sign_best; if (num_good_twists) { *num_good_twists = 0; } if ((cube->index < 0) || (cube->index >= DB_SIZE)) { log_msg(level_fatal, "Invalid position. index=%d\n", cube->index); } num_twists = db[cube->index]; log_msg(level_debug, "num_twists=%d\n", num_twists); if (SOLVE & mode) { if (!num_twists) { /* The cube is already solved. */ return NULL; } /* Start with the current score so that only twists that get closer to * solved are considered. */ score_best = num_twists << 16; } for (axis = 'X'; axis <= 'Z'; axis++) { for (sign = -1; sign <= 1; sign += 2) { cube = cube_twist(cube, 1, axis, sign); if (GENERATE & mode) { if (db[cube->index] > (num_twists + 1)) { db[cube->index] = num_twists + 1; if (num_good_twists) { (*num_good_twists)++; } } } if (SOLVE & mode) { #ifdef RND_SOLUTIONS /* Pick a twist at random that gets it closer to solved . */ score = (db[cube->index] << 16) + (rand() % 0x10000); #else /* Construct the score so that following is considered in * order of importance: * 1) Twists that reduce the number of twists from solved. * 2) Twists that are in a positive direction. * 3) First 'X", then 'Y' and finally 'Z'. */ score = (db[cube->index] << 16) + ((1 - sign) << 8) + axis; #endif log_msg(level_debug, "score=%06x axis=%c sign=%2d\n", score, axis, sign); if (score < score_best) { score_best = score; log_msg(level_debug, "score_best=%06x\n", score_best); if (cube_best) { cube_free(cube_best); } cube_best = cube; axis_best = axis; sign_best = sign; if (num_good_twists) { (*num_good_twists)++; } } } cube = cube_untwist(cube, cube != cube_best); } } if ((SOLVE & mode) && cube_best) { print_twist(1, axis_best, sign_best); } return cube_best; } /* Solve the cube by starting with init_cube and seeing which twists, according * to the database, brings the cube closer to being solved. */ static cube_t * cube_solve(cube_t * cube, char *db) { cube_t *cube_last = NULL; while ((cube = cube_best_twist(cube, db, SOLVE, NULL))) { cube_last = cube; /* The last non-null cube found. */ } if (!cube_last) { /* Due to previous input checking the following should never happen. */ log_msg(level_fatal, " Unable to find any twist that improves the " "specified cube.\n"); } if (cube_last->index) { log_msg(level_warning, " Index %d instead of 0. Solution not valid.\n", cube_last->index); } putchar('\n'); return cube_last; } /* Database functions */ /* For a given index set the number of twists from solved. Although currently * the database is just an array this allows for some abstraction. */ static int dbase_set_twists(char *db, int num_twists) { cube_t *cube; int index; int num_changes; int num_total = 0; for (index = 0; index < DB_SIZE; index++) { if (db[index] == num_twists) { cube = cube_new_index(index); cube_best_twist(cube, db, GENERATE, &num_changes); cube_free(cube); num_total += num_changes; } } printf("%d positions found %d twists from solved.\n", num_total, num_twists + 1); return num_total; } /* A constructor that creates a new database by allocating an array, marking the * solved position as zero twists from being solved and then working backwards * (one positions one twist from solved are marked as one twist from solved, * etc.). */ static char * dbase_new_fname_gen(char *fname, int *do_mmap) { #ifdef HAVE_MMAP int db_fd; #else FILE *db_fp; #endif char *db; int num_twists; struct stat db_stat; /* Delete the db, if it exists. */ if (stat(fname, &db_stat) != -1) { if (remove(fname) == -1) { log_msg(level_fatal, "Unable to remove existing database at %s\n", fname); } } #ifdef HAVE_MMAP *do_mmap = 1; /* Although 0666 is used for the following the umask will limit the * permissions on the resulting file. */ if ((db_fd = open(fname, O_CREAT | O_RDWR, 0666)) == -1) { log_msg(level_fatal, "Unable to open the database at %s\n", fname); } /* Make sure a sparse file exists by seeking forward and writing a single * zero at the end. */ lseek(db_fd, DB_SIZE - 1, SEEK_SET); write(db_fd, "", 1); if ((db = mmap(NULL, DB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, db_fd, 0)) == MAP_FAILED) { log_msg(level_fatal, "Unable to mmap the database at %s\n", fname); } close(db_fd); #else *do_mmap = 0; if (!(db = malloc(DB_SIZE))) { log_msg(level_fatal, "Unable to allocate %d bytes for db\n", DB_SIZE); } #endif memset(db, MAX_TWISTS, DB_SIZE); db[0] = 0; /* Mark the first position as solved. */ for (num_twists = 0; num_twists < MAX_TWISTS; num_twists++) { if (!dbase_set_twists(db, num_twists)) { /* No changes to the database were made. */ break; } } #ifndef HAVE_MMAP if (!(db_fp = fopen(fname, "wb"))) { log_msg(level_fatal, "Could not open database for write: %s\n", fname); } fwrite(db, DB_SIZE, 1, db_fp); fclose(db_fp); #endif return db; } /* A constructor that creates a new database by loading it from a specified file. */ static char * dbase_new_fname_load(const char *fname, int *do_mmap) { #ifdef HAVE_MMAP int db_fd; #else FILE *db_fp; #endif char *db; struct stat db_stat; if (stat(fname, &db_stat) == -1) { log_msg(level_fatal, "Unable to stat the database at %s\n", fname); } if (db_stat.st_size != DB_SIZE) { log_msg(level_fatal, "Database at %s is %d bytes instead of %d\n", fname, db_stat.st_size, DB_SIZE); } #ifdef HAVE_MMAP *do_mmap = 1; if ((db_fd = open(fname, O_RDONLY)) == -1) { log_msg(level_fatal, "Unable to open the database at %s\n", fname); } if ((db = mmap(NULL, DB_SIZE, PROT_READ, MAP_PRIVATE, db_fd, 0)) == MAP_FAILED) { log_msg(level_fatal, "Unable to mmap the database at %s\n", fname); } close(db_fd); #else *do_mmap = 0; if (!(db = malloc(DB_SIZE))) { log_msg(level_fatal, "Unable to allocate %d bytes to load the " "db\n", DB_SIZE); } if (!(db_fp = fopen(fname, "rb"))) { log_msg(level_fatal, "Could not open database for read: %s\n", fname); } /* TODO: Add other validation. Maybe the database should have a magic * header. Maybe it should be stat'd to make sure it is the right size. */ if (fread(db, DB_SIZE, 1, db_fp) < 1) { fclose(db_fp); log_msg(level_fatal, "Short read for the db: %s\n", fname); } fclose(db_fp); #endif return db; } /* Misc functions */ /* Do whatever has to be done for shutdown. */ static void deinit() { freeitem_free(); } /* Initialize global arrays that are redundant to other data (arrays where it * would be inefficient for them to have their own initializers). */ static void init() { int digit; int digit_val = 1; for (digit = 0; digit <= 12; digit++) { digit_value[digit] = digit_val; digit_val *= digit_base[digit]; } atexit(deinit); } /* Test the code by applying various twists and then the corresponding * anti-twist. After each anti-twist the cube should return to it's original * solved state with an index of 0 where dbase[0] = 0 (the solved cube, * stored at index 0, is 0 twists from being solved). */ static void test(const char *db) { cube_t *cube; printf("The database is %sloaded.\n\n", db ? "" : "NOT "); cube = cube_new_index(0); cube = cube_twist(cube, 1, 'X', 1); printf("X+ index=%d from_solved=%d\n", cube->index, db ? db[cube->index] : 0); cube_print(cube); cube = cube_twist(cube, 1, 'X', -1); printf("\nX- index=%d from_solved=%d\n", cube->index, db ? db[cube->index] : 0); cube_print(cube); cube = cube_twist(cube, 0, 'Y', 1); printf("\nY+ index=%d from_solved=%d\n", cube->index, db ? db[cube->index] : 0); cube_print(cube); cube = cube_twist(cube, 0, 'Y', -1); printf("\nY- index=%d from_solved=%d\n", cube->index, db ? db[cube->index] : 0); cube_print(cube); cube = cube_twist(cube, 0, 'Z', 1); printf("\nZ+ index=%d from_solved=%d\n", cube->index, db ? db[cube->index] : 0); cube_print(cube); cube = cube_twist(cube, 0, 'Z', -1); printf("\nZ- index=%d from_solved=%d\n", cube->index, db ? db[cube->index] : 0); cube_print(cube); cube_free_history(cube); } /* Simple usage statement. */ static void usage(const char *name) { fprintf(stderr, "Usage: %s -g dbase\n", name); fprintf(stderr, "Usage: %s [-l dbase] [-n solutions] -s cube file " "[-s cube file ...]\n", name); fprintf(stderr, "Usage: %s -t\n", name); exit(1); } /* Main entry point. This parses command line options and calls everything * else. */ int main(int argc, char *argv[], char *envp[]) { char *cf_name[MAX_CF_NAMES]; char *db; char *dbase_fname = NULL; char opt_char; char opt_err = 0; cube_t *cube_init; cube_t *cube_solved; int cube_file_index; int cube_files = 0; int do_mmap; int mode = NOOP; int sol_count; int solutions = 1; int solve_args = 0; init(); while (EOF != (opt_char = getopt(argc, argv, "g:l:n:s:t"))) { switch (opt_char) { case 'g': mode |= GENERATE; dbase_fname = strdup(optarg); break; case 'l': mode |= SOLVE; solve_args++; dbase_fname = strdup(optarg); break; case 'n': mode |= SOLVE; solve_args++; solutions = atoi(optarg); break; case 's': mode |= SOLVE; solve_args++; if (cube_files == 200) { log_msg(level_fatal, "Too many cube files specified."); break; } cf_name[cube_files++] = optarg; break; case 't': mode |= TEST; break; default: opt_err = 1; } } if (!dbase_fname) { asprintf(&dbase_fname, "%s/%s.db", JC_LIB_DIR, basename(argv[0])); } if (!dbase_fname) { log_msg(level_fatal, "Unable to allocate memory for the database name."); } freeitem_add(dbase_fname, 0, 0); if (!mode || (mode > TEST) || opt_err || ((mode == GENERATE) && (argc != 3)) || ((mode == SOLVE) && !cube_files) || ((mode == SOLVE) && (argc != (1 + 2 * solve_args)))) { usage(basename(argv[0])); } if (GENERATE & mode) { db = dbase_new_fname_gen(dbase_fname, &do_mmap); } else { db = dbase_new_fname_load(dbase_fname, &do_mmap); } freeitem_add(db, do_mmap, DB_SIZE); if (TEST & mode) { test(db); } else if (SOLVE & mode) { for (cube_file_index = 0; cube_file_index < cube_files; cube_file_index++) { cube_init = cube_new_fname(cf_name[cube_file_index]); printf("\nSolutions:\n"); for (sol_count = 1; sol_count <= solutions; sol_count++) { printf(" #%d: ", sol_count); cube_solved = cube_solve(cube_init, db); if (cube_solved) { cube_free_history(cube_solved); } } if (cube_file_index != (cube_files - 1)) { /* Newline if not the last cube. */ putchar('\n'); } } } return 0; }