3
\$\begingroup\$

This program takes a text file and parses the initial cube start position, the pattern that the result should have and an optional start sequence. The cube is using numbers instead of colors for more exact positioning. Each face is represented by 9 numbers 3 rows of 3. The top face would be 1 2 3 4 5 6 7 8 9. The left face would be 10 11 12 13 14 15 16 17 18. etc. Speed is the most important thing to consider. On my computer with 16 cores it can solve a 7 move sequence in a couple of seconds. The time however grows by a factor of 12 with each move. So sequence of 10 moves would be hours. It will create multiple threads to find a sequence.

I can add the header files if needed.

Here is the code:

sequencefinder.cpp

#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <thread>

#include "cube.h"
#include "str.h"
#include "findsequence.h"


using namespace std;

int find_sequence(string& file_name, bool use_slice);
void read_cube_start(istream& inputstream, vector<face_val<face_val_type>>& cubestart);
void read_pattern(istream& inputstream, vector<face_val<face_val_type>>& pattern);
void read_sequence(istream& inputstream, string& sequence);
void readstring(istream& inputstream, string& str);
void readfaceval(istream& inputstream, face_val<face_val_type>& val);
int parse_args(int argc, char** argv);
int check_parameter(vector<string>& args, string searcharg, string& var);
static const vector<face_val<face_val_type>> init = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54};

static int max_depth = 8;


//************************************
// Method:    main
// FullName:  main
// Access:    public 
// Returns:   int
// Qualifier:
// Parameter: const int argc
// Parameter: char * * argv
//************************************
int main(const int argc, char** argv)
{   
    try
    {
        return parse_args(argc, argv);
    }
    catch (...)
    {
        cout << "Exception thrown." << endl;
        return  -1;
    }
}

//************************************
// Method:    parse_args
// FullName:  parse_args
// Access:    public 
// Returns:   int
// Qualifier:
// Parameter: const int argc
// Parameter: char * * argv
//
// Parse the arguments
//************************************
int parse_args(const int argc, char** argv)
{
    if (argc < 2)
    {
        cout << "No options specified." << endl;
        return -1;
    }

    string name;
    string matchpattern;
    string cubestring;
    string sequence;
    string reversesequence;
    string filename;
    string executesequence;
    string executereversesequence;
    string slice;
    string maxdepth;
    
    bool use_slice;
    
    vector<string> args;
    for (auto argindex = 1; argindex < argc; argindex++)
        args.emplace_back(argv[argindex]);

    auto result = check_parameter(args, "-c", cubestring);
    if (result < 0) return result;

    result = check_parameter(args, "-slice", slice);
    if (result < 0) return result;

    result = check_parameter(args, "-f", filename);
    if (result < 0) return result;

    result = check_parameter(args, "-depth", maxdepth);
    if (result < 0) return result;

    if (!maxdepth.empty())
    {
        max_depth = stoi(maxdepth);
    }
    
    use_slice = !slice.empty();
    
    if (!args.empty())
    {
        cout << "Unknown argument(s) ";
        for (auto& arg : args)
            cout << arg << " ";
        cout << endl;
        return -1;
    }

    if (!filename.empty())
    {
        result = find_sequence(filename, use_slice);
    }
    
    return result;
}

//************************************
// Method:    check_parameter
// FullName:  check_parameter
// Access:    public 
// Returns:   int
// Qualifier: // NOLINT(performance-unnecessary-value-param)
// Parameter: vector<string> & args
// Parameter: const string searcharg
// Parameter: string & var
//
// check a single parameter
//************************************
int check_parameter(vector<string>& args, const string searcharg, string& var)  // NOLINT(performance-unnecessary-value-param)
{
    auto argindex = 0;
    const auto argc = int(args.size());
    while (argindex < argc)
    {
        if (args[argindex++] == searcharg)
        {
            if (argindex >= argc)
            {
                cout << "No sequence specified with " << args[argindex] << "." << endl;
                return -1;
            }
            var = args[argindex];
            args.erase(args.begin() + (argindex - 1ll));
            args.erase(args.begin() + (argindex - 1ll));
            return 1;
        }
    }
    return 0;
}

//************************************
// Method:    read_cube_start
// FullName:  read_cube_start
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: istream & inputstream
// Parameter: vector<face_val<face_val_type>>& cubestart
//************************************
void read_cube_start(istream& inputstream, vector<face_val<face_val_type>>& cubestart)
{
    cubestart.clear();

    while (!inputstream.eof() && cubestart.size() < 54)
    {
        face_val<face_val_type> val;
        readfaceval(inputstream, val);
        cubestart.push_back(val);
    }   
}

//************************************
// Method:    read_pattern
// FullName:  read_pattern
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: istream & inputstream
// Parameter: vector<face_val<face_val_type>> & pattern
//************************************
void read_pattern(istream& inputstream, vector<face_val<face_val_type>>& pattern)
{
    pattern.clear();

    while (!inputstream.eof() && pattern.size() < 54)
    {
        face_val<face_val_type> val;
        readfaceval(inputstream, val);
        pattern.push_back(val);
    }
}

//************************************
// Method:    read_sequence
// FullName:  read_sequence
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: istream & inputstream
// Parameter: string & sequence
//************************************
void read_sequence(istream& inputstream, string& sequence)
{
    sequence.clear();
    string temp;
    readstring(inputstream, temp);
    if (temp == "**" || temp != "*") return;

    temp.clear();
    do
    {
        readstring(inputstream, temp);
        if (temp != "*")
        {
            if (sequence.length() > 0) sequence.append(" ");
            sequence.append(temp);
        }
    } while (temp != "*");
}

//************************************
// Method:    find_sequence
// FullName:  find_sequence
// Access:    public 
// Returns:   int
// Qualifier:
// Parameter: string & file_name
// Parameter: const bool use_slice
//************************************
int find_sequence(string& file_name, const bool use_slice)
{
    ifstream inputfile;
    try
    {
        inputfile.open(file_name);
        if (!inputfile.is_open())
            throw;
    }
    catch (...)
    {
        cout << "Error opening input file." << endl;
        return -1;
    }
    
    while (!inputfile.eof())
    {
        auto cube_values = init;
        vector<face_val<face_val_type>> pattern;
        string start_seq;
        string case_name;
        
        readstring(inputfile, case_name);

        if (inputfile.eof())
            continue;
        
        read_cube_start(inputfile, cube_values);
        read_pattern(inputfile, pattern);
        read_sequence(inputfile, start_seq);

        std::cout << "<Entry> " << endl;
        std::cout << "    <Key>" << case_name << "</Key>" << endl;

        string found_sequence;
        findsequence f;
        
        const auto found = f.find_sequence(cube_values, pattern, start_seq, found_sequence, 0, max_depth, use_slice);
        if (found)
        {           
            auto seq = start_seq.append(" ") + found_sequence;
            seq = trim(seq);
            if (!seq.empty())
            {
                std::cout << "    <Value>" << seq << "</Value>" << endl;
            }
            else
            {
                std::cout << "    <Value />" << endl;
            }
        }
        else
        {
            cout << "    <!-- Not Found! -->" << endl << "    <Value />" << endl;
        }
        cout << "</Entry>" << endl << endl;
    }
    inputfile.close();
    return 0;
}

//************************************
// Method:    readstring
// FullName:  readstring
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: istream & inputstream
// Parameter: string & str
//************************************
void readstring(istream& inputstream, string& str)
{
    string temp;
    while (!inputstream.eof() && temp.empty())
    {
        inputstream >> temp;
        str = trim(temp);

        if (str.length() > 1 && str[0] == '/' && str[1] == str[0])
        {
            cout << str << " ";
            getline(inputstream, str);
            cout << str << endl;
            temp.erase();
        }
    }
}

//************************************
// Method:    readfaceval
// FullName:  readfaceval
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: istream & inputstream
// Parameter: face_val<face_val_type> val
//************************************
void readfaceval(istream& inputstream, face_val<face_val_type>& val)
{
    string temp;
    readstring(inputstream, temp);
    if (temp == "-")
    {
        val = 0;
        return;
    }
    const auto n = stoi(temp);
    val = n;
}

findsequence.cpp

#include <string>
#include <vector>
#include <iostream>
#include <thread>
#include <memory>

#include "cube.h"
#include "findsequence.h"
#include "str.h"

#ifndef __countof
#define _countof(array) (sizeof(array) / sizeof(array[0]))
#endif

//************************************
// Method:    find_sequence
// FullName:  findsequence::find_sequence
// Access:    public 
// Returns:   bool
// Qualifier:
// Parameter: cube * cube_original
// Parameter: std::vector<face_val<face_val_type>> & pattern
// Parameter: int n
// Parameter: std::vector<std::string> & data
// Parameter: std::string & out
//
// Find the moves from a given cube that
// makes the cube match the pattern. The number of moves
// will be exactly n or 0 if the cube already has the pattern.
// On exit out will be the sequence of moves or empty string.
// This will return true if the pattern is matched.
//
// This will start a thread for each possible move and wait for all to complete.
// This is the second layer of the findsequence API
//************************************
bool findsequence::find_sequence(cube* cube_original, std::vector<face_val<face_val_type>>& pattern, int n,
    std::vector<std::string>& data, std::string& out)
{
    out = "";

    // Check for initial match
    if (*cube_original == pattern)
    {
        return true;
    }

    if (n > -1)
    {
        constexpr auto buff_sz = 26;
        auto timer = time(nullptr);
        char buffer[buff_sz];
        const auto tm_info = localtime(&timer);

        strftime(buffer, _countof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
        std::cout << "    <!-- Level " << n << " " << buffer << " -->" << std::endl;
    }

    auto result = false;

    // create the thread start data
    auto threaddata = std::vector<std::unique_ptr<thread_start_data>>(data.size());
    for (auto i = 0lu; i < data.size(); i++)
    {
        threaddata[i] = std::make_unique<thread_start_data>(cube_original, pattern, n, data, data[i], this);
    }

    // Create the threads
    auto threads = std::vector<std::thread>(threaddata.size());
    for (auto i = 0lu; i < threads.size(); i++)
    {       
        threads[i] = std::thread(&find_sequence_async, threaddata[i].get());
    }

    // wait for threads to terminate
    for (auto& t : threads)
    {
        t.join();
    }

    // Check result
    for (auto& td : threaddata)
    {
        if (!result && td->result)
        {
            // save result
            out = td->out;
            result = true;
            break;
        }
    }

    return result;
}

//************************************
// Method:    find_sequence
// FullName:  findsequence::find_sequence
// Access:    public 
// Returns:   bool
// Qualifier:
// Parameter: std::vector<face_val<face_val_type>> & cube_initial_values
// Parameter: std::vector<face_val<face_val_type>> & pattern
// Parameter: std::string & start_seq
// Parameter: std::string & out
// Parameter: const int start
// Parameter: const int max_moves
// Parameter: const bool use_slice
//
// Find the moves from a given cube string that
// makes the cube match the pattern (pattern). The number of moves will up to n.
// On exit out will be the sequence of moves or empty string.
// This will return true if the pattern is matched.
// This will start a thread for each possible move and wait for all to complete.
//
// This is the topmost layer of the find_sequence API
//************************************
bool findsequence::find_sequence(std::vector<face_val<face_val_type>>& cube_initial_values, std::vector<face_val<face_val_type>>& pattern, std::string& start_seq,
                                 std::string& out, const int start, const int max_moves, const bool use_slice)
{
    static std::vector<std::string> cube_directions =
    {
        "U", "Ui", "D", "Di", "L", "Li", "R", "Ri", "B", "Bi", "F", "Fi",
        "Us", "Ds", "Ls", "Rs", "Fs", "Bs"
    };

    static std::vector<std::string> cube_directions_no_slice =
    {
        "U", "Ui", "D", "Di", "L", "Li", "R", "Ri", "B", "Bi", "F", "Fi"
    };

    auto data = use_slice ? cube_directions : cube_directions_no_slice; 

    const auto cube_original = std::make_unique<cube>();
    cube_original->set_cube(cube_initial_values);

    // if there is a start sequence execute it here
    if (!start_seq.empty())
    {
        cube_original->execute_sequence(start_seq);
    }
    
    auto found = false;
    for (auto n = start; !found && n <= max_moves; n++)
    {
        // call second layer of API
        found = find_sequence(cube_original.get(), pattern, n, data, out);
    }
    return found;
}

//************************************
// Method:    find_sequence
// FullName:  findsequence::find_sequence
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: cube * cube_original
// Parameter: std::vector<face_val<face_val_type>> & pattern
// Parameter: const int n
// Parameter: std::vector<std::string> & data
// Parameter: std::string & out
// Parameter: std::string & start
// Parameter: bool & result
//************************************
void findsequence::find_sequence(cube* cube_original, std::vector<face_val<face_val_type>>& pattern, const int n,
                                 std::vector<std::string>& data, std::string& out, std::string& start, bool& result) const
{
    std::vector<face_val<face_val_type>> cube_initial_values;

    cube_original->get_cube(cube_initial_values);
    find_sequence(cube_initial_values, pattern, n, data, out, start, result);
}

//************************************
// Method:    find_sequence
// FullName:  findsequence::find_sequence
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: std::string & cube_initial_values
// Parameter: std::vector<std::string> & pattern
// Parameter: const int n
// Parameter: std::vector<std::string> & data
// Parameter: std::string & out
// Parameter: std::string & start
// Parameter: bool & result
//************************************
void findsequence::find_sequence(std::vector<face_val<face_val_type>>& cube_initial_values, std::vector<face_val<face_val_type>>& pattern, 
    const int n, std::vector<std::string>& data, std::string& out, std::string& start, bool& result) const
{
    result = false;

    auto indexlist = std::vector<int>(n, 0);
    auto done = false;
    const auto end = data.size();

    std::vector<std::string> start_moves;
    std::string chars = "\t\n\r\v\f ";
    split(start, chars, start_moves);
    
    auto c = std::make_unique<cube>();
    while (!done)
    {
        c->set_cube(cube_initial_values);
        auto tokens = start_moves;
        
        for (auto i = 1; i < n; i++)
        {
            tokens.push_back(data[indexlist[i]]);
        }

        c->execute_sequence(tokens);
        done = true;
        if (*c == pattern)
        {
            join(tokens, out, " ");
            result = true;
        }
        else
        {
            for (auto index = 1; index < n; index++)
            {
                if (indexlist[index] + 1 < int(end))
                {
                    indexlist[index]++;
                    done = false;
                    break;
                }
                indexlist[index] = 0;
            }
        }
    }
}

//************************************
// Method:    find_sequence_async
// FullName:  findsequence::find_sequence_async
// Access:    private static 
// Returns:   void
// Qualifier:
// Parameter: thread_start_data * threadstart
//************************************
void findsequence::find_sequence_async(thread_start_data* threadstart)
{
    const auto sz = threadstart->n;
    auto data = threadstart->data;  
    auto pattern = threadstart->pattern;

    std::vector<face_val<face_val_type>> cube_initial_values;
    threadstart->cube_original->get_cube(cube_initial_values);
    
    auto c = std::make_unique<cube>();
    c->set_cube(cube_initial_values);

    std::string out;
    bool result;
    auto start = threadstart->start;
    threadstart->instance->find_sequence(cube_initial_values, pattern, sz, data, out, start, result);
    threadstart->result = result;
    threadstart->out = out;
}

cube.cpp

#include <cstdlib>
#include <cstring>
#include <ctime>
#include <string>
#include <vector>

#include "cube.h"
#include "str.h"

//************************************
// Method:    cube
// FullName:  cube::cube
// Access:    public 
// Returns:   
// Qualifier:
//
// Build a default cube.
//************************************
cube::cube()
{
    const auto time_ui = unsigned (time(nullptr));
    srand(time_ui);

    whitechars = "\t\n\r\v\f ";
    record_ = true;
    init_cube();
}

//************************************
// Method:    init_cube
// FullName:  cube::init_cube
// Access:    public 
// Returns:   void
// Qualifier:
//
// Initialize the cube.
// This clears _moves and _scramble_sequence.
//************************************
void cube::init_cube()
{
    face *faces[] = { &up, &left, &front, &right, &back, &down };
    auto n = 1;
    for (auto& f : faces)
        for (auto& row : f->square)
            for (auto& col : row)
                col = n++;

    moves_.clear();
    scramble_sequence_.clear();
}

//************************************
// Method:    set_cube
// FullName:  cube::set_cube
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const char * cube_values
//
// Set cube values.
// This clears _moves and _scramble_sequence.
//************************************
void cube::set_cube(std::vector<face_val<face_val_type>>& cube_values)
{
    face *faces[] = { &up, &left, &front, &right, &back, &down };
    auto n = 0;
    for (auto& f : faces)
        for (auto& row : f->square)
            for (auto& col : row)
                col = cube_values[n++];

    moves_.clear();
    scramble_sequence_.clear();
}

//************************************
// Method:    get_cube
// FullName:  cube::get_cube
// Access:    private 
// Returns:   void
// Qualifier: const
// Parameter: std::vector<face_val<face_val_type>> & pattern
//************************************
void cube::get_cube(std::vector<face_val<face_val_type>>& pattern) const
{
    pattern.clear();

    const face *faces[] = { &up, &left, &front, &right, &back, &down };
    for (auto& f : faces)
        for (auto& row : f->square)
            for (auto& col : row)
                pattern.push_back(col);
}

//************************************
// Method:    clone
// FullName:  cube::clone
// Access:    public 
// Returns:   cube::cube *
// Qualifier:
// 
// This clones the cube instance
//************************************
cube * cube::clone() const
{
    auto clone_cube = std::make_unique<cube>();
    for (auto row = 0; row < cube_size; row++)
        for (auto col = 0; col < cube_size; col++)
        {
            clone_cube->up.square[row][col] = up.square[row][col];
            clone_cube->left.square[row][col] = left.square[row][col];
            clone_cube->front.square[row][col] = front.square[row][col];
            clone_cube->right.square[row][col] = right.square[row][col];
            clone_cube->back.square[row][col] = back.square[row][col];
            clone_cube->down.square[row][col] = down.square[row][col];
        }
    return clone_cube.get();
}

// ReSharper disable once CppMemberFunctionMayBeStatic
//************************************
// Method:    simple_solve
// FullName:  cube::simple_solve
// Access:    public 
// Returns:   bool
// Qualifier:
// Parameter: char * buffer
// Parameter: size_t * sz
// 
// This performs a simple solve
//************************************
bool cube::simple_solve(char* buffer, size_t* sz)
{
    // const c_simple_solver solver(this);
    // return solver.solve(buffer, sz);
    return true;
}

//************************************
// Method:    issolved
// FullName:  cube::issolved
// Access:    public 
// Returns:   bool
// Qualifier:
//
// This returns true if the cube is solved
//************************************
bool cube::issolved()
{
    return up.issolved() && left.issolved() && front.issolved() && right.issolved() && back.issolved() && down.issolved();
}

//************************************
// Method:    f
// FullName:  cube::f
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the front face clockwise
//************************************
void cube::f()
{
    front.rotate_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[cube_size - 1][cube_size - 1 - index];
        up.square[cube_size - 1][cube_size - 1 - index] = left.square[index][cube_size - 1];
        left.square[index][cube_size - 1] = down.square[0][index];
        down.square[0][index] = right.square[cube_size - 1 - index][0];
        right.square[cube_size - 1 - index][0] = temp;
    }
    if (record_)
        moves_ += "F ";
}

//************************************
// Method:    fi
// FullName:  cube::fi
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the front face counter clockwise
//************************************
void cube::fi()
{
    front.rotate_counter_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[cube_size -1][cube_size -1 - index];
        up.square[cube_size - 1][cube_size - 1 - index] = right.square[cube_size - 1 - index][0];
        right.square[cube_size - 1 - index][0] = down.square[0][index];
        down.square[0][index] = left.square[index][cube_size -1];
        left.square[index][cube_size - 1] = temp;
    }
    if (record_)
        moves_ += "Fi ";
}

//************************************
// Method:    u
// FullName:  cube::u
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the up face clockwise
//************************************
void cube::u()
{
    up.rotate_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = front.square[0][index];
        front.square[0][index] = right.square[0][index];
        right.square[0][index] = back.square[0][index];
        back.square[0][index] = left.square[0][index];
        left.square[0][index] = temp;
    }
    if (record_)
        moves_ += "U ";
}

//************************************
// Method:    ui
// FullName:  cube::ui
// Access:    public 
// Returns:   void 
// Qualifier:
//
// Rotate the up face counter clockwise
//************************************
void cube::ui()
{
    up.rotate_counter_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = front.square[0][index];
        front.square[0][index] = left.square[0][index];
        left.square[0][index] = back.square[0][index];
        back.square[0][index] = right.square[0][index];
        right.square[0][index] = temp;
    }
    if (record_)
        moves_ += "Ui ";
}

//************************************
// Method:    b
// FullName:  cube::b
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the back face clockwise
//************************************
void cube::b()
{
    back.rotate_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[0][index];
        up.square[0][index] = right.square[index][cube_size - 1];
        right.square[index][cube_size - 1] = down.square[cube_size - 1][cube_size - 1 - index];
        down.square[cube_size - 1][cube_size - 1 - index] = left.square[cube_size - 1 - index][0];
        left.square[cube_size - 1 - index][0] = temp;
    }
    if (record_)
        moves_ += "B ";
}

//************************************
// Method:    bi
// FullName:  cube::bi
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the back face counter clockwise
//************************************
void cube::bi()
{
    back.rotate_counter_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[0][index];
        up.square[0][index] = left.square[cube_size -1 - index][0];
        left.square[cube_size - 1 - index][0] = down.square[cube_size - 1][cube_size - 1 - index];
        down.square[cube_size - 1][cube_size - 1 - index] = right.square[index][cube_size - 1];
        right.square[index][cube_size - 1] = temp;
    }
    if (record_)
        moves_ += "Bi ";
}

//************************************
// Method:    l
// FullName:  cube::l
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the left face clockwise
//************************************
void cube::l()
{
    left.rotate_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[index][0];
        up.square[index][0] = back.square[cube_size - 1 - index][cube_size - 1];
        back.square[cube_size - 1 - index][cube_size - 1] = down.square[index][0];
        down.square[index][0] = front.square[index][0];
        front.square[index][0] = temp;
    }
    if (record_)
        moves_ += "L ";
}

//************************************
// Method:    li
// FullName:  cube::li
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the left face counter clockwise
//************************************
void cube::li()
{
    left.rotate_counter_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[index][0];
        up.square[index][0] = front.square[index][0];
        front.square[index][0] = down.square[index][0];
        down.square[index][0] = back.square[cube_size - 1 - index][cube_size - 1];
        back.square[cube_size - 1 - index][cube_size - 1] = temp;
    }
    if (record_)
        moves_ += "Li ";
}

//************************************
// Method:    r
// FullName:  cube::r
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the right face clockwise
//************************************
void cube::r()
{
    right.rotate_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[index][cube_size - 1];
        up.square[index][cube_size - 1] = front.square[index][cube_size - 1];
        front.square[index][cube_size - 1] = down.square[index][cube_size - 1];
        down.square[index][cube_size - 1] = back.square[cube_size - 1 - index][0];
        back.square[cube_size - 1 - index][0] = temp;
    }
    if (record_)
        moves_ += "R ";
}

//************************************
// Method:    ri
// FullName:  cube::ri
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the right face counter clockwise
//************************************
void cube::ri()
{
    right.rotate_counter_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[cube_size - 1 - index][cube_size - 1];
        up.square[cube_size - 1 - index][cube_size - 1] = back.square[index][0];
        back.square[index][0] = down.square[cube_size - 1 - index][cube_size - 1];
        down.square[cube_size - 1 - index][cube_size - 1] = front.square[cube_size - 1 - index][cube_size - 1];
        front.square[cube_size - 1 - index][cube_size - 1] = temp;
    }
    if (record_)
        moves_ += "Ri ";
}

//************************************
// Method:    d
// FullName:  cube::d
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the down face clockwise
//************************************
void cube::d()
{
    down.rotate_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = front.square[cube_size -1][index];
        front.square[cube_size - 1][index] = left.square[cube_size - 1][index];
        left.square[cube_size - 1][index] = back.square[cube_size - 1][index];
        back.square[cube_size - 1][index] = right.square[cube_size - 1][index];
        right.square[cube_size - 1][index] = temp;
    }
    if (record_)
        moves_ += "D ";
}

//************************************
// Method:    di
// FullName:  cube::di
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the down face counter clockwise
//************************************
void cube::di()
{
    down.rotate_counter_clockwise();
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = front.square[cube_size - 1][index];
        front.square[cube_size - 1][index] = right.square[cube_size - 1][index];
        right.square[cube_size - 1][index] = back.square[cube_size - 1][index];
        back.square[cube_size - 1][index] = left.square[cube_size - 1][index];
        left.square[cube_size - 1][index] = temp;
    }
    if (record_)
        moves_ += "Di ";
}

//************************************
// Method:    us
// FullName:  cube::us
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube up slice clockwise
//************************************
void cube::us()
{
    for (auto col = 0; col < cube_size; col++)
    {
        const auto temp = front.square[1][col];
        front.square[1][col] = right.square[1][col];
        right.square[1][col] = back.square[1][col];
        back.square[1][col] = left.square[1][col];
        left.square[1][col] = temp;
    }
    if (record_)
        moves_ += "Us ";
}

//************************************
// Method:    ds
// FullName:  cube::ds
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube down slice clockwise
//************************************
void cube::ds()
{
    for (auto col = 0; col < cube_size; col++)
    {
        const auto temp = front.square[1][col];
        front.square[1][col] = left.square[1][col];
        left.square[1][col] = back.square[1][col];
        back.square[1][col] = right.square[1][col];
        right.square[1][col] = temp;
    }
    if (record_)
        moves_ += "Ds ";
}

//************************************
// Method:    ls
// FullName:  cube::ls
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube left slice clockwise
//************************************
void cube::ls()
{
    for (auto row = 0; row < cube_size; row++)
    {
        const auto temp = up.square[row][1];
        up.square[row][1] = back.square[cube_size - row - 1][1];
        back.square[cube_size - row - 1][1] = down.square[row][1];
        down.square[row][1] = front.square[row][1];
        front.square[row][1] = temp;
    }
    if (record_)
        moves_ += "Ls ";
}

//************************************
// Method:    rs
// FullName:  cube::rs
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube right slice clockwise
//************************************
void cube::rs()
{
    for (auto row = 0; row < cube_size; row++)
    {
        const auto temp = up.square[row][1];
        up.square[row][1] = front.square[row][1];
        front.square[row][1] = down.square[row][1];
        down.square[row][1] = back.square[cube_size - row - 1][1];
        back.square[cube_size - row - 1][1] = temp;
    }
    if (record_)
        moves_ += "Rs ";
}

//************************************
// Method:    fs
// FullName:  cube::fs
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube front slice clockwise
//************************************
void cube::fs()
{
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[1][index];
        up.square[1][index] = left.square[cube_size - index - 1][1];
        left.square[cube_size - index - 1][1] = down.square[1][cube_size - index -1];
        down.square[1][cube_size - index -1] = right.square[index][1];
        right.square[index][1] = temp;
    }
    if (record_)
        moves_ += "Fs ";
}

//************************************
// Method:    bs
// FullName:  cube::bs
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube back slice clockwise
//************************************
void cube::bs()
{
    for (auto index = 0; index < cube_size; index++)
    {
        const auto temp = up.square[1][index];
        up.square[1][index] = right.square[index][1];
        right.square[index][1] = down.square[1][cube_size - index - 1];
        down.square[1][cube_size - index - 1] = left.square[cube_size - index - 1][1];
        left.square[cube_size - index - 1][1] = temp;
    }
    if (record_)
        moves_ += "Bs ";
}

//************************************
// Method:    cu
// FullName:  cube::cu
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube up
//************************************
void cube::cu()
{
    left.rotate_counter_clockwise();
    right.rotate_clockwise();
    for (auto row = 0; row < cube_size; row++)
        for (auto col = 0; col < cube_size; col++)
        {
            const auto temp = up.square[row][col];
            up.square[row][col] = front.square[row][col];
            front.square[row][col] = down.square[row][col];
            down.square[row][col] = back.square[cube_size - 1 - row][cube_size - 1 - col];
            back.square[cube_size - 1 - row][cube_size - 1 - col] = temp;
        }
    if (record_)
        moves_ += "Cu ";
}

//************************************
// Method:    cd
// FullName:  cube::cd
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube down
//************************************
void cube::cd()
{
    left.rotate_clockwise();
    right.rotate_counter_clockwise();
    for (auto row = 0; row < cube_size; row++)
        for (auto col = 0; col < cube_size; col++)
        {
            const auto temp = down.square[row][col];
            down.square[row][col] = front.square[row][col];
            front.square[row][col] = up.square[row][col];
            up.square[row][col] = back.square[cube_size - 1 - row][cube_size - 1 - col];
            back.square[cube_size - 1 - row][cube_size - 1 - col] = temp;
        }
    if (record_)
        moves_ += "Cd ";
}

//************************************
// Method:    cl
// FullName:  cube::cl
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube left
//************************************
void cube::cl()
{
    down.rotate_counter_clockwise();
    up.rotate_clockwise();
    for (auto row = 0; row < cube_size; row++)
        for (auto col = 0; col < cube_size; col++)
        {
            const auto temp = front.square[row][col];
            front.square[row][col] = right.square[row][col];
            right.square[row][col] = back.square[row][col];
            back.square[row][col] = left.square[row][col];
            left.square[row][col] = temp;
        }
    if (record_)
        moves_ += "Cl ";
}

//************************************
// Method:    cr
// FullName:  cube::cr
// Access:    public 
// Returns:   void
// Qualifier:
//
// Rotate the cube face_right
//************************************
void cube::cr()
{
    down.rotate_clockwise();
    up.rotate_counter_clockwise();
    for (auto row = 0; row < cube_size; row++)
        for (auto col = 0; col < cube_size; col++)
        {
            const auto temp = front.square[row][col];
            front.square[row][col] = left.square[row][col];
            left.square[row][col] = back.square[row][col];
            back.square[row][col] = right.square[row][col];
            right.square[row][col] = temp;
        }
    if (record_)
        moves_ += "Cr ";
}

//************************************
// Method:    reverse_sequence
// FullName:  cube::reverse_sequence
// Access:    public static 
// Returns:   void
// Qualifier: const
// Parameter: const char * sequence
// Parameter: char * buffer
// Parameter: size_t * sz
//
// Create an inverse to a sequence
//************************************
void cube::reverse_sequence(const char* sequence, char* buffer, size_t* sz) const
{
    std::string result;
    reverse_sequence(sequence, result);
    if (buffer != nullptr && *sz >= result.length())
        strcpy(buffer, result.c_str());
    if (sz != nullptr) *sz = result.length();
}

//************************************
// Method:    reverse_sequence
// FullName:  cube::reverse_sequence
// Access:    public static 
// Returns:   void
// Qualifier:
// Parameter: const std::string & sequence
// Parameter: std::string & out
//
// Create an inverse to a sequence
//************************************
void cube::reverse_sequence(const std::string& sequence, std::string& out)
{
    std::string buffer;
    const std::string white = "\t\n\r\v\f ";
    
    std::vector<std::string> strs;
    split(sequence, white, strs);

    for (auto str : strs)
    {
        auto lowerstr = lower(str);
        if (buffer.length() > 0)
            buffer.insert(buffer.begin(), ' ');

        if (lowerstr == "u")
        {
            buffer.insert(buffer.begin(), 'i');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "ui")
        {
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "l")
        {
            buffer.insert(buffer.begin(), 'i');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "li")
        {
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "f")
        {
            buffer.insert(buffer.begin(), 'i');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "fi")
        {
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "r")
        {
            buffer.insert(buffer.begin(), 'i');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "ri")
        {
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "b")
        {
            buffer.insert(buffer.begin(), 'i');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "bi")
        {
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "d")
        {
            buffer.insert(buffer.begin(), 'i');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "di")
        {
            buffer.insert(buffer.begin(), 'd');
        }
        else if (lowerstr == "us")
        {
            buffer.insert(buffer.begin(), str[1]);
            buffer.insert(buffer.begin(), 'D');
        }
        else if (lowerstr == "ds")
        {
            buffer.insert(buffer.begin(), str[1]);
            buffer.insert(buffer.begin(), 'U');
        }
        else if (lowerstr == "ls")
        {
            buffer.insert(buffer.begin(), str[1]);
            buffer.insert(buffer.begin(), 'R');
        }
        else if (lowerstr == "rs")
        {
            buffer.insert(buffer.begin(), str[1]);
            buffer.insert(buffer.begin(), 'L');
        }
        else if (lowerstr == "fs")
        {
            buffer.insert(buffer.begin(), str[1]);
            buffer.insert(buffer.begin(), 'B');
        }
        else if (lowerstr == "bs")
        {
            buffer.insert(buffer.begin(), str[1]);
            buffer.insert(buffer.begin(), 'F');
        }
        else if (lowerstr == "cu")
        {
            buffer.insert(buffer.begin(), 'd');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "cd")
        {
            buffer.insert(buffer.begin(), 'u');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "cl")
        {
            buffer.insert(buffer.begin(), 'r');
            buffer.insert(buffer.begin(), str[0]);
        }
        else if (lowerstr == "cr")
        {
            buffer.insert(buffer.begin(), 'l');
            buffer.insert(buffer.begin(), str[0]);
        }
    }
    optimize_sequence(buffer, out); 
}

//************************************
// Method:    execute_move
// FullName:  cube::execute_move
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const char * move
//
// Perform a single move
//************************************
void cube::execute_move(const char* move)
{
    if (iequals(move, "u"))         u();
    else if (iequals(move, "ui"))   ui();
    else if (iequals(move, "d"))    d();
    else if (iequals(move, "di"))   di();
    else if (iequals(move, "l"))    l();
    else if (iequals(move, "li"))   li();
    else if (iequals(move, "r"))    r();
    else if (iequals(move, "ri"))   ri();
    else if (iequals(move, "f"))    f();
    else if (iequals(move, "fi"))   fi();
    else if (iequals(move, "b"))    b();
    else if (iequals(move, "bi"))   bi();
    else if (iequals(move, "us"))   us();
    else if (iequals(move, "ds"))   ds();
    else if (iequals(move, "rs"))   rs();
    else if (iequals(move, "ls"))   ls();
    else if (iequals(move, "fs"))   fs();
    else if (iequals(move, "bs"))   bs();
    else if (iequals(move, "cu"))   cu();
    else if (iequals(move, "cd"))   cd();
    else if (iequals(move, "cl"))   cl();
    else if (iequals(move, "cr"))   cr();
}

//************************************
// Method:    execute_move
// FullName:  cube::execute_move
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const std::string & move
//
// Perform a single move
//************************************
void cube::execute_move(const std::string& move)
{
    execute_move(move.c_str());
}

//************************************
// Method:    execute_sequence
// FullName:  cube::execute_sequence
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const char * sequence
//
// Execute a move sequence
//************************************
void cube::execute_sequence(const char* sequence)
{
    const std::string seq = sequence;
    execute_sequence(seq);
}
 
//************************************
// Method:    execute_sequence
// FullName:  cube::execute_sequence
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const std::string & sequence
//
// Execute a move sequence
//************************************
void cube::execute_sequence(const std::string& sequence)
{
    if (sequence.length() == 0) return;
    std::vector<std::string> tokens;
    split(sequence, whitechars, tokens);
    execute_sequence(tokens);
}

//************************************
// Method:    execute_sequence
// FullName:  cube::execute_sequence
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const std::vector<std::string> & tokens
//************************************
void cube::execute_sequence(const std::vector<std::string>& tokens)
{
    for (const auto &token : tokens)
        execute_move(token);
}
//************************************
// Method:    scramble_cube
// FullName:  cube::scramble_cube
// Access:    public 
// Returns:   void
// Qualifier:
// Parameter: const int scramble_count
//
// Scramble the cube
//************************************
void cube::scramble_cube(const int scramble_count = 1000)
{
    const auto temp = record_;
    record_ = false;

    const auto count = scramble_count;
    const auto num_moves = 18; 
    static const std::string directions[num_moves] =
    {
        "U", "Ui", "D", "Di", "L", "Li", "R", "Ri", "B", "Bi", "F", "Fi", 
        "Us", "Ds", "Ls", "Rs", "Fs", "Bs"
    };

    std::string sequence;
    for (auto scrambleloop = 0; scrambleloop < count; scrambleloop++)
    {
        const auto rnd = rand() % num_moves;
        sequence += directions[rnd] + ' ';
    }
    optimize_sequence(sequence, scramble_sequence_);
    execute_sequence(scramble_sequence_);
    record_ = temp;
}

//************************************
// Method:    get_optimized_moves
// FullName:  cube::get_optimized_moves
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: std::string & moves
//
// get the optimized moves as a string
//************************************
void cube::get_optimized_moves(std::string& moves) const
{
    optimize_sequence(moves_, moves);
}

//************************************
// Method:    get_moves
// FullName:  cube::get_moves
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: std::string & moves
//
// get the moves as a string
//************************************
void cube::get_moves(std::string& moves) const
{
    moves = moves_;
    trim(moves);
}

//************************************
// Method:    get_scramble_sequence
// FullName:  cube::get_scramble_sequence
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: std::string & scramble_sequence
//
// get the scramble sequence as a string
//************************************
void cube::get_scramble_sequence(std::string& scramble_sequence) const
{
    scramble_sequence = scramble_sequence_;
}

//************************************
// Method:    get_moves
// FullName:  cube::get_moves
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: char * buffer
// Parameter: size_t * sz
//
// get the moves as a c string
//************************************
void cube::get_moves(char* buffer, size_t* sz) const
{
    std::string moves;
    get_moves(moves);
    if (buffer != nullptr && *sz > moves.length())
        strcpy(buffer, moves.c_str());
    if (sz != nullptr)
        *sz = moves_.length() + 1;
}

//************************************
// Method:    get_optimized_moves
// FullName:  cube::get_optimized_moves
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: char * buffer
// Parameter: size_t * sz
//
// get the moves made as a string that are optimized
//************************************
void cube::get_optimized_moves(char* buffer, size_t* sz) const
{
    std::string moves;
    get_optimized_moves(moves);
    if (buffer != nullptr && *sz > moves.length())
        strcpy(buffer, moves.c_str());
    if (sz != nullptr)
        *sz = moves_.length() + 1;
}

//************************************
// Method:    get_scramble_sequence
// FullName:  cube::get_scramble_sequence
// Access:    public 
// Returns:   void
// Qualifier: const
// Parameter: char * buffer
// Parameter: size_t * sz
//
// get the scramble sequence
//************************************
void cube::get_scramble_sequence(char* buffer, size_t* sz) const
{
    std::string scramble_sequence;
    get_scramble_sequence(scramble_sequence);
    if (buffer != nullptr && *sz > scramble_sequence.length())
        strcpy(buffer, scramble_sequence.c_str());
    if (sz != nullptr)
        *sz = scramble_sequence_.length() + 1;
}

//************************************
// Method:    optimize_sequence
// FullName:  cube::optimize_sequence
// Access:    public static 
// Returns:   void
// Qualifier:
// Parameter: const std::string & sequence
// Parameter: std::string & out
//
// optimize a sequence
//************************************
void cube::optimize_sequence(const std::string& sequence, std::string& out)
{
    auto temp = sequence;
    size_t len1;
    size_t len2;
    do
    {
        optimize_sequence_recursion(temp, out);
        len1 = temp.length();
        len2 = out.length();
        temp = out;
    } while (len2 < len1);
}

//************************************
// Method:    optimize_sequence_recursion
// FullName:  cube::optimize_sequence_recursion
// Access:    private static 
// Returns:   void
// Qualifier:
// Parameter: const std::string & sequence
// Parameter: std::string & out
//
// optimize a sequence
//
// The basic algorithm is to track 
// how many turns there are in a given direction
// and opposite direction while keeping track of which moves can be ignored.
// Optimize the ignored moves via recursion
//************************************
void cube::optimize_sequence_recursion(const std::string& sequence, std::string& out)
{
    out.clear();
    const std::string end_marker = "****";
    std::vector<std::string>tokens;

    const std::string white = "\t\n\r\v\f ";
    split(sequence, white, tokens);
    tokens.emplace_back(end_marker);

    auto index = 0u;
    auto count = 0;

    std::vector<std::string> ignore;
    std::vector<std::string> add;
    std::vector<std::string> subtract;
    std::string search;
    std::string ig_string;
    std::string add_string;
    std::string subtract_string;

    // loop through all tokens
    while (index < tokens.size())
    {
        // get the current token
        auto tok = lower(tokens[index++]);

        // new sequence
        // set add subtract and ignore vectors
        if (search.length() == 0)
        {
            count = 0;
            search = tok;
            ignore.clear();
            add.clear();
            subtract.clear();
            ig_string.clear();
            search = tok;
            add_string.clear();
            subtract_string.clear();

            // left and left inverse
            if (tok == "l" || tok == "li")
            {
                add.emplace_back("l");
                subtract.emplace_back("li");
                ignore.emplace_back("r");
                ignore.emplace_back("ri");
                ignore.emplace_back("ls");
                ignore.emplace_back("rs");
                add_string = "L";
                subtract_string = "Li";
            }
            // right and right inverse
            else if (tok == "r" || tok == "ri")
            {
                add.emplace_back("r");
                subtract.emplace_back("ri");
                ignore.emplace_back("l");
                ignore.emplace_back("li");
                ignore.emplace_back("ls");
                ignore.emplace_back("rs");
                add_string = "R";
                subtract_string = "Ri";
            }
            // front and front inverse
            else if (tok == "f" || tok == "fi")
            {
                add.emplace_back("f");
                subtract.emplace_back("fi");
                ignore.emplace_back("b");
                ignore.emplace_back("bi");
                ignore.emplace_back("fs");
                ignore.emplace_back("bs");
                add_string = "F";
                subtract_string = "Fi";
            }
            // back and back inverse
            else if (tok == "b" || tok == "bi")
            {
                add.emplace_back("b");
                subtract.emplace_back("bi");
                ignore.emplace_back("f");
                ignore.emplace_back("fi");
                ignore.emplace_back("fs");
                ignore.emplace_back("bs");
                add_string = "B";
                subtract_string = "Bi";
            }
            // up and up inverse
            else if (tok == "u" || tok == "ui")
            {
                add.emplace_back("u");
                subtract.emplace_back("ui");
                ignore.emplace_back("d");
                ignore.emplace_back("di");
                ignore.emplace_back("us");
                ignore.emplace_back("ds");
                add_string = "U";
                subtract_string = "Ui";
            }
            // down and down inverse
            else if (tok == "d" || tok == "di")
            {
                add.emplace_back("d");
                subtract.emplace_back("di");
                ignore.emplace_back("u");
                ignore.emplace_back("ui");
                ignore.emplace_back("us");
                ignore.emplace_back("ds");
                add_string = "D";
                subtract_string = "Di";
            }
            // cube up and cube down
            else if (tok == "cu" || tok == "cd")
            {
                add.emplace_back("cu");
                subtract.emplace_back("cd");
                add_string = "Cu";
                subtract_string = "Cd";
            }
            // cube left and cube right
            else if (tok == "cl" || tok == "cr")
            {
                add.emplace_back("cl");
                subtract.emplace_back("cr");
                add_string = "Cl";
                subtract_string = "Cr";
            }
            // up slice, up slice inverse, down slice and down slice inverse
            else if (tok == "us" || tok == "ds")
            {
                add.emplace_back("us");
                subtract.emplace_back("ds");
                ignore.emplace_back("u");
                ignore.emplace_back("ui");
                ignore.emplace_back("d");
                ignore.emplace_back("di");
                add_string = "Us";
                subtract_string = "Ds";
            }
            // left slice, left slice inverse, right slice and right slice inverse
            else if (tok == "ls" || tok == "rs")
            {
                add.emplace_back("ls");
                subtract.emplace_back("rs");
                ignore.emplace_back("l");
                ignore.emplace_back("li");
                ignore.emplace_back("r");
                ignore.emplace_back("ri");
                add_string = "Ls";
                subtract_string = "Rs";
            }
            // front slice, front slice inverse, back slice and back slice inverse
            else if (tok == "fs" || tok == "bs")
            {
                add.emplace_back("fs");
                subtract.emplace_back("bs");
                ignore.emplace_back("f");
                ignore.emplace_back("fi");
                ignore.emplace_back("b");
                ignore.emplace_back("bi");
                add_string = "Fs";
                subtract_string = "Bs";
            }
            else  // if (tok == end_marker)
            {
                add.emplace_back(tok);
            }
        }

        // At this point add, subtract and ignore vectors are set

        // add
        auto found = false;
        for (const auto& a : add)
        {
            if (tok == a)
            {
                count++;
                found = true;
                break;
            }
        }
        // subtract
        if (!found)
            for (const auto& s : subtract)
            {
                if (tok == s)
                {
                    count--;
                    found = true;
                    break;
                }
            }
        // ignore
        if (!found)
            for (const auto& i : ignore)
            {
                if (tok == i)
                {
                    ig_string += ' ' + tok;
                    found = true;
                    break;
                }
            }

        // check for end of sequence
        if (!found)
        {
            // recurse over ignore string
            if (ig_string.length() > 0)
            {
                std::string opt;
                optimize_sequence_recursion(ig_string, opt);
                if (opt.length() > 0)
                    out += ' ' + opt;
            }

            // the numbers of moves in any direction must be mod 4
            count %= 4;
            if (count > 0)
            {
                switch (count)
                {
                case 1:
                    out += ' ' + add_string;
                    break;
                case 2:
                    out += ' ' + add_string;
                    out += ' ' + add_string;
                    break;
                case 3:  // 3 add == 1 substract
                    out += ' ' + subtract_string;
                    break;
                default:
                    break;
                }
            }
            else if (count < 0)
            {
                switch (count)
                {
                case -1:
                    out += ' ' + subtract_string;
                    break;
                case -2: // 2 subtracts == 2 adds for simplicity
                    out += ' ' + add_string;
                    out += ' ' + add_string;
                    break;
                case -3: // 3 subtracts == 1 add
                    out += ' ' + add_string;
                    break;
                default:
                    break;
                }
            }
            // trigger a new sequence by clearing it
            search.clear();

            // move 1 token backwards
            index--;
        }
    }
    trim(out);
}

//************************************
// Method:    optimize_sequence
// FullName:  cube::optimize_sequence
// Access:    public static 
// Returns:   void
// Qualifier: const
// Parameter: const char * sequence
// Parameter: char * buffer
// Parameter: size_t * sz
//
// optimize a sequence
//************************************
void cube::optimize_sequence(const char* sequence, char* buffer, size_t* sz) const
{
    std::string out;
    optimize_sequence(sequence, out);
    if (buffer != nullptr && *sz > out.length())
        strcpy(buffer, out.c_str());
    if (sz != nullptr)
        *sz = out.length() + 1;
}
\$\endgroup\$
  • \$\begingroup\$ I omitted face.cpp and headers due to size. \$\endgroup\$ – Paul Baxter Jul 3 at 19:55
1
\$\begingroup\$

Literal initialization of vector

static const vector<face_val<face_val_type>> init = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54};

seems like something a computer should be able to do. Indeed, iota seems capable.

Return values

In a language with exception handling, this:

auto result = check_parameter(args, "-c", cubestring);
if (result < 0) return result;

result = check_parameter(args, "-slice", slice);
if (result < 0) return result;

result = check_parameter(args, "-f", filename);
if (result < 0) return result;

result = check_parameter(args, "-depth", maxdepth);
if (result < 0) return result;

seems like it should not use return codes, and should use exceptions instead.

// This is a comment

// Method:    parse_args
// FullName:  parse_args
// Access:    public 
// Returns:   int
// Qualifier:
// Parameter: const int argc
// Parameter: char * * argv

is worse than having no comment at all. You should document what the int actually means.

stderr

    cout << "Unknown argument(s) ";

seems like it should use cerr.

Returning strings

Currently, something like cubestring:

  • Is constructed once, with an empty value, in parse_args string cubestring;
  • Is constructed a second time with an actual value, in args.emplace_back
  • Is copy-constructed a third time, on var = args[argindex];

This is not great. I think you can reduce it by:

  • Not pre-declaring string cubestring;
  • Not accepting a mutable reference in check_parameter
  • Not returning an integer from check_parameter
  • Returning a const string & from check_parameter
  • Immediately assigning that returned reference to const string &cubestring = check_parameter(...

Also note that you should be able to pass argv directly to the vector constructor without a loop.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ FYI the comments for methods was auto generated and I will add to them \$\endgroup\$ – Paul Baxter Jul 6 at 19:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.