mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Not calling the indexchanged callback here meant that if K_BHeapSortDown didn't make any changes, the item at index 0 would never call the callback despite changing position.
599 lines
14 KiB
C
599 lines
14 KiB
C
// SONIC ROBO BLAST 2 KART
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2018-2020 by Sean "Sryder" Ryder
|
|
// Copyright (C) 2018-2020 by Kart Krew
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file k_bheap.c
|
|
/// \brief Binary Heap implementation for SRB2 code base.
|
|
|
|
#include "k_bheap.h"
|
|
|
|
#include "z_zone.h"
|
|
|
|
/*--------------------------------------------------
|
|
static boolean K_BHeapItemValidate(bheap_t *heap, bheapitem_t *item)
|
|
|
|
Validates an item on a heap to ensure it is correct and on that heap.
|
|
|
|
Input Arguments:-
|
|
heap - The heap to validate the item with
|
|
item - The item to validate
|
|
|
|
Return:-
|
|
True if the item is valid, false if it isn't.
|
|
--------------------------------------------------*/
|
|
static boolean K_BHeapItemValidate(bheap_t *heap, bheapitem_t *item)
|
|
{
|
|
boolean heapitemvalid = false;
|
|
|
|
I_Assert(heap != NULL);
|
|
I_Assert(item != NULL);
|
|
|
|
if ((item->data != NULL) && (item->heapindex < SIZE_MAX / 2) && (item->owner == heap))
|
|
{
|
|
heapitemvalid = true;
|
|
}
|
|
|
|
return heapitemvalid;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2)
|
|
|
|
Compares 2 items in the heap to find the better (lower) value one.
|
|
|
|
Input Arguments:-
|
|
heap - The heap to compare items
|
|
item1 - The first item to compare
|
|
item2 - The second item to compare
|
|
|
|
Return:-
|
|
The item out of the 2 sent in that has the better value, returns item2 if they are identical
|
|
--------------------------------------------------*/
|
|
static bheapitem_t *K_BHeapItemsCompare(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2)
|
|
{
|
|
bheapitem_t *lowervalueitem = NULL;
|
|
|
|
I_Assert(heap != NULL);
|
|
I_Assert(K_BHeapValid(heap));
|
|
I_Assert(item1 != NULL);
|
|
I_Assert(item2 != NULL);
|
|
I_Assert(K_BHeapItemValidate(heap, item1));
|
|
I_Assert(K_BHeapItemValidate(heap, item2));
|
|
|
|
(void)heap;
|
|
|
|
if (item1->value < item2->value)
|
|
{
|
|
lowervalueitem = item1;
|
|
}
|
|
else
|
|
{
|
|
lowervalueitem = item2;
|
|
}
|
|
|
|
return lowervalueitem;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2)
|
|
|
|
Swaps 2 items in the heap
|
|
|
|
Input Arguments:-
|
|
heap - The heap to swap items in
|
|
item1 - The first item to swap in the heap
|
|
item2 - The second item to swap in the heap
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BHeapSwapItems(bheap_t *heap, bheapitem_t *item1, bheapitem_t *item2)
|
|
{
|
|
I_Assert(heap != NULL);
|
|
I_Assert(K_BHeapValid(heap));
|
|
I_Assert(item1 != NULL);
|
|
I_Assert(item2 != NULL);
|
|
I_Assert(K_BHeapItemValidate(heap, item1));
|
|
I_Assert(K_BHeapItemValidate(heap, item2));
|
|
|
|
(void)heap;
|
|
|
|
{
|
|
size_t tempitemindex = item1->heapindex;
|
|
bheapitem_t tempitemstore = *item1;
|
|
|
|
// Swap the items fully with each other
|
|
*item1 = *item2;
|
|
*item2 = tempitemstore;
|
|
|
|
// Swap the heap index on each item to be correct
|
|
item2->heapindex = item1->heapindex;
|
|
item1->heapindex = tempitemindex;
|
|
|
|
if (item1->indexchanged != NULL)
|
|
{
|
|
item1->indexchanged(item1->data, item1->heapindex);
|
|
}
|
|
if (item2->indexchanged != NULL)
|
|
{
|
|
item2->indexchanged(item2->data, item2->heapindex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static size_t K_BHeapItemGetParentIndex(bheapitem_t *item)
|
|
|
|
Gets the parent index of a heap item
|
|
|
|
Input Arguments:-
|
|
item - The item to get the parent index of
|
|
|
|
Return:-
|
|
The parent index of the item
|
|
--------------------------------------------------*/
|
|
static size_t K_BHeapItemGetParentIndex(bheapitem_t *item)
|
|
{
|
|
size_t parentindex = SIZE_MAX;
|
|
|
|
I_Assert(item != NULL);
|
|
I_Assert(item->heapindex < (SIZE_MAX / 2));
|
|
|
|
parentindex = (item->heapindex - 1U) / 2U;
|
|
|
|
return parentindex;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static size_t K_BHeapItemGetLeftChildIndex(bheapitem_t *item)
|
|
|
|
Gets the left child index of a heap item
|
|
|
|
Input Arguments:-
|
|
item - The item to get the left child index of
|
|
|
|
Return:-
|
|
The left child index of the item
|
|
--------------------------------------------------*/
|
|
static size_t K_BHeapItemGetLeftChildIndex(bheapitem_t *item)
|
|
{
|
|
size_t leftchildindex = SIZE_MAX;
|
|
|
|
I_Assert(item != NULL);
|
|
I_Assert(item->heapindex < (SIZE_MAX / 2));
|
|
|
|
leftchildindex = (item->heapindex * 2U) + 1U;
|
|
|
|
return leftchildindex;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static size_t K_BHeapItemGetRightChildIndex(bheapitem_t *item)
|
|
|
|
Gets the right child index of a heap item
|
|
|
|
Input Arguments:-
|
|
item - The item to get the right child index of
|
|
|
|
Return:-
|
|
The right child index of the item
|
|
--------------------------------------------------*/
|
|
static size_t K_BHeapItemGetRightChildIndex(bheapitem_t *item)
|
|
{
|
|
size_t rightchildindex = SIZE_MAX;
|
|
|
|
I_Assert(item != NULL);
|
|
I_Assert(item->heapindex < (SIZE_MAX / 2));
|
|
|
|
rightchildindex = (item->heapindex * 2U) + 2U;
|
|
|
|
return rightchildindex;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BHeapSortUp(bheap_t *heap, bheapitem_t *item)
|
|
|
|
Sorts a heapitem up the list to its correct index, lower value items are higher up
|
|
|
|
Input Arguments:-
|
|
heap - The heap to sort the item up.
|
|
item - The item to sort up the heap
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BHeapSortUp(bheap_t *heap, bheapitem_t *item)
|
|
{
|
|
I_Assert(heap != NULL);
|
|
I_Assert(K_BHeapValid(heap));
|
|
I_Assert(item != NULL);
|
|
|
|
if (item->heapindex > 0U)
|
|
{
|
|
size_t parentindex = SIZE_MAX;
|
|
do
|
|
{
|
|
parentindex = K_BHeapItemGetParentIndex(item);
|
|
|
|
// Swap the nodes if the parent has a higher value
|
|
if (K_BHeapItemsCompare(heap, item, &heap->array[parentindex]) == item)
|
|
{
|
|
K_BHeapSwapItems(heap, item, &heap->array[parentindex]);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
} while (parentindex > 0U);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_BHeapSortDown(bheap_t *heap, bheapitem_t *item)
|
|
|
|
Sorts a heapitem down the list to its correct index, higher value items are further down
|
|
|
|
Input Arguments:-
|
|
heap - The heap to sort the item down.
|
|
item - The item to sort down the heap
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
static void K_BHeapSortDown(bheap_t *heap, bheapitem_t *item)
|
|
{
|
|
I_Assert(heap != NULL);
|
|
I_Assert(K_BHeapValid(heap));
|
|
I_Assert(item != NULL);
|
|
|
|
if (heap->count > 0U)
|
|
{
|
|
size_t leftchildindex = SIZE_MAX;
|
|
size_t rightchildindex = SIZE_MAX;
|
|
bheapitem_t *leftchild = NULL;
|
|
bheapitem_t *rightchild = NULL;
|
|
bheapitem_t *swapchild = NULL;
|
|
boolean noswapneeded = false;
|
|
|
|
do
|
|
{
|
|
leftchildindex = K_BHeapItemGetLeftChildIndex(item);
|
|
rightchildindex = K_BHeapItemGetRightChildIndex(item);
|
|
|
|
if (leftchildindex < heap->count)
|
|
{
|
|
leftchild = &heap->array[leftchildindex];
|
|
swapchild = leftchild;
|
|
if (rightchildindex < heap->count)
|
|
{
|
|
rightchild = &heap->array[rightchildindex];
|
|
// Choose the lower child node to swap with
|
|
if (K_BHeapItemsCompare(heap, leftchild, rightchild) == rightchild)
|
|
{
|
|
swapchild = rightchild;
|
|
}
|
|
}
|
|
|
|
// Swap with the lower child, if it's lower than item
|
|
if (K_BHeapItemsCompare(heap, swapchild, item) == swapchild)
|
|
{
|
|
K_BHeapSwapItems(heap, item, swapchild);
|
|
}
|
|
else
|
|
{
|
|
noswapneeded = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
noswapneeded = true;
|
|
}
|
|
|
|
if (noswapneeded)
|
|
{
|
|
break;
|
|
}
|
|
|
|
} while (item->heapindex < (heap->count - 1U));
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
boolean K_BHeapInit(bheap_t *const heap, size_t initialcapacity)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
boolean K_BHeapInit(bheap_t *const heap, size_t initialcapacity)
|
|
{
|
|
boolean initsuccess = false;
|
|
|
|
if (heap == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapInit.\n");
|
|
}
|
|
else if (initialcapacity == 0U)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "initialcapacity is 0 in K_BHeapInit.\n");
|
|
}
|
|
else
|
|
{
|
|
heap->array = Z_Calloc(initialcapacity * sizeof(bheapitem_t), PU_STATIC, NULL);
|
|
|
|
if (heap->array == NULL)
|
|
{
|
|
I_Error("K_BHeapInit: Out of Memory.");
|
|
}
|
|
|
|
heap->capacity = initialcapacity;
|
|
heap->count = 0U;
|
|
|
|
initsuccess = true;
|
|
}
|
|
|
|
return initsuccess;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
boolean K_BHeapValid(bheap_t *const heap)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
boolean K_BHeapValid(bheap_t *const heap)
|
|
{
|
|
boolean heapvalid = false;
|
|
|
|
if (heap == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapValid.\n");
|
|
}
|
|
else
|
|
{
|
|
if ((heap->capacity > 0U) && (heap->array != NULL))
|
|
{
|
|
heapvalid = true;
|
|
}
|
|
}
|
|
|
|
return heapvalid;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value, updateindexfunc changeindexcallback)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
boolean K_BHeapPush(bheap_t *const heap, void *const item, UINT32 value, updateindexfunc changeindexcallback)
|
|
{
|
|
boolean pushsuccess = false;
|
|
if (heap == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapPush.\n");
|
|
}
|
|
else if (!K_BHeapValid(heap))
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapPush.\n");
|
|
}
|
|
else if (item == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL item in K_BHeapPush.\n");
|
|
}
|
|
else if (heap->count >= (SIZE_MAX / 2))
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Tried to push too many items on binary heap in K_BHeapPush.\n");
|
|
}
|
|
else
|
|
{
|
|
bheapitem_t *newitem = NULL;
|
|
// If the capacity of the heap has been reached, a realloc is needed
|
|
// I'm just doing a basic double of capacity for simplicity
|
|
if (heap->count >= heap->capacity)
|
|
{
|
|
size_t newarraycapacity = heap->capacity * 2;
|
|
heap->array = Z_Realloc(heap->array, newarraycapacity, PU_STATIC, NULL);
|
|
|
|
if (heap->array == NULL)
|
|
{
|
|
I_Error("K_BHeapPush: Out of Memory.");
|
|
}
|
|
|
|
heap->capacity = newarraycapacity;
|
|
}
|
|
|
|
newitem = &heap->array[heap->count];
|
|
|
|
newitem->heapindex = heap->count;
|
|
newitem->owner = heap;
|
|
newitem->data = item;
|
|
newitem->value = value;
|
|
newitem->indexchanged = changeindexcallback;
|
|
|
|
if (newitem->indexchanged != NULL)
|
|
{
|
|
newitem->indexchanged(newitem->data, newitem->heapindex);
|
|
}
|
|
|
|
heap->count++;
|
|
|
|
K_BHeapSortUp(heap, &heap->array[heap->count - 1U]);
|
|
|
|
pushsuccess = true;
|
|
}
|
|
|
|
return pushsuccess;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage)
|
|
{
|
|
boolean popsuccess = false;
|
|
if (heap == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapPop.\n");
|
|
}
|
|
else if (!K_BHeapValid(heap))
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapPop.\n");
|
|
}
|
|
else if (heap->count == 0U)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Tried to Pop from empty heap in K_BHeapPop.\n");
|
|
}
|
|
else if (returnitemstorage == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL returnitemstorage in K_BHeapPop.\n");
|
|
}
|
|
else
|
|
{
|
|
*returnitemstorage = heap->array[0];
|
|
|
|
// Invalidate the heap related data from the return item
|
|
returnitemstorage->owner = NULL;
|
|
returnitemstorage->heapindex = SIZE_MAX;
|
|
|
|
if (returnitemstorage->indexchanged != NULL)
|
|
{
|
|
returnitemstorage->indexchanged(returnitemstorage->data, returnitemstorage->heapindex);
|
|
}
|
|
|
|
heap->count--;
|
|
|
|
heap->array[0] = heap->array[heap->count];
|
|
heap->array[0].heapindex = 0U;
|
|
memset(&heap->array[heap->count], 0x00, sizeof(bheapitem_t));
|
|
if (heap->array[0].indexchanged != NULL)
|
|
{
|
|
heap->array[0].indexchanged(heap->array[0].data, heap->array[0].heapindex);
|
|
}
|
|
|
|
K_BHeapSortDown(heap, &heap->array[0]);
|
|
popsuccess = true;
|
|
}
|
|
|
|
return popsuccess;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
boolean K_UpdateBHeapItemValue(bheapitem_t *const item, const UINT32 newvalue)
|
|
{
|
|
boolean updatevaluesuccess = false;
|
|
if (item == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL item in K_UpdateHeapItemValue.\n");
|
|
}
|
|
else if (item->owner == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "item has NULL owner in K_UpdateHeapItemValue.\n");
|
|
}
|
|
else if (K_BHeapItemValidate(item->owner, item) == false)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Invalid item in K_UpdateHeapItemValue.\n");
|
|
}
|
|
else if (K_BHeapValid(item->owner) == false)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Invalid item owner in K_UpdateHeapItemValue.\n");
|
|
}
|
|
else
|
|
{
|
|
size_t oldvalue = item->value;
|
|
item->value = newvalue;
|
|
if (newvalue < oldvalue)
|
|
{
|
|
K_BHeapSortUp(item->owner, item);
|
|
}
|
|
else if (newvalue > oldvalue)
|
|
{
|
|
K_BHeapSortDown(item->owner, item);
|
|
}
|
|
else
|
|
{
|
|
// No change is needed as the value is the same
|
|
}
|
|
|
|
}
|
|
|
|
return updatevaluesuccess;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
size_t K_BHeapContains(bheap_t *const heap, void *const data, size_t index)
|
|
{
|
|
size_t heapindexwithdata = SIZE_MAX;
|
|
|
|
if (heap == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapContains.\n");
|
|
}
|
|
else if (!K_BHeapValid(heap))
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapContains.\n");
|
|
}
|
|
else if (data == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL data in K_BHeapContains.\n");
|
|
}
|
|
else
|
|
{
|
|
if ((heap->count != 0U) && (index < heap->count))
|
|
{
|
|
if (heap->array[index].data == data)
|
|
{
|
|
heapindexwithdata = index;
|
|
}
|
|
}
|
|
else if (index == SIZE_MAX)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < heap->count; i++)
|
|
{
|
|
if (heap->array[i].data == data)
|
|
{
|
|
heapindexwithdata = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return heapindexwithdata;
|
|
}
|
|
|
|
boolean K_BHeapFree(bheap_t *const heap)
|
|
{
|
|
boolean freesuccess = false;
|
|
|
|
if (heap == NULL)
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "NULL heap in K_BHeapFree.\n");
|
|
}
|
|
else if (!K_BHeapValid(heap))
|
|
{
|
|
CONS_Debug(DBG_GAMELOGIC, "Uninitialised heap in K_BHeapFree.\n");
|
|
}
|
|
else
|
|
{
|
|
Z_Free(heap->array);
|
|
heap->array = NULL;
|
|
heap->capacity = 0U;
|
|
heap->count = 0U;
|
|
freesuccess = true;
|
|
}
|
|
|
|
return freesuccess;
|
|
}
|