How to shuffle a TArray?

So I have a deck of cards I need to shuffle. I tried implementing this code:
http://stackoverflow.com/a/12646864

like this:

void ACardsGameMode::Shuffle()
{
	for (int32 i = Deck.Num() - 1; i > 0; i--) {
		int32 j = FMath::Floor(FMath::Rand() * (i + 1));
		ASCard* temp = Deck[i];
		Deck[i] = Deck[j];
		Deck[j] = temp;
	}
}

The code compiles, but it crashes every time. Anyone know why?

Try this:

void ACardsGameMode::Shuffle()
{
    for (int32 i = Deck.Num() - 1; i > 0; i--) {
       int32 j = FMath::Floor(FMath::Rand() * (i + 1)) % Deck.Num();
       ASCard* temp = Deck[i];
       Deck[i] = Deck[j];
       Deck[j] = temp;
    }
}

Or something along those lines

Well, FMath::Rand returns an int between 0 and RAND_MAX. So your j index is invalid. And when you use that j to look up into your Deck (whatever container that is), you are hitting an out of bounds error.

When I need to shuffle an array I usually do something like this

MyArray.Sort([this](const int Item1, const int Item2) {
	return FMath::FRand() < 0.5f;
});

It’s nice and compact and no extra functions need to be defined.

1 Like

Thanks, that was it. JavaScript’s Math.random is 0-1 inclusive so I needed to use FRand instead.

Doesn’t this mean that each time the sort makes a comparison it generates a new random number? Which means that when it does the final check it re-randomizes everything?

I just found that in a packaged game, it will generate the same sequence of random numbers every time. This is different from playing in the editor, which will generate a different sequence every time. I solved this by using a RandomStream and seeding it with the current UnixTimestamp.

 int64 DateInSeconds = FDateTime::Now().ToUnixTimestamp();
 FRandomStream SRand = FRandomStream();
 SRand.Initialize(DateInSeconds);

So instead of using FMath::FRand(), I use SRand.FRand().

I tried your Shuffle( ) and came up with this solution.
Include Math Utilities at the top of your file

#include "UnrealMathUtility.h"

The SRandInit seeds the SRand to produce new numbers, even in a packaged game. Put it somewhere before the Shuffle gets used (for example inside BeginPlay)

FGenericPlatformMath::SRandInit(time(0));

And the Shuffle( ) function with an updated Floor call

void ACardsGameMode::Shuffle()
{
    for (int32 i = Deck.Num() - 1; i > 0; i--) {
        int32 j = FMath::FloorToInt(FMath::SRand() * (i + 1)) % Deck.Num();
        ASCard* temp = Deck[i];
        Deck[i] = Deck[j];
        Deck[j] = temp;
    }
}

Sorry for necroposting, I’ve tried this implementation and it works but I don’t quite understand what’s going on behind the scenes, why the function that takes a predicate can take extra parameters?