Server Hosting Advice – Should I move?

Hello fellow Webhosters. I"m new here, but certainly not new to webhosting or forums :] Sorry if I posted this in the wrong forum, mods fe… | Read the rest of https://www.webhostingtalk.com/showthread.php?t=1814758&goto=newpost

How do i move from Blogger to WordPress without Loosing SEO?

How do i move from Blogger to WordPress without Loosing SEO?

Can we move RAID configured disks across different Servers

I have 2 SSD disks configured as RAID 1 using RAID feature in BIOS. By chance if my motherboard and processor is damaged, can I connect these 2 SSD disks to another Server. Will these SSD disks boot on another Server. If not, is there any solution for making it to work.

How can I get the basic Unity player controller to move based on a gameobjects rotation?

I am making a basic movement script based off of the built in playercontroller in Unity. I am trying to get the Player gameobject to move forward based on its own rotation (i.e. if it is rotated to 90 degrees, it will go forward in the direction it is facing and not based off of its transform values), but I can’t figure out how to change it.

How can I do this?

What are the limits to repeating a move in Dungeon World?

You seem to have misunderstood one of the most fundamental rules of Dungeon World:

You don’t make the moves

Moves just happen when their trigger is met. That means you cannot tell your GM that you wish to, for example, take the “Defend” move. You have to narrate what your character does. If that triggers a move, then the move happens.

For example, if you narrate your character slashing at the goblin with their sword, the Hack and Slash move happens, because that move is triggered by attacking enemies in melee. However, the goblin might try to grab onto your character’s back and sink their filthy fangs into your neck in retaliation. You can’t just declare you want to Hack and Slash at the goblin again (well, you can, but it’s going to require some creative narration to explain how you can use your sword against a foe in such an awkward place). Instead you might need to shake off the goblin, which might amount to Defying danger.

The circumstances may also prevent you from making certain moves: for example, if your party is fighting a dragon with scales thick enough to withstand all your blows, the GM and other players around the table might decide that your attacks don’t count as Hack and Slash. You might be required to, for example, find a weak spot first – an operation that will most likely require plenty of other moves like Spouting Lore about the dragons’ weaknesses and Defying Danger to dodge the dragon’s attacks long enough to get to the softer spot.

The moves cut both ways

When a move calls for a roll and your result is 6 or lower, it means trouble. Sometimes the move directly tells you what the effect is, and otherwise the GM is free to make a move of their own against your character. For example, with a squirming and biting goblin on your back, failing to Defy danger could result in the GM deciding to Deal damage to your character, or Separate them from the party by stumbling down a chasm trying to shake off the goblin. Failing to Spout lore could result in Using up the party’s resources (in this case, time spent observing the dragon in hopes of a weak spot) or an Unwelcome truth being revealed – for example that the dragon’s weak spot is actually in a very dangerous place, like the inside of its mouth.

Because these moves that result from failure tend to necessarily involve changing the circumstances, you won’t be able to indefinitely repeat a move in the same circumstance — quite soon, something is going to change to push the narrative forward.

Magento2: Issue with move product tabs

In catalog_product.view.xml I try move product tabs to short description area:

<move element="reviews.tab" destination="product.info.overview" after="-" />
<move element="product.attributes" destination="product.info.overview" after="-"/>

When I use this code, tabs disappear. Does anyone have any solution?

https://prnt.sc/tc42v2

unity – How to move objects across a canvas with consistent speed on different screen resolutions?

If you want to linearly interpolate between two values, the jargon for that is “lerp“. The basic idea is you pass a lerp function two values and a number between 0 and 1 that specifies how far between the two you want the current position to be. In this way you can control when a moving object hits the target by controlling when the value between 0 and 1 hits 1.

Here’s an official Unity article on lerping.

After adapting examples from there to your particular use case where at least two notes need to hit the same place at the same time I get this:

Vector3 noteAStart = new Vector3 (...); 
Vector3 noteBStart = new Vector3 (...); // choose these however you like
Vector3 center = new Vector3 (...); // calculate the center of the screen here.

Vector3 noteACurrent = Vector3.Lerp (noteAStart, center, progress);
Vector3 noteBCurrent = Vector3.Lerp (noteBStart, center, progress);

You can then set your notes’ positions directly to noteACurrent and noteBCurrent, which will be change based on the current value of progress. So, if we can figure out what to set progress to each frame, then we’re set. But how do we do that?

First, we need to know how long we want the animation to take. Let’s assume we want it to take half a second.

Well, assuming the game runs at exactly 60 frames per second, the most straight-forward way would be to just increment progress by 1/30th each time, so we get to 1 after exactly 30 frames.

const float PROGRESS_PER_UPDATE = 1.0 / 30.0;
//...
progress += PROGRESS_PER_UPDATE;

However, Update does not necessarily get called exactly 60 frames per second, depending on the system load. Depending on your timing needs the occasional lag may be both noticeable and highly undesirable.

One way to deal with this is to factor in the time that the last frame took, (the Time.deltaTime,) into the interpolation, by doing something like this:

progress += Time.deltaTime / DESIRED_DURATION_IN_SECONDS;

But this approach can produce undesirable results if Time.deltaTime is ever significantly larger than it usually is, depending on what other code is triggered by progress reaching or exceeding 1.0. You also may want to clamp progress to be at most 1.0, depending on how far things move in a frame.

Another method which Unity has built-in for you, is to use FixedUpdate instead which attempts to provide a more consistent time step, allowing you to just use a constant amount of progress per frame. It is still true that ultimately a system with sufficiently heavy load, (say several other heavy programs are running in the background besides just your game,) will need to skip some frames, and/or call the method at a different frequency, some of the time. In practice this is likely not an issue.

If the way Unity deals with the an overloaded machine ever does become an issue, it would be possible to keep track of the actual elapsed time yourself, and react accordingly. You could even set up your own loop that handles heavy load the way you want to, but this is much more complicated, particularly making such a loop play nice with Unity’s expectations.

Magento2: How to move category description to bottom

Try this

app/design/frontend/[Company]/[theme_name]/Magento_Catalog/layout/catalog_category_view.xml

Code

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <move element="category.description" destination="content.bottom" />
    </body>
</page>

Hope this help you

Thanks …

java – How to make camera move with the player in 3D Libgdx

Iam trying to create a 3D game in Libgdx, so I created a 3d World with physics, and added just a small terrain and capulse (the Player) .

I want to make the game first person, so I created a player class and managed to move the player (Capulse) by joystick.

Like this :-

@Override
    public void create() {
        // load enviroment
        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
        // setup camera
        cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        cam.position.set(10f, 10f, 10f);
        cam.lookAt(0, 0, 0);
        cam.near = 0.001f;
        cam.far = 3000f;
        cam.update();
        // setup controller for camera
        camController = new CameraInputController(cam);
        Gdx.input.setInputProcessor(camController);
        // load the models
        assets = new AssetManager();
        assets.load("ground_stairs.g3db", Model.class);
        assets.load("gathering_node.g3db", Model.class);
        loading = true;
        modelBatch = new ModelBatch();

        // setup bulletphysics
        Bullet.init();
        collisionConfig = new btDefaultCollisionConfiguration();
        dispatcher = new btCollisionDispatcher(collisionConfig);
        broadphase = new btDbvtBroadphase();
        constraintSolver = new btSequentialImpulseConstraintSolver();
        dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, constraintSolver, collisionConfig);
        dynamicsWorld.setGravity(new Vector3(0, -10f, 0));
        contactListener = new MyContactListener();
        loadPlayer();
        
    }

    private void loadPlayer() {
     
        // setup player/camera movement
        pc = new PlayerController(instances,cam,dynamicsWorld);
    }

    private void doneLoading() {
        loading = false;
        onDoneLoadingStatic("ground_stairs.g3db", GROUND);
        onDoneLoadingStatic("gathering_node.g3db", GATHERING_NODE);
        gatheringNode = instances.get(instances.size() - 1);
    }

    public btRigidBody onDoneLoadingStatic(String fileName, int id) {
        Model model = assets.get(fileName, Model.class);
        ModelInstance instance = new ModelInstance(model);
        instances.add(instance);

        btBvhTriangleMeshShape shape = new btBvhTriangleMeshShape(instance.model.meshParts);
        btRigidBody body = new btRigidBody(0, null, shape, new Vector3(0, 0, 0));
        body.proceedToTransform(instance.transform);
        // set id to find with collision detection
        body.setUserValue(id);
        dynamicsWorld.addRigidBody(body);
        
        return body;
    }

    @Override
    public void render() {
        if (loading && assets.update()) {
            doneLoading();
        }
        camController.update();

        pc.update();
       
        
        final float delta = Math.min(1f / 30f, Gdx.graphics.getDeltaTime());
        dynamicsWorld.stepSimulation(delta, 5, 1f / 60f);

        
        Gdx.gl20.glClearColor(0, 0.5f, 1, 1);
        Gdx.gl20.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
        modelBatch.begin(cam);
        modelBatch.render(instances, environment);
        modelBatch.end();
    }

    @Override
    public void dispose() {
        modelBatch.dispose();
    }

    

    private class MyContactListener extends ContactListener {

        @Override
        public void onContactStarted(int userValue0, int userValue1) {
            if (userValue0 == PLAYER && userValue1 == GATHERING_NODE) {
                ((ColorAttribute) gatheringNode.materials.first().get(ColorAttribute.Diffuse)).color.set(Color.RED);
            }
            if (userValue0 == PLAYER && userValue1 == GROUND) {
                PlayerController.canJump = true;
            }
        }

        @Override
        public void onContactEnded(int userValue0, int userValue1) {
            if (userValue0 == PLAYER && userValue1 == GATHERING_NODE) {
                ((ColorAttribute) gatheringNode.materials.first().get(ColorAttribute.Diffuse)).color.set(Color.BLUE);
            }
        }
    }
}




PlayerController

public class PlayerController
 {
    private btRigidBody playerBody;
    private ModelInstance player;
    private float speed = 1f;
    public static boolean canJump = true;
    private PerspectiveCamera cam;
    private List<ModelInstance> instances = new ArrayList<ModelInstance>();
    private static final int PLAYER = 1;
    public static float xposition;
    public static float yposition;
    private btDynamicsWorld dynamicsWorld;
    public static float angleX;
    
    public PlayerController(List<ModelInstance> instances,PerspectiveCamera cam,btDynamicsWorld dynamicWorld) {
        this.instances = instances;
        this.cam = cam;
        this.dynamicsWorld = dynamicWorld;
        
        player = new ModelInstance(new ModelBuilder()
                                   .createCapsule(0.25f, 3, 10, new Material(ColorAttribute.createAmbient(Color.BLACK)), Usage.Normal | Usage.Position)
                                   );
        player.transform.translate(5, 7, 0);
        instances.add(player);
        // load player rigid body
        btCapsuleShape playerShape = new btCapsuleShape(0.25f, 2.5f);
        float mass = 10;
        Vector3 localInertia = new Vector3();
        playerShape.calculateLocalInertia(mass, localInertia);
        playerBody = new btRigidBody(mass, null, playerShape, localInertia);
        playerBody.proceedToTransform(player.transform);
        playerBody.setCollisionFlags(playerBody.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
        // set id to find with collision detection
        playerBody.setUserValue(PLAYER);
        dynamicsWorld.addRigidBody(playerBody);
        
        
    }

    public void update() {
        // make sure to activate the player body so bullet doesnt put it to sleep
        playerBody.activate();
        // prevent the capsule from falling over
        playerBody.setAngularFactor(new Vector3(0, 0, 0));
        playerBody.getWorldTransform(player.transform);
        
        
        Vector3 velocity = new Vector3(0, playerBody.getLinearVelocity().y, 0);
        velocity.x = xposition;
        velocity.z = yposition;
        player.transform.rotate(Vector3.Y,angleX);
        
        
        cam.update();
        
        playerBody.setLinearVelocity(velocity);
        

    }
}

And this is the output :-

the Player move perfectly with joystick

Now I want the camera to be at the top of the player (the Capulse) , and move with the player, so I added this

cam.position.add(velocity);

In update method in PlayerController

And this the output : just a blue screen
So, my question how to make the camera move we it the player like first person?

enter image description here

c++ program to help find move sequences to solve Rubiks cubes

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 = "tnrvf ";
    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 = "tnrvf ";
    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 = "tnrvf ";
    
    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 = "tnrvf ";
    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;
}