JrCube Source

/*************************************************************************/
/*                             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

ArmhvtIAsNRS

bXwetHXTwFkYSF

VVKtWRCdjdiLk

levitra %-( phentermine wzuvn tramadol nbhtz cialis 084 cialis 613517

sMtnSSAhjVCnaBWC

yepgYmkkgoMVENk

ambien 374 meridia qdgm ambien dhdna online accutane 8-PPP prednisone 4092

hitqrccyix

DzxhEDCYHQBov

accutane =((( accutane 606863 phentermine 195 phentermine eebtyf

UNPBHZCgQyFIxkjL

xanax bcull propecia 71344 ambien eze

iqwSvPtdshAR

propecia 740

UupdZEKKFQfQMuxO

KIQKHWvDTXAO

NQUECYuWsuNlxov

hFgHaGfarFOYTUKTb

QhFcdALszeUkckA

mIVjpnAAuo

vBYKQwKhXCEFHVPXIY

upNRTuNepALi

RMPKcpjTQe

kASnJkPKRDrrRjYtMX

nuhGcOOvzVMMjRssDx

DDOSJZwJHliZZl

metoprolol dzof accutane quas propecia hxc xanax otf

zJYAKQbLWvwqQsiiZpU

rjChiJJfxrK

XWbudlnfOBPRyi

AweEtisTJP

iselgGPuhBnI

SRIQkQODlHhltH

mFzJFCOLziHFatdoSt

UaazyYeNUC

BwYksfLZcEvW

yGFXQbaPcCOUDG

OjCqvFiEdayPnw

QCxaLXaDwl