Download

C++ Permutations / Shuffling Player Input

Merry Christmas everyone,

I’m fairly new to the UnrealEngine and C++ programming, still the community has helped me like a dozen times already. I feel like it is time to give something back!

The last couple days I’ve been working on a party game and I thought it would be fun to have some funky powerups. So I came up with an item which modifies the player’s input. Basically my players have actions which they can perform by pressing one out of four buttons. Now when this item is used, all players (except the one who used it) have got their buttons shuffeld for a specific amount of time. Well physically they are still the same but each button does now trigger a different action (different to his previous action). So for the first time pressing a button after the use of the item you can’t be sure what will happen. Of course you know, you will trigger one out of your four abilites but you don’t know which. You will eventually figure out which button calls which action but by the time it takes to get used to it, the controls already changed back to normal.

Let me explain this with an small example:

For example could you use this feature to make one of your players gain advantage by still beeing able to use his buttons normally, whilst everyone else has to struggle with his control for 15 seconds.

7330d8b3a5.png

One more thing before we get started. I’m pretty sure there are certainly better ways to programm this. If you wish to, you can point out flaws and improvements in this thread, I would appreciate it. Really.
So let’s get started.

[HR][/HR]
As I looked for a way to shuffle the button presses I remembered my math courses and one word came to my mind: Permutations. So let’s have a look at it and recapulate what this actually was by using a simple example.
We want to create all possible permutations of the numbers (1,2,3). As there are 3 elements which can be shuffeld, there are 3! (=6) ways to shuffle them differently:

d67c2ab492.png

e.g. this means for [SUB]2[/SUB] that Button1(upper row) would shuffled still call Action1, whereas Button2 would call Action3 and Button3 accordingly calls Action2.

So all what we need at the moment are permutations with n-digits, where n is the number of buttons we have. Unfortunatley Unreal’s Math does not have a built in permutation function (at least I didn’t find any), so we are going to implement our own function for getting permutations. Yeay. But before we jump into coding, let’s think this through a little bit more. We do need permutations to shuffle, yes. But how are we going to save these permutations? The answer is in a custom struct which basically is a 2D Array (please notice that arrays use a zero-based numbering, meaning they start with 0 and not a 1). So the outer range of the array (first bracket) indicates at which permutation we are looking, whereas the inner brackets contain the actual permutation information (which numbers are switched). Formally written it would look somewhat like this, if we stick to our example.

9c58bd80ea59b3724e5eadf496ff05c7df68a0d2.png

You will have noticed that the index of the outer array ranges from 0 to (n!-1); here from 0 to 5. So now we’ve something in mind, let’s start to create the struct.

Setting Up the Struct

I’ve declared the structs in an extra header-only file, following this “tutorial”/adive here.

As we don’t have normaly arrays in UE, we are going to use the TArray<int32> for this. (You may reckognize this code from Rama’s tutorial about Structs).


#include "MyStructs.generated.h"
#pragma once



USTRUCT()
struct FPermutationRow
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	TArray<int32> Columns;

	void AddNewColumn()
	{
		Columns.Add(NULL);
	}

	//default properties
	FPermutationRow()
	{

	}
};


USTRUCT()
struct FPermutations
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	TArray<FPermutationRow> Rows;

	void AddNewRow()
	{
		Rows.Add(FPermutationRow());
	}

	void AddUninitialized(const int32 RowCount, const int32 ColCount)
	{
		//Add Rows
		for (int32 v = 0; v < RowCount; v++)
		{
			AddNewRow();
		}

		//Add Columns
		for (int32 v = 0; v < RowCount; v++)
		{
			for (int32 b = 0; b < ColCount; b++)
			{
				Rows[v].AddNewColumn();
			}
		}
	}

	//default properties
	FPermutations()
	{

	}
};

This basically sets up a two dimensional array which can we access via



FPermutations MyPermutationStruct;
MyPermutationStruct.AddUninitialized(n!, n); //n! rows, because we need enough rows for every possible permutation and n Columns because we have n-digits to swap.
MyPermutationStruct.Rows[x].Columns[y]

So first things first. We do need to calculate the factorial of n (= n!) to initialize our array. Sadly I couldn’t find any function for this in UKismetMath, again. So I quickly wrote my own code:


uint32 UMyStaticLibrary::GetFactorial(uint32 n)
{
	if (n <= 0)
	{
		return 1;
	}

	return GetFactorial(n - 1) * n;
}

As you may noticed or at least now will notice, that the class in which I’m going to write all this permutation code is a so called “MyStaticLibrary” which is also a concept Rama presented (man, this guy is everywhere!). It’s like our own addition to the Unreal Math library and can be accessed from anywhere (if header included) because it is static and only has static functions (which means it doesn’t need to be instantiated).

Permutation Calculation

Now let the fun part begin. To calculate the permutations we do need some functions (I’m using the algorithm Daniel Scocco provided here online), which we declare as following:


// @author Theo

#pragma once

#include "Object.h"
#include "MyStructs.h"
#include "MyStaticLibrary.generated.h"



UCLASS()
class THEOSECHS_API UMyStaticLibrary : public UObject
{
	GENERATED_BODY()
	static TArray<int32> MySwap(int32 x, int32 y, TArray<int32> TargetArray);
	static FPermutations Permutate(int32 k, int32 size, TArray<int32> TargetArray, FPermutations PermutationStruct);
	static FPermutations FillPermutationStruct(int32 size, TArray<int32> SourceArray, FPermutations PermutationStruct, int32 row);

public:
	static FPermutations GetPermutations(int32 k, int32 size, bool debug=false);
	static uint32 GetFactorial(uint32 n);
	static void DebugPrintPermutations(FPermutations Permutations);
};


The main function which we are going to call from outside (later more), as you might have guessed, will be “GetPermutations(int32 k, int32 size, bool debug)”.


FPermutations UMyStaticLibrary::GetPermutations(int32 k, int32 size, bool debug)
{
	//Init a normal array with all digits we want to use {1,2,3,4,5,6,7,8,9}
	TArray <int32> Array;
	for (int i = 1; i < 10; i++)
	{
		Array.Add(i);
	}

	FPermutations MyPermutations;
	if (k <= size)
	{
		MyPermutations.AddUninitialized(GetFactorial(k), size); //init our struct empty
		MyPermutations = Permutate(k, size, Array, MyPermutations); //this is where the magic happens
	}

	//debug msg
	if (debug)
	{
		DebugPrintPermutations(MyPermutations);
	}

	return MyPermutations;
}

Let me briefly explain the arguments. You can ignore the k at this moment, just set it to the same number as size. Size stands for the amount of numbers we can shuffle (in our example it would size = 3, because we have the numbers [1,2,3]). The bool just indicates that this permutation should be printed on screen (via a nested forloop).

Firstly we do create an array which only contains the numbers 1-9. We will use it later on. (Is there an easier way to init an TArray, like in Java?)


 digits] = {1,2,3,4,5,6,7,8,9};

After this we create our structure called MyPermutations. And now comes the important and fun part. I will not comment so much from here on because it’s mostly the permutation-algorithm but in a static manner. If you still have questions however, feel free to ask - I’ll do my best. :wink:


TArray<int32> UMyStaticLibrary::MySwap(int32 x, int32 y, TArray<int32> TargetArray)
{
	int32 temp = TargetArray[x];
	TargetArray[x] = TargetArray[y];
	TargetArray[y] = temp;
	return TargetArray;
}

FPermutations UMyStaticLibrary::Permutate(int32 k, int32 size, TArray<int32> TargetArray, FPermutations PermutationStruct)
{
	if (k == 0)
	{
		//value can only be 0 if it hasn't been written yet (because the TargetArray does not contain any 0's). So if it is 0 -> go set it
		for (int i = 0; i < PermutationStruct.Rows.Num(); i++) {
			if (PermutationStruct.Rows*.Columns[0] == 0) {
				return FillPermutationStruct(size, TargetArray, PermutationStruct, i);
				break;
			}
		}
	}

	else
	{
		for (int32 i = k - 1; i >= 0; i--) 
		{
			TargetArray = MySwap(i, k - 1, TargetArray);
			PermutationStruct = Permutate(k - 1, size, TargetArray, PermutationStruct);
			TargetArray = MySwap(i, k - 1, TargetArray);
		}
	}

	return PermutationStruct;
}

FPermutations UMyStaticLibrary::FillPermutationStruct(int32 size, TArray<int32> SourceArray, FPermutations PermutationStruct, int32 row)
{
	for (int32 i = 0; i < size; i++) {	
		PermutationStruct.Rows[row].Columns* = SourceArray*;
	}
	return PermutationStruct;
}

This should leave you with an easy access to permutations!

Setting Up the Input

But how exactly are we going to make use if it? Good news is that this can really be done easily. What about the bad news? Well, there aren’t any. It’s really quite simple. Make sure you’ve set the input commands in the settings of your project already:

c431ee2d69a72a0fd0779e0e4700246cf5898cd9.png

After you’ve done this you need a custom class derived from APlayerController, with the following methods:


// @author Theo

#pragma once

#include "GameFramework/PlayerController.h"
#include "MyStructs.h"
#include "BrawlingPlayerController.generated.h"

UCLASS()
class THEOSECHS_API ABrawlingPlayerController : public APlayerController
{
	GENERATED_BODY()

	ABrawlingPlayerController();
	virtual void SetupInputComponent() override;
	void Action_1();
	void Action_2();
	void Action_3();
	void Action_4();
	void Action_1_Pressed();
	void Action_2_Pressed();
	void Action_3_Pressed();
	void Action_4_Pressed();
	void ExecAction(int32 ActionPressed);
	FPermutations ButtonPermutation;
	int32 rndPermutationRow;
	int32 NumButtons;
};

In your SetupInputComponent() you are simply binding the buttonpress to a specific action which should be executed when this button is pressed. But instead of directly calling the action (like Jump/Shoot/etc) we do need some buffer-functions to process the input (Action_1_Pressed, Action_2_Pressed, …).


void ABrawlingPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();
	InputComponent->BindAction("ActionButton_1", IE_Pressed, this, &ABrawlingPlayerController::Action_1_Pressed);
	InputComponent->BindAction("ActionButton_2", IE_Pressed, this, &ABrawlingPlayerController::Action_2_Pressed);
	InputComponent->BindAction("ActionButton_3", IE_Pressed, this, &ABrawlingPlayerController::Action_3_Pressed);
	InputComponent->BindAction("ActionButton_4", IE_Pressed, this, &ABrawlingPlayerController::Action_4_Pressed);
}

These buffers have just one effect: they add a number, which can be associated with the button from which the press originated. This would not be possible otherwise, because we are - as far as i know - not able to add arguments in the BindAction call.




void ABrawlingPlayerController::Action_1_Pressed()
{
	ExecAction(1);
}

void ABrawlingPlayerController::Action_2_Pressed()
{
	ExecAction(2);
}

void ABrawlingPlayerController::Action_3_Pressed()
{
	ExecAction(3);
}

void ABrawlingPlayerController::Action_4_Pressed()
{
	ExecAction(4);
}


Our initial goal was to shuffle the actions. Now we are going to use our permutations to achieve this. So let’s initialize it in the constructor for example.



ABrawlingPlayerController::ABrawlingPlayerController()
{
	NumButtons = 4;
	ButtonPermutation = UMyStaticLibrary::GetPermutations(NumButtons, NumButtons); //our permutation struct
	rndPermutationRow = UKismetMathLibrary::RandomIntegerInRange(1, UMyStaticLibrary::GetFactorial(NumButtons) - 1); //-1 because we don't want the first permutation which means it is not shuffled at all.
}


We do create our permutation ButtonPermutation and a random int value which indicates the row (e.g. which permutation) we are going to use. We subtract one here, because we want to make sure that we don’t pick the permutation which is mapped to itself ( σ[SUB]1/SUB = 1; ). If you wanted the controls to change every X seconds, you would create a TimeHandler for this and set the *rndPermutationRow *every X-Seconds to a random value.

Let’s have a look at ExecAction now. It decides which action (jumping/shooting/…) is now (after shuffling) mapped to which button.
We use our input to feed the permutation and then decide, based on that outcome, which action should be executed. If we wouldn’t permutate the input (ActionPressed) we would always execute the exact action associated with the button like in a normal game.
Because of the permutations, we can also be sure that each button is now mapped to exactly one action and every action is still accessable. We do not lose any functionality or have to fear that two buttons now trigger the same effect.



void ABrawlingPlayerController::ExecAction(int32 ActionPressed)
{
	int32 Action = ButtonPermutation.Rows[rndPermutationRow].Columns[ActionPressed - 1];

	switch (Action)
	{
		case 1:
			Action_1();
			break;

		case 2:
			Action_2();
			break;

		case 3:
			Action_3();
			break;

		case 4:
			Action_4();
			break;

		default:
			GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Red, FString::Printf(TEXT("No default found for Action Nr. %d"), Action));
			break;
	}
}


Last but not least are the actual actions. It is up to you what is happening here. I just printed a debug message on screen to see if the system was working.



void ABrawlingPlayerController::Action_1()
{
	GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Red, TEXT("Action_1"));
}

void ABrawlingPlayerController::Action_2()
{
	GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Red, TEXT("Action_2"));
}

void ABrawlingPlayerController::Action_3()
{
	GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Red, TEXT("Action_3"));
}

void ABrawlingPlayerController::Action_4()
{
	GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Red, TEXT("Action_4"));
}


This is it. This is how you would change the mapping of buttons.

I don’t know if anyone really needs this, but perhaps someone can make use of it at some point.
(Please acknowledge my superior PAINT SKILLz!)

Merry Christmas!
~Theo