Skip to content

Thad writes blogs

this is a lot more work than i expected

Menu
  • About
  • Pin Posts
Menu

Advent of Code in Unreal Engine Day 11

Posted on December 12, 2022December 12, 2022 by Thad

Day 11 is all about the big numbers, and doing it in Unreal didn’t help me at all. Some horrible input handing, and some nasty gotchas. It’s all in C++.

It starts with the Monkey struct. A plain old C++ struct, and really just so I can mangle the text input into memory:

static const FString starting_str = TEXT("  Starting items: ");
static const FString operation_str = TEXT("  Operation: new = old ");
static const FString test_str = TEXT("  Test: divisible by ");
static const FString test_true = TEXT("    If true: throw to monkey ");
static const FString test_false= TEXT("    If false: throw to monkey ");

struct Monkey
{
	TArray< int64 > Items;
	FString Op;
	int64 Test;
	int TrueDest;
	int FalseDest;
	uint64 Inspections=0;
	
	Monkey(const TArray<FString>& Input, int& Line)
	{
		TArray<FString> Strings;
		ensure(Input[Line].StartsWith(starting_str));
		Input[Line].RightChop(starting_str.Len()).ParseIntoArray(Strings, TEXT(","));
		for (auto S : Strings)
			Items.Add( FCString::Atoi(*S) );
		Line++;

		ensure(Input[Line].StartsWith(operation_str));
		Op = Input[Line].RightChop(operation_str.Len());
		Line++;

		ensure(Input[Line].StartsWith(test_str));
		Test = FCString::Atoi(*Input[Line].RightChop(test_str.Len() ));
		Line++;

		ensure(Input[Line].StartsWith(test_true));
		TrueDest = FCString::Atoi(*Input[Line].RightChop(test_true.Len() ));
		Line++;

		ensure(Input[Line].StartsWith(test_false));
		FalseDest = FCString::Atoi(*Input[Line].RightChop(test_false.Len() ));
		Line++;
	}	
};

Then that is used by a single function that can handle either parts 1 and part 2 via the Relief and MaxRounds arguments:

//static
int UAoC22BPFunctionLibrary::D11_MonkeyAround(const TArray<FString>& Input, int Relief, int MaxRounds)
{
	int Line = 0;
	TArray< Monkey* > Monkeys;
	
	while (Line<Input.Num())
	{
		// skip monkey id, just goes up
		Line++;
		
		Monkeys.Emplace(new Monkey(Input, Line));

		// skip blank line
		Line++;
	}

	// find the combined multiplier of all the monkeys tests
	// see: https://en.wikipedia.org/wiki/Chinese_remainder_theorem
	int64 combined = 1;
	for (auto M : Monkeys)
	{
		combined *= M->Test;
	}

	for (int Rounds=0; Rounds!=MaxRounds; ++Rounds)
	{
		// ROUND
		for (auto M : Monkeys)
		{
			// TURN
			const int i = 0;
			while (M->Items.Num())
			{			
				int64 OpVal = (M->Op.EndsWith(TEXT("old")))
					? M->Items[i]
					: FCString::Atoi(*M->Op.RightChop(2));
				
				switch (**M->Op)
				{
				case '*': M->Items[i] *= OpVal; break;
				case '+': M->Items[i] += OpVal; break;
				default: UE_LOG(LogTemp, Warning, TEXT("Expected Op: %s"), *M->Op); break; 
				}

				M->Items[i] /= Relief;
				M->Items[i] %= combined;// keep it from overflowing
				
				if ((M->Items[i] % M->Test)==0)
				{
					Monkeys[M->TrueDest]->Items.Add( M->Items[i] );
					M->Items.RemoveAt(i);
				}
				else
				{
					Monkeys[M->FalseDest]->Items.Add( M->Items[i] );
					M->Items.RemoveAt(i);
				}

				M->Inspections++;
			}
		}
	}

	int C=0;
	for (auto M : Monkeys)
	{
		UE_LOG(LogTemp, Warning, TEXT("Monkey %i inspected items %i times."), C, M->Inspections);
		C++;
	}
	Monkeys.Sort([](auto a, auto b){return a.Inspections > b.Inspections;});
	UE_LOG(LogTemp, Warning, TEXT("Monkey business: %i * %i = %llu"),
		Monkeys[0]->Inspections, Monkeys[1]->Inspections, Monkeys[0]->Inspections*Monkeys[1]->Inspections);
	
	return 0;
}

This jumped out as “probably needs 64 bit ints” right away, but 2 further stumbling blocks held me up a long time:

  1. It overflows even 64 bits in the Items value repeatedly. You need to use Chinese remainder theorem to prevent that from affecting the results.
  2. The final combined monkey business result is also a large number. Forgetting to use %llu in my logging had me scratching my head for ages.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Archives

  • April 2026
  • October 2024
  • January 2023
  • December 2022
  • November 2022

Categories

  • Food
  • meta
  • Uncategorized
© 2026 Thad writes blogs | Powered by Minimalist Blog WordPress Theme