92cb9f9883
* initial additions * Move current_blind to game vars * tmp * tmp * tmp * tmp * tmp * tmp * tmp * Compile but is broken * temp fix for token location * tmp * Working, lets clean up * remove todo * Remove odd reroll blind variable * fix skipping blind screen * fix reroll * remove magic for blind token locations * clang format * Cleanup button highlight in blind menu * some cleanup * Add layout file for global UI rects * remove dead code * Clang format * clang format it up * Move reset top left funct to gfx utils * make note of soon-to-be removed vars * Cleanup gamevars declaration * Cleanup layout file * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Add include for stdbool.h in common_ui.h * Cleanup rect declaration * Start moving state machine stuff to new file * quick tmp * oops, add new files * working call list * fixed pointer stuff * working as per norm * working replacement * move defines to static const * fix compile issue * Add some missing "void"s * Working new substates * add statemachine to blind select * Fix game restart statemachine leak * clang-format * Clang format * Rework shop exit logic * Use new state machine in shop * Remove substates entirely * Remove debug stuff * Clang format * document state machine better * update list tests * clang-format * Update for PR * Claaaaang format * another clang-format * Remove extra state in round_end * cleanup docs for removals in lists * clang-format... again * update doxygen format * Clang format * Update list docs * Update lists tests * Fix bad conflict resolution --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
332 lines
6.7 KiB
C
332 lines
6.7 KiB
C
/**
|
|
* @file list.c
|
|
*
|
|
* @brief List functions implementation.
|
|
*/
|
|
|
|
#include "list.h"
|
|
|
|
#include "pool.h"
|
|
|
|
#include <stdbool.h>
|
|
|
|
/**
|
|
* Remove a node from a list.
|
|
*
|
|
* Remove a @ref ListNode from a @ref List. There are no checks to ensure that the
|
|
* passed `node` is actually part of the passed `list`. Handle with care.
|
|
* This is used with the @ref ListItr specifically.
|
|
*
|
|
* @param list pointer to a @ref List
|
|
* @param node pointer to a @ref ListNode
|
|
*/
|
|
static void s_list_remove_node(List* list, ListNode* node);
|
|
|
|
/**
|
|
* Get the next @ref ListNode in a @ref ListItr
|
|
*
|
|
* Note: Use of this function outside of testing is strongly discouraged. Unless
|
|
* you really want to access the @ref ListNode itself, it's preferred to just use
|
|
* @ref list_itr_next .
|
|
*
|
|
* @param itr pointer to the @ref ListItr
|
|
*
|
|
* @return A pointer to the @ref ListNode in the itr, otherwise return NULL.
|
|
*/
|
|
static ListNode* s_list_itr_node_next(ListItr* itr);
|
|
|
|
List list_init(void)
|
|
{
|
|
List list = LIST_DEFAULT;
|
|
return list;
|
|
}
|
|
|
|
void list_clear(List* list)
|
|
{
|
|
if (list_is_empty(list))
|
|
return;
|
|
|
|
ListItr itr = list_itr_create(list);
|
|
ListNode* ln;
|
|
|
|
while ((ln = s_list_itr_node_next(&itr)))
|
|
{
|
|
POOL_FREE(ListNode, ln);
|
|
}
|
|
|
|
list->head = NULL;
|
|
list->tail = NULL;
|
|
list->len = 0;
|
|
}
|
|
|
|
bool list_is_empty(const List* list)
|
|
{
|
|
return list->len == 0;
|
|
}
|
|
|
|
void list_push_front(List* list, void* data)
|
|
{
|
|
ListNode* node = POOL_GET(ListNode);
|
|
|
|
node->data = data;
|
|
node->prev = NULL;
|
|
node->next = list->head;
|
|
|
|
if (list_is_empty(list))
|
|
{
|
|
list->tail = node;
|
|
}
|
|
else
|
|
{
|
|
list->head->prev = node;
|
|
}
|
|
|
|
list->head = node;
|
|
|
|
list->len++;
|
|
}
|
|
|
|
void list_push_back(List* list, void* data)
|
|
{
|
|
ListNode* node = POOL_GET(ListNode);
|
|
node->data = data;
|
|
node->prev = list->tail;
|
|
node->next = NULL;
|
|
|
|
if (list_is_empty(list))
|
|
{
|
|
list->head = node;
|
|
}
|
|
else
|
|
{
|
|
list->tail->next = node;
|
|
}
|
|
|
|
list->tail = node;
|
|
|
|
list->len++;
|
|
}
|
|
|
|
void list_insert(List* list, void* data, unsigned int idx)
|
|
{
|
|
if (idx >= list->len)
|
|
{
|
|
list_push_back(list, data);
|
|
return;
|
|
}
|
|
|
|
if (idx == 0)
|
|
{
|
|
list_push_front(list, data);
|
|
return;
|
|
}
|
|
|
|
// After the above two checks the index is guaranteed to be inbetween the
|
|
// `head` and `tail` of the `list`. This means the actual list doesn't need
|
|
// to modify it's head and tail, only it's length. Simplifying the code below:
|
|
|
|
unsigned int curr_idx = 0;
|
|
ListItr itr = list_itr_create(list);
|
|
ListNode* ln;
|
|
|
|
while ((ln = s_list_itr_node_next(&itr)))
|
|
{
|
|
if (idx == curr_idx++)
|
|
{
|
|
ListNode* node = POOL_GET(ListNode);
|
|
node->prev = ln->prev;
|
|
node->next = ln;
|
|
ln->prev->next = node;
|
|
ln->prev = node;
|
|
node->data = data;
|
|
list->len++;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool list_swap(List* list, unsigned int idx_a, unsigned int idx_b)
|
|
{
|
|
if (idx_a >= list->len || idx_b >= list->len)
|
|
return false;
|
|
if (idx_a == idx_b)
|
|
return true; // swapping with yourself isn't technically an error
|
|
|
|
unsigned int curr_idx = 0;
|
|
unsigned int max_idx = idx_a > idx_b ? idx_a : idx_b;
|
|
ListNode* node_a = NULL;
|
|
ListNode* node_b = NULL;
|
|
|
|
ListItr itr = list_itr_create(list);
|
|
ListNode* ln;
|
|
|
|
do
|
|
{
|
|
ln = s_list_itr_node_next(&itr);
|
|
if (idx_a == curr_idx)
|
|
{
|
|
node_a = ln;
|
|
continue;
|
|
}
|
|
if (idx_b == curr_idx)
|
|
{
|
|
node_b = ln;
|
|
continue;
|
|
}
|
|
} while (max_idx != curr_idx++);
|
|
|
|
// Just swap the data pointers
|
|
void* tmp = node_a->data;
|
|
node_a->data = node_b->data;
|
|
node_b->data = tmp;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void s_list_remove_node(List* list, ListNode* node)
|
|
{
|
|
if (node->prev && !node->next) // end of list
|
|
{
|
|
node->prev->next = NULL;
|
|
list->tail = node->prev;
|
|
}
|
|
else if (node->prev && node->next) // somewhere in between
|
|
{
|
|
node->prev->next = node->next;
|
|
node->next->prev = node->prev;
|
|
}
|
|
else if (node->next && !node->prev) // beginning of list
|
|
{
|
|
node->next->prev = NULL;
|
|
list->head = node->next;
|
|
}
|
|
else if (!node->prev && !node->next) // only element in list
|
|
{
|
|
list->head = NULL;
|
|
list->tail = NULL;
|
|
}
|
|
|
|
POOL_FREE(ListNode, node);
|
|
|
|
list->len--;
|
|
}
|
|
|
|
int list_get_len(const List* list)
|
|
{
|
|
return list->len;
|
|
}
|
|
|
|
void* list_get_at_idx(List* list, unsigned int idx)
|
|
{
|
|
if (idx >= list_get_len(list))
|
|
return NULL;
|
|
|
|
int curr_idx = 0;
|
|
ListItr itr = list_itr_create(list);
|
|
void* data = NULL;
|
|
|
|
while ((data = list_itr_next(&itr)))
|
|
{
|
|
if (idx == curr_idx++)
|
|
return data;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool list_remove_at_idx(List* list, unsigned int idx)
|
|
{
|
|
if (idx >= list_get_len(list))
|
|
return false;
|
|
|
|
int len = 0;
|
|
ListItr itr = list_itr_create(list);
|
|
ListNode* ln;
|
|
|
|
while ((ln = s_list_itr_node_next(&itr)))
|
|
{
|
|
if (idx == len++)
|
|
{
|
|
s_list_remove_node(list, ln);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ListItr list_itr_create(List* list)
|
|
{
|
|
ListItr itr = {
|
|
.list = list,
|
|
.next_node = !list_is_empty(list) ? list->head : NULL,
|
|
.current_node = NULL,
|
|
.direction = LIST_ITR_FORWARD,
|
|
};
|
|
|
|
return itr;
|
|
}
|
|
|
|
ListItr rev_list_itr_create(List* list)
|
|
{
|
|
ListItr itr = {
|
|
.list = list,
|
|
.next_node = !list_is_empty(list) ? list->tail : NULL,
|
|
.current_node = NULL,
|
|
.direction = LIST_ITR_REVERSE,
|
|
};
|
|
|
|
return itr;
|
|
}
|
|
|
|
void* list_itr_next(ListItr* itr)
|
|
{
|
|
ListNode* ln = s_list_itr_node_next(itr);
|
|
return ln ? ln->data : NULL;
|
|
}
|
|
|
|
static ListNode* s_list_itr_node_next(ListItr* itr)
|
|
{
|
|
if (!itr->next_node)
|
|
return NULL;
|
|
|
|
itr->current_node = itr->next_node;
|
|
|
|
ListNode* ln = itr->next_node;
|
|
ListNode* next_itr_node = (itr->direction == LIST_ITR_FORWARD) ? ln->next : ln->prev;
|
|
|
|
if (next_itr_node)
|
|
{
|
|
itr->next_node = next_itr_node;
|
|
return ln;
|
|
}
|
|
|
|
itr->next_node = NULL;
|
|
return ln;
|
|
}
|
|
|
|
void list_itr_remove_current_node(ListItr* itr)
|
|
{
|
|
if (!itr || !itr->current_node)
|
|
return;
|
|
ListNode* tmp_prev = itr->current_node->prev;
|
|
s_list_remove_node(itr->list, itr->current_node);
|
|
itr->current_node = tmp_prev;
|
|
}
|
|
|
|
bool list_remove_data(List* list, void* data)
|
|
{
|
|
ListItr itr = list_itr_create(list);
|
|
ListNode* ln;
|
|
|
|
while ((ln = s_list_itr_node_next(&itr)))
|
|
{
|
|
if (ln->data == data)
|
|
{
|
|
s_list_remove_node(list, ln);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|