Using C++ shuffle

I want to use the C++ version of shuffle as the BP Node version doesn’t accept a random seed.
However, I’m missing something about the C++ shuffle function.

My code:


TArray<FString> AStarArraySpawner::ShuffleStarNames(TArray<FString> Names, int seed)
{
    mtRand = std::mt19937(seed);
    int x = Names.Num();

    std::shuffle(Names[0], Names[x], mtRand);

    GEngine->AddOnScreenDebugMessage(-1, 20.f, FColor::Red, FString::Printf(TEXT("NewStarLastIndex: %d"), x));
    return Names;
}

#include <algorithms> is in the header file. The Names TArray is being pulled in properly. If I comment out the shuffle line, the script compiles and outputs the unmodified TArray back to the blueprint and prints the correct number of items in the TArray to the screen.

The errors thrown by VS 2017 concerning the shuffle line are:

‘difference_type’: is not a member of any direct or indirect base class of ‘std::iterator_traits<_RanIt>’
‘std::_Iter_diff_t<FString>’ : Failed to specialize alias template
‘std::_Rng_from_urng’: class has no constructors

(I do have a blueprint function that (in theory) does the shuffle I want but it’s not exactly fast since the array has 2600 items in it.)

Still haven’t got the ‘official’ shuffle working but this:



TArray<FString> ADoTheShuffle::ShuffleNames(TArray<FString> Names, int seed)
{
    srand(seed);
    int x = Names.Num();

    for (int i = 0; i < x; i++)
    {
        int index = rand() % x;
        FString temp = Names*;
        Names* = Names[index];
        Names[index] = temp;
    }

    return Names;
}

appears to work okay in generating replicatible shuffles. But it would be nice to know why std::shuffle isn’t working for me.

UE4 doesn’t play nice with Standard Library calls. In this case, std::shuffle is complaining that a difference_type isn’t defined for FString. You should try to use UE4 calls and avoid anything that is standard lib as much as possible.

For shuffling an array, you can find tons of examples if you search the forums:
https://forums.unrealengine.com/development-discussion/c-gameplay-programming/53682-tutorial-shuffle-tarray-with-randomstream-generic-blueprint-node

The problem that needed to be handled was not the shuffle itself - Blueprints has a shuffle function that works fine if you don’t need to replicate results. It’s that we needed a shuffle that accepted a random seed for replicatable results. The for loop version in C++ appears to do the trick.

This is my first time hearing of a shuffle blueprint node, but from my understanding, it reorders the array in a random fashion.

If so, I’d make a function that takes a TArray as a parameter, and second TArray (a temp one) inside that function.
Then, I would make a for-loop going from 0 to the length of the original array. Inside that for-loop, I would first use TArray::IsValidIndex() to check if there is actually a value at the index.
If so, set the temporary TArray at index i to be a RandRange() function using ‘i’ as the start and original TArray length as the end. Otherwise, continue.
However, because you don’t want the random function to potentially choose the same random number, you should use the TArray::RemoveAt function to remove the index the chosen index of the original TArray.
Because, if all goes well, the size of the original array is 0 now. I would then use a second for-loop from 0 to the size of the TempArray which adds each entry from the temp array back into the original array. You should return that value as it would be the randomized original array now.

That being said, all of this is completely untested.

Any version of shuffle should act as though you were shuffling a deck of cards.
The version of shuffle I’m used to:



use List::Util qw(shuffle);
srand $seed;
my @newshuffle = shuffle @newapps;


Shuffling an array:


void AMyActor::ShuffleArray(TArray<FName>& myArray)
{
     if (myArray.Num() > 0)
     {
          int32 LastIndex = myArray.Num() - 1;
          for (int32 i = 0; i <= LastIndex; ++i)
          {
               int32 Index = FMath::RandRange(i, LastIndex);
               if (i != Index)
               {
                    myArray.Swap(i, Index);
               }
          }
     }
}

hope this helps

1 Like