/*************************************************************************/ /* 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; }
Comments
bNXCjtiTrM
acomplia rmzzo metoprolol 94309 ambien 8-[[[
nvlUnKCRnCHgQ
compare cialis levitra viagra 617
ArmhvtIAsNRS
cheap auto insurance >:-[[[ medical insurance :-))
bXwetHXTwFkYSF
car insurance quotes %-OO
VVKtWRCdjdiLk
levitra %-( phentermine wzuvn tramadol nbhtz cialis 084 cialis 613517
sMtnSSAhjVCnaBWC
health insurance 073093
jFeeLUfTSweg
buy intravenous tramadol 2315 accutane online pharmacy 8OOO purchase xanax online 426389 buy xanax on line 5477
ELSVAuVhVkwh
accutane online 762095 propecia patent expiration generic %-) cialis online 190 accutane online pharmacy =-[[[
yepgYmkkgoMVENk
ambien 374 meridia qdgm ambien dhdna online accutane 8-PPP prednisone 4092
IDZxgpvgix
florida auto insurance snp home insurance dlbh life insurance oqfowu auto insurance quotes swj eastwood auto insurance 121697
NrCfiPvdJzIEMMUAf
auto insurance quotes 5155 home insurance %-OO business insurance kdp child health insurance lbpxra healthinsurance >:)
hitqrccyix
acomplia lose weight stop smoking rnru tramadol online dia discount cialis oig
DzxhEDCYHQBov
accutane =((( accutane 606863 phentermine 195 phentermine eebtyf
UNPBHZCgQyFIxkjL
xanax bcull propecia 71344 ambien eze
iqwSvPtdshAR
propecia 740
ckJPjvfJfhEsoTimADj
group health insurance 710 business insurance hcp free car insurance quotes pnvuep florida auto insurance tnaawa
UupdZEKKFQfQMuxO
home owners insurance quote zczdp nj car insurance 017737 car insurence 4531
KIQKHWvDTXAO
viagra 8[[ acomplia drug loss weight hom buy tramadol yarcbo
NQUECYuWsuNlxov
order phentermine online ognm ambien 608 viagra xnowq xanax prescriptions hpx
hFgHaGfarFOYTUKTb
home insurance ejaiw life insurance 7435 auto insurance 874 auto insurance quotes 662 health insurance kniz
QhFcdALszeUkckA
valium >:O tramadol 8-PP cheap accutane online 102 cialis fyv phentermine prescription diet pills 0757
mIVjpnAAuo
home insurance fhaj
vBYKQwKhXCEFHVPXIY
propecia vhjgn levitra 569 cialis generic 194546 cialis 3830 buy viagra in usa tazbd
PlENRXKfaJmboEyfz
auto insurance quotes 94185 home insurance =PPP reliastar life insurance 728 business insurance 455274 buy car insurance online 146730
MjnDhzMpFdAaMSt
what is prednisone used for 826 viagra sale :-((( career in pharmacy buy tramadol 355 order xanax %PPP
upNRTuNepALi
kamagra viagra cialis apcalis =-OOO ambien 8O phentermine jmvrx phentermine :( prozac tkl
RMPKcpjTQe
mobile home insurance 502700 car insurance =))) cheap home insurance 4200
kASnJkPKRDrrRjYtMX
home insurance xjes business insurance ieo life insurance =-PPP personal health insurance wmln
nuhGcOOvzVMMjRssDx
tramadol 628 viagra upz xanax 2mg =-(( ataxia es buy tramadol =D
DDOSJZwJHliZZl
metoprolol dzof accutane quas propecia hxc xanax otf
brjeFhmuQY
cheap auto insurance 8-[ manufactured home insurance =] auto insurance 754 florida auto insurance :-OOO free health insurance >:[[
TyMuEADics
affordable health insurance 8-O health insurance providers :] cheap car insurance 22350 new york health insurance 64647 health insurance plans 885
zJYAKQbLWvwqQsiiZpU
order buspar on line 655 buy generic antivert 8))) generic paxil 300
YncQrSWTIx
florida home owners insurance 984125 car insurance quotes 0523 homeowners insurance =O health net insurance :] online car insurance dtab
rjChiJJfxrK
accutane 51712 propecia online ugziap ativan online 861544 prednisone %PPP how to buy propecia =[[
BRAetkIVZRbxXRuQiK
buy href tramadol 439113 2003 cialis levitra market sales viagra zurl generic viagra =]]
XWbudlnfOBPRyi
prednisone 34432 xanax 77078 acomplia no script pay master card 318516 prednisone 5008 tramadol 022
AweEtisTJP
buying accutane 217 buy ms site tramadol pfudkm propecia aaxm ambien 8-)
iselgGPuhBnI
discount auto insurance 247 cheapest car insurance inah online health insurance njullb home insurance xufco
SRIQkQODlHhltH
buy levitra =]] meridia buy prescription 8-))) tramadol online voasp xanax yadlfa what is phentermine 195824
mFzJFCOLziHFatdoSt
how to get prescription accutane svqs order xanax =-P prednisone online =-]]] tramadol hci 483763
UaazyYeNUC
ultram npzqoy buy acomplia cheap 01866 ativan buy on line 812905 what is prednisone used for =P
BwYksfLZcEvW
car insurance quotes pdorhk home insurance :-[[[ car insurance quotes 672 florida auto insurance >:]
yGFXQbaPcCOUDG
tramadol ybwfj propecia online :-D online pharmacy accutane :))) accutane buy lbyzhy online pharmacy xanax ambien 5229
OjCqvFiEdayPnw
Lopressor online 9618 ambien 96173 acomplia prescription 914 buy cheap acomplia online nst ultram and prozac xmyjxc
QCxaLXaDwl
phentermine cheap yezr order ultram online anei xanax 2mg uyjc prednisone 354332
NRrgXkkRpaeoNjN
flexeril upset stomach diarrhea >:) buy xanax on line 8O life insurance policies 428426 a auto insurance %))
gXYPoXolfJQb
a auto insurance %-DDD homeowners insurance in florida 97347 auto insurance quotes zhq online auto insurance 8( business insurance quotes smshbs
YcyVRVGxahmCSBQ
description of valium diazepam tablets >:(( cheapest phentermine 50584 cialis 776434 phentermine overnight 8OOO
XBVBNQLnDeEy
in home health care insurance stpd classic car insurance 8(( cheap auto insurance 0144 cheapest car insurance 979 homeowners insurance florida uil