Update list implementation to work with memory pools (#168)
* Introduce indexed list implementation * Fix CI tests for pool * Take bitset out of pool * Replace joker bitset interactions with wrappers * Add bitset tests * Add test to gitignore --------- Co-authored-by: rfehr-idexx <ric-fehr@idexx.com>
This commit is contained in:
@@ -4,4 +4,6 @@
|
|||||||
*.sln
|
*.sln
|
||||||
*.vcxproj*
|
*.vcxproj*
|
||||||
/balatro-gba/x64/**
|
/balatro-gba/x64/**
|
||||||
|
/tests/bitset/build
|
||||||
|
/tests/list/build
|
||||||
/tests/pool/build
|
/tests/pool/build
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
/** @file bitset.h
|
||||||
|
*
|
||||||
|
* @brief A bitset for operating on flags
|
||||||
|
*/
|
||||||
|
#ifndef BITSET_H
|
||||||
|
#define BITSET_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def BITSET_BITS_PER_WORD
|
||||||
|
* @brief Number of bits in a word for a bitset.
|
||||||
|
*
|
||||||
|
* Number of bits in a word for a bitset. Will always be 32 here.
|
||||||
|
*/
|
||||||
|
#define BITSET_BITS_PER_WORD 32
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def BITSET_ARRAY_SIZE
|
||||||
|
* @brief Number of words in a bitset
|
||||||
|
*
|
||||||
|
* Number of words in every bitset. This represents the maximum number and each
|
||||||
|
* bitset will always use this number of words, though it's capacity can be any length
|
||||||
|
* from `1` to `BITSET_BITS_PER_WORD * BITSET_ARRAY_SIZE`
|
||||||
|
*/
|
||||||
|
#define BITSET_ARRAY_SIZE 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def BITSET_MAX_BITS
|
||||||
|
* @brief Maximum number of bits in a bitset
|
||||||
|
*/
|
||||||
|
#define BITSET_MAX_BITS BITSET_BITS_PER_WORD * BITSET_ARRAY_SIZE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A bitset spread across multiple `uint32_t` words
|
||||||
|
*/
|
||||||
|
typedef struct Bitset
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Word array of `uint32_t` to hold the bitset data
|
||||||
|
*/
|
||||||
|
uint32_t *w;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of bits in a word, will be 32
|
||||||
|
*/
|
||||||
|
uint32_t nbits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of words int the `w` array
|
||||||
|
*/
|
||||||
|
uint32_t nwords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of actual flags (nbits * nwords)
|
||||||
|
*/
|
||||||
|
uint32_t cap;
|
||||||
|
} Bitset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An iterator into a @ref Bitset
|
||||||
|
*
|
||||||
|
* This iterator will parse and find the next index to a '1' bit as efficiently as possible.
|
||||||
|
*
|
||||||
|
* There is no implementation of the following (yet):
|
||||||
|
* - Reverse iteration
|
||||||
|
* - Bit-by-bit iteration
|
||||||
|
* - Iterating on offsets to '0' bits
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief @ref Bitset this is iterating through
|
||||||
|
*/
|
||||||
|
const Bitset *bitset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Current word the iterator is on
|
||||||
|
*/
|
||||||
|
int word;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Current bit the iterator is on
|
||||||
|
*/
|
||||||
|
int bit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of bits that have been iterated through in total
|
||||||
|
*/
|
||||||
|
int itr;
|
||||||
|
} BitsetItr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set a flag in a bitset to a value
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
* @param idx the index of the flag to set
|
||||||
|
* @param on the value to set the flag to
|
||||||
|
*/
|
||||||
|
void bitset_set_idx(Bitset *bitset, int idx, bool on);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the value of a flag
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
* @param idx the index of the flag to get
|
||||||
|
*
|
||||||
|
* @return the value of the flag as `true` or `false`
|
||||||
|
*/
|
||||||
|
bool bitset_get_idx(Bitset *bitset, int idx);
|
||||||
|
|
||||||
|
// Get the next free (set to 0) index in the bitset.
|
||||||
|
// It also sets the bit which it maybe should do... It really shouldn't do two things
|
||||||
|
// But it's such a fast operation idk. // TODO: decide what you wanna do
|
||||||
|
int bitset_allocate_idx(Bitset *bitset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the bitset, all to 0
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
*/
|
||||||
|
void bitset_clear(Bitset *bitset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a bitset is empty (all 0's)
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
*
|
||||||
|
* @return `true` if empty, `false` otherwise
|
||||||
|
*/
|
||||||
|
bool bitset_is_empty(Bitset *bitset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count how many bits are set to `1` in a bitset
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
*
|
||||||
|
* @return The number of flags set to `1` in a bitset
|
||||||
|
*/
|
||||||
|
int bitset_num_set_bits(Bitset *bitset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the index of the nth set bit
|
||||||
|
*
|
||||||
|
* Find the index of the nth flag set to `1`. This function is useful to get one value quickly,
|
||||||
|
* but does not operate iteratively well. Use a @BitsetItr for iterative access to a bitset.
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
*
|
||||||
|
* @return The index of the nth flag set to `1` in the bitset
|
||||||
|
*/
|
||||||
|
int bitset_find_idx_of_nth_set(const Bitset *bitset, int n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Declare a @ref BitsetItr
|
||||||
|
*
|
||||||
|
* @param bitset A @ref Bitset to operate on
|
||||||
|
*
|
||||||
|
* @return A newly constructed BitsetItr
|
||||||
|
*/
|
||||||
|
BitsetItr bitset_itr_create(const Bitset* bitset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the index of the next set bit in the bitset from a @ref BitsetItr
|
||||||
|
*
|
||||||
|
* @param itr A @ref BitsetItr to operate on
|
||||||
|
*
|
||||||
|
* @return a positive number if successful, UNDEFINED otherwise (out-of-bounds)
|
||||||
|
*/
|
||||||
|
int bitset_itr_next(BitsetItr* itr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def BITSET_DEFINE
|
||||||
|
* @brief Make a standard bitset
|
||||||
|
*
|
||||||
|
* Make a bitset with a valid static array to store it's array of words.
|
||||||
|
*
|
||||||
|
* Use this to define bitsets in the code, specifically as a `static` scoped
|
||||||
|
* variable. The passed `name` will be the same name as the bitset.
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
*
|
||||||
|
* ```c
|
||||||
|
* BITSET_DEFINE(_my_bitset, 128);
|
||||||
|
* // normal operation...
|
||||||
|
* bitset_clear(&_my_bitset);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param name the name of the bitset
|
||||||
|
* @param capacity the capacity of the bitset
|
||||||
|
*/
|
||||||
|
#define BITSET_DEFINE(name, capacity) \
|
||||||
|
static uint32_t name##_w[BITSET_ARRAY_SIZE] = {0}; \
|
||||||
|
static Bitset name = \
|
||||||
|
{ \
|
||||||
|
.w = name##_w, \
|
||||||
|
.nbits = BITSET_BITS_PER_WORD, \
|
||||||
|
.nwords = BITSET_ARRAY_SIZE, \
|
||||||
|
.cap = capacity, \
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BITSET_H
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "joker.h"
|
#include "joker.h"
|
||||||
#include "card.h"
|
#include "card.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
POOL_ENTRY(Sprite, MAX_SPRITES);
|
POOL_ENTRY(Sprite, MAX_SPRITES);
|
||||||
POOL_ENTRY(SpriteObject, MAX_SPRITE_OBJECTS);
|
POOL_ENTRY(SpriteObject, MAX_SPRITE_OBJECTS);
|
||||||
@@ -8,3 +9,4 @@ POOL_ENTRY(Joker, MAX_ACTIVE_JOKERS);
|
|||||||
POOL_ENTRY(JokerObject, MAX_ACTIVE_JOKERS);
|
POOL_ENTRY(JokerObject, MAX_ACTIVE_JOKERS);
|
||||||
POOL_ENTRY(Card, MAX_CARDS);
|
POOL_ENTRY(Card, MAX_CARDS);
|
||||||
POOL_ENTRY(CardObject, MAX_CARDS_ON_SCREEN);
|
POOL_ENTRY(CardObject, MAX_CARDS_ON_SCREEN);
|
||||||
|
POOL_ENTRY(ListNode, MAX_LIST_NODES);
|
||||||
|
|||||||
+1
-2
@@ -91,7 +91,6 @@ void game_init();
|
|||||||
void game_update();
|
void game_update();
|
||||||
void game_change_state(enum GameState new_game_state);
|
void game_change_state(enum GameState new_game_state);
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
struct List;
|
struct List;
|
||||||
typedef struct List List;
|
typedef struct List List;
|
||||||
|
|
||||||
@@ -106,9 +105,9 @@ int hand_get_size(void);
|
|||||||
CardObject** get_played_array(void);
|
CardObject** get_played_array(void);
|
||||||
int get_played_top(void);
|
int get_played_top(void);
|
||||||
int get_scored_card_index(void);
|
int get_scored_card_index(void);
|
||||||
List* get_jokers(void);
|
|
||||||
bool is_joker_owned(int joker_id);
|
bool is_joker_owned(int joker_id);
|
||||||
bool card_is_face(Card *card);
|
bool card_is_face(Card *card);
|
||||||
|
List* get_jokers_list(void);
|
||||||
|
|
||||||
int get_deck_top(void);
|
int get_deck_top(void);
|
||||||
int get_num_discards_remaining(void);
|
int get_num_discards_remaining(void);
|
||||||
|
|||||||
+2
-1
@@ -12,6 +12,8 @@
|
|||||||
// plus the amount that can fit in the shop, 8 should be fine. For now...
|
// plus the amount that can fit in the shop, 8 should be fine. For now...
|
||||||
#define MAX_ACTIVE_JOKERS 8
|
#define MAX_ACTIVE_JOKERS 8
|
||||||
|
|
||||||
|
#define MAX_DEFINABLE_JOKERS 150
|
||||||
|
|
||||||
#define JOKER_TID (MAX_HAND_SIZE + MAX_SELECTION_SIZE) * JOKER_SPRITE_OFFSET // Tile ID for the starting index in the tile memory
|
#define JOKER_TID (MAX_HAND_SIZE + MAX_SELECTION_SIZE) * JOKER_SPRITE_OFFSET // Tile ID for the starting index in the tile memory
|
||||||
#define JOKER_SPRITE_OFFSET 16 // Offset for the joker sprites
|
#define JOKER_SPRITE_OFFSET 16 // Offset for the joker sprites
|
||||||
#define JOKER_BASE_PB 4 // The starting palette index for the jokers
|
#define JOKER_BASE_PB 4 // The starting palette index for the jokers
|
||||||
@@ -68,7 +70,6 @@ enum JokerEvent
|
|||||||
#define SHORTCUT_JOKER_ID 26
|
#define SHORTCUT_JOKER_ID 26
|
||||||
#define FOUR_FINGERS_JOKER_ID 48
|
#define FOUR_FINGERS_JOKER_ID 48
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
u8 id; // Unique ID for the joker, used to identify different jokers
|
u8 id; // Unique ID for the joker, used to identify different jokers
|
||||||
|
|||||||
+175
-15
@@ -1,27 +1,187 @@
|
|||||||
|
/** @file list.h
|
||||||
|
*
|
||||||
|
* @brief A doubly-linked list
|
||||||
|
*
|
||||||
|
* List Implementation
|
||||||
|
* ===================
|
||||||
|
*
|
||||||
|
* - This @ref List operates as a linked list @ref ListNodes. It operates as a regular doubly-linked list
|
||||||
|
* but doesn't allocate memory and rather gets @ref ListNodes from a pool.
|
||||||
|
*/
|
||||||
#ifndef LIST_H
|
#ifndef LIST_H
|
||||||
#define LIST_H
|
#define LIST_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define MAX_LIST_NODES 128
|
||||||
|
|
||||||
|
typedef struct ListNode ListNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A single entry in a @ref List
|
||||||
|
*/
|
||||||
|
struct ListNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief The previous @ref ListNode in the associated @ref List, NULL if at the `head` of the list
|
||||||
|
*/
|
||||||
|
ListNode* prev;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The next @ref ListNode in the associated @ref List, NULL if at the `tail` of the list
|
||||||
|
*/
|
||||||
|
ListNode* next;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pointer to generic data stored in this node
|
||||||
|
*/
|
||||||
|
void* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A doubly-linked list
|
||||||
|
*/
|
||||||
typedef struct List
|
typedef struct List
|
||||||
{
|
{
|
||||||
void** _array;
|
/**
|
||||||
int size;
|
* @brief The first entry in the list
|
||||||
int allocated_size;
|
*/
|
||||||
|
ListNode* head;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The last entry in the list
|
||||||
|
*/
|
||||||
|
ListNode* tail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of elements in list
|
||||||
|
*/
|
||||||
|
int len;
|
||||||
} List;
|
} List;
|
||||||
|
|
||||||
List *list_new(int init_size);
|
/**
|
||||||
void list_destroy(List **list);
|
* @brief An iterator into a list
|
||||||
bool list_append(List *list, void *value);
|
*/
|
||||||
bool list_remove_by_idx(List *list, int index);
|
typedef struct
|
||||||
void* list_get(List *list, int index);
|
{
|
||||||
int list_get_size(List *list);
|
/**
|
||||||
bool list_remove_by_value(List *list, void *value);
|
* @brief A pointer to the @ref List this is iterating through
|
||||||
bool list_exists(List *list, void *value);
|
*/
|
||||||
|
List* list;
|
||||||
|
|
||||||
bool int_list_append(List *list, intptr_t value);
|
/**
|
||||||
intptr_t int_list_get(List *list, int index);
|
* @brief The next node in the list
|
||||||
bool int_list_remove_by_value(List *list, intptr_t value);
|
*/
|
||||||
bool int_list_exists(List *list, intptr_t value);
|
ListNode* next_node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current node in the list iterator
|
||||||
|
*
|
||||||
|
* The node of the most recently returned data from @ref list_itr_next() .
|
||||||
|
*/
|
||||||
|
ListNode* current_node;
|
||||||
|
} ListItr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a list.
|
||||||
|
*
|
||||||
|
* While this function does not allocate memory for the list itself, the list does allocate memory for each element.
|
||||||
|
* So every created list must be freed with @ref list_clear to ensure the list's nodes are deleted properly.
|
||||||
|
*
|
||||||
|
* @return A @ref List with head and tail reset.
|
||||||
|
*/
|
||||||
|
List list_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a list.
|
||||||
|
*
|
||||||
|
* Go through the list and free each node and set the `head` and `tail` to `NULL`.
|
||||||
|
* Note, it doesn't "free" the data at the node.
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List to clear
|
||||||
|
*/
|
||||||
|
void list_clear(List* list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a list is empty
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
*
|
||||||
|
* @return `true` if the `list` is empty, `false` otherwise.
|
||||||
|
*/
|
||||||
|
bool list_is_empty(const List* list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepend an entry to the `head` of a @ref list
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
* @param data pointer to data to put into the @ref List
|
||||||
|
*/
|
||||||
|
void list_push_front(List* list, void* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append an entry to the `tail` of a @ref list
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
* @param data pointer to data to put into the @ref List
|
||||||
|
*/
|
||||||
|
void list_push_back(List* list, void* data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a List's node at it's nth index
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
* @param n index of the desired @ref ListNode in the list
|
||||||
|
*
|
||||||
|
* @return a pointer to the data at the nth @ref ListNode, or NULL if out-of-bounds
|
||||||
|
*/
|
||||||
|
void* list_get_at_idx(List *list, int n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a List's node at it's nth index
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
* @param n index of the desired @ref ListNode in the list
|
||||||
|
*
|
||||||
|
* @return `true` if successfully removed, `false` if out-of-bounds
|
||||||
|
*/
|
||||||
|
bool list_remove_at_idx(List *list, int n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of elements in a @ref List
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
*
|
||||||
|
* @return The number of elements in the list
|
||||||
|
*/
|
||||||
|
int list_get_len(const List* list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare a @ref ListItr
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
*
|
||||||
|
* @return A new @ref ListItr
|
||||||
|
*/
|
||||||
|
ListItr list_itr_create(List* list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next data entry in a @ref ListItr
|
||||||
|
*
|
||||||
|
* @param itr pointer to the @ref ListItr
|
||||||
|
*
|
||||||
|
* @return A pointer to the data pointer at the next @ref ListNode if valid, otherwise return NULL.
|
||||||
|
*/
|
||||||
|
void* list_itr_next(ListItr* itr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the current @ref ListNode from the iterator.
|
||||||
|
*
|
||||||
|
* The "current node" corresponds to the list node associated with the
|
||||||
|
* most recently returned valu from @ref list_itr_next()
|
||||||
|
*
|
||||||
|
* @param itr pointer to the @ref ListItr
|
||||||
|
*/
|
||||||
|
void list_itr_remove_current_node(ListItr* itr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+21
-23
@@ -2,6 +2,9 @@
|
|||||||
#define POOL_H
|
#define POOL_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "bitset.h"
|
||||||
|
|
||||||
#ifdef POOLS_TEST_ENV
|
#ifdef POOLS_TEST_ENV
|
||||||
#define POOLS_DEF_FILE "def_test_mempool.h"
|
#define POOLS_DEF_FILE "def_test_mempool.h"
|
||||||
@@ -9,44 +12,28 @@
|
|||||||
#define POOLS_DEF_FILE "def_balatro_mempool.h"
|
#define POOLS_DEF_FILE "def_balatro_mempool.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define POOL_BITS_PER_WORD 32
|
|
||||||
#define POOL_BITMAP_BYTES 8
|
|
||||||
|
|
||||||
typedef struct PoolBitmap {
|
|
||||||
uint32_t *w;
|
|
||||||
uint32_t nbits;
|
|
||||||
uint32_t nwords;
|
|
||||||
uint32_t cap;
|
|
||||||
} PoolBitmap;
|
|
||||||
|
|
||||||
void pool_bm_clear_idx(PoolBitmap *bm, int idx);
|
|
||||||
int pool_bm_get_free_idx(PoolBitmap *bm);
|
|
||||||
|
|
||||||
#define POOL_DECLARE_TYPE(type) \
|
#define POOL_DECLARE_TYPE(type) \
|
||||||
typedef struct \
|
typedef struct \
|
||||||
{ \
|
{ \
|
||||||
PoolBitmap bm; \
|
Bitset* bitset; \
|
||||||
type * objects; \
|
type * objects; \
|
||||||
} type##Pool; \
|
} type##Pool; \
|
||||||
type *pool_get_##type(); \
|
type *pool_get_##type(); \
|
||||||
void pool_free_##type(type *obj); \
|
void pool_free_##type(type *obj); \
|
||||||
|
int pool_idx_##type(type *obj); \
|
||||||
|
type *pool_at_##type(int idx);
|
||||||
|
|
||||||
#define POOL_DEFINE_TYPE(type, capacity) \
|
#define POOL_DEFINE_TYPE(type, capacity) \
|
||||||
|
BITSET_DEFINE(type##_bitset, capacity) \
|
||||||
static type type##_storage[capacity]; \
|
static type type##_storage[capacity]; \
|
||||||
static uint32_t type##_bitmap_w[POOL_BITMAP_BYTES] = {0}; \
|
|
||||||
static type##Pool type##_pool = \
|
static type##Pool type##_pool = \
|
||||||
{ \
|
{ \
|
||||||
.bm = { \
|
.bitset = & type##_bitset, \
|
||||||
.w = type##_bitmap_w, \
|
|
||||||
.nbits = POOL_BITS_PER_WORD, \
|
|
||||||
.nwords = POOL_BITMAP_BYTES, \
|
|
||||||
.cap = capacity, \
|
|
||||||
}, \
|
|
||||||
.objects = type##_storage, \
|
.objects = type##_storage, \
|
||||||
}; \
|
}; \
|
||||||
type * pool_get_##type() \
|
type * pool_get_##type() \
|
||||||
{ \
|
{ \
|
||||||
int free_offset = pool_bm_get_free_idx(&type##_pool.bm); \
|
int free_offset = bitset_allocate_idx(type##_pool.bitset); \
|
||||||
if(free_offset == -1) return NULL; \
|
if(free_offset == -1) return NULL; \
|
||||||
return &type##_pool.objects[free_offset]; \
|
return &type##_pool.objects[free_offset]; \
|
||||||
} \
|
} \
|
||||||
@@ -54,11 +41,22 @@ int pool_bm_get_free_idx(PoolBitmap *bm);
|
|||||||
{ \
|
{ \
|
||||||
if(entry == NULL) return; \
|
if(entry == NULL) return; \
|
||||||
int offset = entry - &type##_pool.objects[0]; \
|
int offset = entry - &type##_pool.objects[0]; \
|
||||||
pool_bm_clear_idx(&type##_pool.bm, offset); \
|
bitset_set_idx(type##_pool.bitset, offset, false); \
|
||||||
|
} \
|
||||||
|
int pool_idx_##type(type *entry) \
|
||||||
|
{ \
|
||||||
|
return entry - &type##_pool.objects[0]; \
|
||||||
|
} \
|
||||||
|
type *pool_at_##type(int idx) \
|
||||||
|
{ \
|
||||||
|
if(idx < 0 || idx >= (type##_pool.bitset)->cap) return NULL; \
|
||||||
|
return &type##_pool.objects[idx]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define POOL_GET(type) pool_get_##type()
|
#define POOL_GET(type) pool_get_##type()
|
||||||
#define POOL_FREE(type, obj) pool_free_##type(obj)
|
#define POOL_FREE(type, obj) pool_free_##type(obj)
|
||||||
|
#define POOL_IDX(type, obj) pool_idx_##type(obj) // the index of the object
|
||||||
|
#define POOL_AT(type, idx) pool_at_##type(idx) // the object at
|
||||||
|
|
||||||
#define POOL_ENTRY(name, capacity) \
|
#define POOL_ENTRY(name, capacity) \
|
||||||
POOL_DECLARE_TYPE(name);
|
POOL_DECLARE_TYPE(name);
|
||||||
|
|||||||
@@ -55,23 +55,22 @@ for name in $(get_pool_names); do
|
|||||||
sed -E 's@ +@ @g; s@^ @@' | \
|
sed -E 's@ +@ @g; s@^ @@' | \
|
||||||
tr -d '\n' \
|
tr -d '\n' \
|
||||||
)"
|
)"
|
||||||
output_bm="$( \
|
output_bitset="$( \
|
||||||
"$READELF" -sW "$ELF_FILE" | \
|
"$READELF" -sW "$ELF_FILE" | \
|
||||||
grep -E "${name}_bitmap_w" | \
|
grep -E "${name}_bitset_w" | \
|
||||||
grep OBJECT | \
|
grep OBJECT | \
|
||||||
sed -E 's@ +@ @g; s@^ @@' | \
|
sed -E 's@ +@ @g; s@^ @@' | \
|
||||||
tr -d '\n' \
|
tr -d '\n' \
|
||||||
)"
|
)"
|
||||||
|
|
||||||
|
|
||||||
address="$(cut -d ' ' -f 2 <<< $output_pool)"
|
address="$(cut -d ' ' -f 2 <<< $output_pool)"
|
||||||
pool_size="$(cut -d ' ' -f 3 <<< $output_pool)"
|
pool_size="$(cut -d ' ' -f 3 <<< $output_pool)"
|
||||||
func_size="$(cut -d ' ' -f 3 <<< $output_func)"
|
func_size="$(cut -d ' ' -f 3 <<< $output_func)"
|
||||||
bm_size="$(cut -d ' ' -f 3 <<< $output_bm)"
|
bitset_size="$(cut -d ' ' -f 3 <<< $output_bitset)"
|
||||||
|
|
||||||
TOTAL_BYTES=$(( TOTAL_BYTES + pool_size + func_size + bm_size ))
|
TOTAL_BYTES=$(( TOTAL_BYTES + pool_size + func_size + bitset_size ))
|
||||||
|
|
||||||
printf "%-16s| 0x%8s | %-10u | %-10u | %-10u \n" "$name" "$address" "$pool_size" "$func_size" "$bm_size"
|
printf "%-16s| 0x%8s | %-10u | %-10u | %-10u \n" "$name" "$address" "$pool_size" "$func_size" "$bitset_size"
|
||||||
done
|
done
|
||||||
|
|
||||||
print_line_break
|
print_line_break
|
||||||
|
|||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
#include "bitset.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void bitset_set_idx(Bitset *bitset, int idx, bool on)
|
||||||
|
{
|
||||||
|
uint32_t i = idx / BITSET_BITS_PER_WORD;
|
||||||
|
uint32_t b = idx % BITSET_BITS_PER_WORD;
|
||||||
|
|
||||||
|
// Below are the "fast" forms of the above operations, respectively.
|
||||||
|
// These are more efficient, but removed for readability
|
||||||
|
// See: https://github.com/cellos51/balatro-gba/pull/132#discussion_r2365966071
|
||||||
|
// Divide by 32 to get the word index
|
||||||
|
//uint32_t i = idx >> 5;
|
||||||
|
// Get last 5-bits, same as a modulo (% 32) operation on positive numbers
|
||||||
|
//uint32_t b = idx & 0x1F;
|
||||||
|
if(on)
|
||||||
|
{
|
||||||
|
bitset->w[i] |= (uint32_t)1 << b;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bitset->w[i] &= ~((uint32_t)1 << b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitset_allocate_idx(Bitset *bitset)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < bitset->nwords; i++)
|
||||||
|
{
|
||||||
|
uint32_t inv = ~bitset->w[i];
|
||||||
|
|
||||||
|
// guard so we don't call `ctz` with 0, since __builtin_ctz(0) is undefined
|
||||||
|
// https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz
|
||||||
|
//
|
||||||
|
// By using the bitwise inverse of the word, you can skip words that are full
|
||||||
|
// quickly (where the value is 0 or 'false' since all bits are '1', or 'in use'). Any value greater
|
||||||
|
// than 0 indicates there is a free slot. Then, when counting the trailing 0's, you can test very quickly
|
||||||
|
// where the first free slot is. This operation prevents looping through every bit of filled flags, and
|
||||||
|
// will instead operate only on the first word with free slots.
|
||||||
|
if (inv)
|
||||||
|
{
|
||||||
|
int bit = __builtin_ctz(inv);
|
||||||
|
bitset->w[i] |= ((uint32_t)1 << bit);
|
||||||
|
int idx = i * BITSET_BITS_PER_WORD + bit;
|
||||||
|
return (idx < bitset->cap) ? idx : UNDEFINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bitset_clear(Bitset *bitset)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < bitset->nwords; i++)
|
||||||
|
{
|
||||||
|
bitset->w[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bitset_is_empty(Bitset *bitset)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < bitset->nwords; i++)
|
||||||
|
{
|
||||||
|
if(bitset->w[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bitset_get_idx(Bitset *bitset, int idx)
|
||||||
|
{
|
||||||
|
uint32_t i = idx / BITSET_BITS_PER_WORD;
|
||||||
|
uint32_t b = idx % BITSET_BITS_PER_WORD;
|
||||||
|
|
||||||
|
return bitset->w[i] & (uint32_t)1 << b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitset_num_set_bits(Bitset *bitset)
|
||||||
|
{
|
||||||
|
int sum = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < bitset->nwords; i++)
|
||||||
|
{
|
||||||
|
sum += __builtin_popcount(bitset->w[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitset_find_idx_of_nth_set(const Bitset *bitset, int n)
|
||||||
|
{
|
||||||
|
int tracker = 0;
|
||||||
|
int prev_tracker = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < bitset->nwords; i++)
|
||||||
|
{
|
||||||
|
tracker += __builtin_popcount(bitset->w[i]);
|
||||||
|
|
||||||
|
if(tracker > n)
|
||||||
|
{
|
||||||
|
// The index is here somewhere
|
||||||
|
int base = prev_tracker - 1; // this one is to count the 1's not the offset, underflow to -1 is good for finding the 0 index
|
||||||
|
int offset = bitset->nbits * i; // this one is for the actual offset we want to map the id to
|
||||||
|
for (int j = 0; j < bitset->nbits; j++)
|
||||||
|
{
|
||||||
|
if(base == n)
|
||||||
|
{
|
||||||
|
return offset - 1;
|
||||||
|
}
|
||||||
|
base += (bitset->w[i] >> j) & 0x01;
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_tracker = tracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitsetItr bitset_itr_create(const Bitset* bitset)
|
||||||
|
{
|
||||||
|
BitsetItr itr =
|
||||||
|
{
|
||||||
|
.bitset = bitset,
|
||||||
|
.word = 0,
|
||||||
|
.bit = 0,
|
||||||
|
.itr = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitset_itr_next(BitsetItr* itr)
|
||||||
|
{
|
||||||
|
// So, worst case scenario for this is one bit at the end of the last
|
||||||
|
// word in the bitset. You would look (32 * 7) + 31 times!
|
||||||
|
// This can be sped up with by checking if the word is empty first.
|
||||||
|
// Then the worst enemy of this method would be something like a set bit at the end
|
||||||
|
// of every word. In that case you would need to loop 31 times maximum.
|
||||||
|
// So one last thing you could do is something like `bitset_allocate_idx` does with the
|
||||||
|
// __builtin_ctz function as well.
|
||||||
|
//
|
||||||
|
// The point being, this can be very slow, but it's simple and can be much faster.
|
||||||
|
for (; itr->word < itr->bitset->nwords; itr->word++)
|
||||||
|
{
|
||||||
|
for (; itr->bit < itr->bitset->nbits; itr->bit++)
|
||||||
|
{
|
||||||
|
itr->itr++;
|
||||||
|
if(itr->bitset->w[itr->word] & (1 << itr->bit))
|
||||||
|
{
|
||||||
|
// if itr->bit == nbits on the next run, the for loop will handle it
|
||||||
|
itr->bit++;
|
||||||
|
// above we always make it one more than it is
|
||||||
|
// it's so we can return without mutating the actual iterator
|
||||||
|
// once it gets here. Just subtract one
|
||||||
|
return itr->itr - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itr->bit = 0;
|
||||||
|
}
|
||||||
|
itr->word = 0;
|
||||||
|
|
||||||
|
return UNDEFINED;
|
||||||
|
}
|
||||||
+180
-125
@@ -1,9 +1,11 @@
|
|||||||
#include "game.h"
|
#include "game.h"
|
||||||
|
|
||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <tonc.h>
|
#include <tonc.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "bitset.h"
|
||||||
#include "tonc_memdef.h"
|
#include "tonc_memdef.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
@@ -221,17 +223,18 @@ static int hand_selections = 0;
|
|||||||
static int scored_card_index = 0;
|
static int scored_card_index = 0;
|
||||||
|
|
||||||
// Keeping track of what Jokers are scored at each step
|
// Keeping track of what Jokers are scored at each step
|
||||||
static int joker_scored_index = 0;
|
static ListItr _joker_scored_itr;
|
||||||
static int joker_round_end_index = 0;
|
|
||||||
|
|
||||||
static int selection_x = 0;
|
static int selection_x = 0;
|
||||||
static int selection_y = 0;
|
static int selection_y = 0;
|
||||||
|
|
||||||
static bool sort_by_suit = false;
|
static bool sort_by_suit = false;
|
||||||
|
|
||||||
static List *jokers = NULL;
|
static List _owned_jokers_list;
|
||||||
static List *discarded_jokers = NULL;
|
static List _discarded_jokers_list;
|
||||||
static List *jokers_available_to_shop; // List of joker IDs
|
|
||||||
|
BITSET_DEFINE(_avail_jokers_bitset, MAX_DEFINABLE_JOKERS)
|
||||||
|
static List _shop_jokers_list;
|
||||||
|
|
||||||
// Stacks
|
// Stacks
|
||||||
static CardObject *played[MAX_SELECTION_SIZE] = {NULL};
|
static CardObject *played[MAX_SELECTION_SIZE] = {NULL};
|
||||||
@@ -263,6 +266,37 @@ int get_straight_and_flush_size(void)
|
|||||||
return straight_and_flush_size;
|
return straight_and_flush_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void _set_shop_joker_avail(int joker_id, bool avail)
|
||||||
|
{
|
||||||
|
bitset_set_idx(&_avail_jokers_bitset, joker_id, avail);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((unused))
|
||||||
|
static inline bool _get_shop_joker_avail(int joker_id)
|
||||||
|
{
|
||||||
|
return bitset_get_idx(&_avail_jokers_bitset, joker_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int _get_num_shop_jokers_avail(void)
|
||||||
|
{
|
||||||
|
return bitset_num_set_bits(&_avail_jokers_bitset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _reset_shop_jokers(void)
|
||||||
|
{
|
||||||
|
int num_jokers = get_joker_registry_size();
|
||||||
|
bitset_clear(&_avail_jokers_bitset);
|
||||||
|
for(int i = 0; i < num_jokers; i++)
|
||||||
|
{
|
||||||
|
bitset_set_idx(&_avail_jokers_bitset, i, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _no_avail_jokers(void)
|
||||||
|
{
|
||||||
|
return bitset_is_empty(&_avail_jokers_bitset);
|
||||||
|
}
|
||||||
|
|
||||||
// Played stack
|
// Played stack
|
||||||
static inline void played_push(CardObject *card_object)
|
static inline void played_push(CardObject *card_object)
|
||||||
{
|
{
|
||||||
@@ -347,16 +381,18 @@ int get_scored_card_index(void)
|
|||||||
return scored_card_index;
|
return scored_card_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
List *get_jokers(void)
|
List* get_jokers_list(void)
|
||||||
{
|
{
|
||||||
return jokers;
|
return &_owned_jokers_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_joker_owned(int joker_id)
|
bool is_joker_owned(int joker_id)
|
||||||
{
|
{
|
||||||
for (int k = 0; k < list_get_size(jokers); k++)
|
ListItr itr = list_itr_create(&_owned_jokers_list);
|
||||||
|
JokerObject* joker;
|
||||||
|
|
||||||
|
while((joker = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker = list_get(jokers, k);
|
|
||||||
if (joker->joker->id == joker_id)
|
if (joker->joker->id == joker_id)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@@ -367,7 +403,7 @@ bool is_joker_owned(int joker_id)
|
|||||||
|
|
||||||
void add_joker(JokerObject *joker_object)
|
void add_joker(JokerObject *joker_object)
|
||||||
{
|
{
|
||||||
list_append(jokers, joker_object);
|
list_push_back(&_owned_jokers_list, joker_object);
|
||||||
|
|
||||||
// TODO: Extract to on_joker_added() callback
|
// TODO: Extract to on_joker_added() callback
|
||||||
// In case the player gets multiple Four Fingers Jokers,
|
// In case the player gets multiple Four Fingers Jokers,
|
||||||
@@ -387,10 +423,10 @@ void add_joker(JokerObject *joker_object)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_held_joker(int joker_idx)
|
void remove_owned_joker(int owned_joker_idx)
|
||||||
{
|
{
|
||||||
// TODO: Extract to on_joker_removed() callback
|
// TODO: Extract to on_joker_removed() callback
|
||||||
JokerObject* joker_object = list_get(jokers, joker_idx);
|
JokerObject* joker_object = list_get_at_idx(&_owned_jokers_list, owned_joker_idx);
|
||||||
// In case the player gets multiple Four Fingers Jokers,
|
// In case the player gets multiple Four Fingers Jokers,
|
||||||
// and only reset the size when all of them have been removed
|
// and only reset the size when all of them have been removed
|
||||||
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
|
if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
|
||||||
@@ -407,7 +443,8 @@ void remove_held_joker(int joker_idx)
|
|||||||
shortcut_joker_count--;
|
shortcut_joker_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_remove_by_idx(jokers, joker_idx);
|
_set_shop_joker_avail(joker_object->joker->id, true);
|
||||||
|
list_remove_at_idx(&_owned_jokers_list, owned_joker_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_deck_top(void)
|
int get_deck_top(void)
|
||||||
@@ -1421,26 +1458,20 @@ void game_change_state(enum GameState new_game_state)
|
|||||||
|
|
||||||
void jokers_available_to_shop_init()
|
void jokers_available_to_shop_init()
|
||||||
{
|
{
|
||||||
int num_defined_jokers = get_joker_registry_size();
|
_reset_shop_jokers();
|
||||||
jokers_available_to_shop = list_new(num_defined_jokers);
|
|
||||||
for (intptr_t i = 0; i < num_defined_jokers; i++)
|
|
||||||
{
|
|
||||||
// Add all joker IDs sequentially
|
|
||||||
int_list_append(jokers_available_to_shop, i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_init()
|
void game_init()
|
||||||
{
|
{
|
||||||
|
// Initialize all jokers list once
|
||||||
|
_owned_jokers_list = list_create();
|
||||||
|
_discarded_jokers_list = list_create();
|
||||||
|
_shop_jokers_list = list_create();
|
||||||
|
// TODO: Move this to an initialization of the play scoring states
|
||||||
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
|
|
||||||
jokers_available_to_shop_init();
|
jokers_available_to_shop_init();
|
||||||
|
|
||||||
// Initialize jokers list
|
|
||||||
if (jokers) list_destroy(&jokers);
|
|
||||||
jokers = list_new(MAX_JOKERS_HELD_SIZE);
|
|
||||||
|
|
||||||
if (discarded_jokers != NULL) list_destroy(&discarded_jokers);
|
|
||||||
discarded_jokers = list_new(MAX_JOKERS_HELD_SIZE);
|
|
||||||
|
|
||||||
hands = max_hands;
|
hands = max_hands;
|
||||||
discards = max_discards;
|
discards = max_discards;
|
||||||
timer = TM_ZERO;
|
timer = TM_ZERO;
|
||||||
@@ -1465,6 +1496,7 @@ void game_init()
|
|||||||
void game_start()
|
void game_start()
|
||||||
{
|
{
|
||||||
set_seed(rng_seed);
|
set_seed(rng_seed);
|
||||||
|
//set_seed(9); // 9 is a full house
|
||||||
|
|
||||||
affine_background_change_background(AFFINE_BG_GAME);
|
affine_background_change_background(AFFINE_BG_GAME);
|
||||||
|
|
||||||
@@ -2149,12 +2181,12 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns true if a joker was scored, false otherwise
|
// returns true if a joker was scored, false otherwise
|
||||||
static bool check_and_score_joker_for_event(int* iteration_start, Card* played_card, enum JokerEvent joker_event)
|
static bool check_and_score_joker_for_event(ListItr* starting_joker_itr, Card* played_card, enum JokerEvent joker_event)
|
||||||
{
|
{
|
||||||
for (int k = *iteration_start; k < list_get_size(jokers); k++)
|
JokerObject* joker;
|
||||||
|
|
||||||
|
while((joker = list_itr_next(starting_joker_itr)))
|
||||||
{
|
{
|
||||||
(*iteration_start)++;
|
|
||||||
JokerObject *joker = list_get(jokers, k);
|
|
||||||
if (joker_object_score(joker, played_card, joker_event, &chips, &mult, &money, &retrigger))
|
if (joker_object_score(joker, played_card, joker_event, &chips, &mult, &money, &retrigger))
|
||||||
{
|
{
|
||||||
display_chips();
|
display_chips();
|
||||||
@@ -2225,7 +2257,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
retrigger = false;
|
retrigger = false;
|
||||||
scored_card_index--;
|
scored_card_index--;
|
||||||
(*played_selections)--;
|
(*played_selections)--;
|
||||||
joker_scored_index = 0;
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// So pretend "played_selections" is now called "scored_card_index" and it counts the number of cards that have been scored
|
// So pretend "played_selections" is now called "scored_card_index" and it counts the number of cards that have been scored
|
||||||
@@ -2236,15 +2268,15 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
// Trigger all Jokers after each card scored
|
// Trigger all Jokers after each card scored
|
||||||
if (*played_selections > 0)
|
if (*played_selections > 0)
|
||||||
{
|
{
|
||||||
if (check_and_score_joker_for_event(&joker_scored_index, played[*played_selections - 1]->card, JOKER_EVENT_ON_CARD_SCORED))
|
if (check_and_score_joker_for_event(&_joker_scored_itr, played[*played_selections - 1]->card, JOKER_EVENT_ON_CARD_SCORED))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger all Jokers that have an effect when a card finishes scoring
|
// Trigger all Jokers that have an effect when a card finishes scoring
|
||||||
// (e.g. retriggers) after activating all the other scored_card Jokers normally
|
// (e.g. retriggers) after activating all the other scored_card Jokers normally
|
||||||
joker_scored_index = 0;
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
if (check_and_score_joker_for_event(&joker_scored_index, played[*played_selections - 1]->card, JOKER_EVENT_ON_CARD_SCORED_END))
|
if (check_and_score_joker_for_event(&_joker_scored_itr, played[*played_selections - 1]->card, JOKER_EVENT_ON_CARD_SCORED_END))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2272,7 +2304,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
display_chips();
|
display_chips();
|
||||||
|
|
||||||
// Allow Joker scoring
|
// Allow Joker scoring
|
||||||
joker_scored_index = 0;
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2280,7 +2312,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
|
|
||||||
// advance state after going past the last card (exited the loop without returning)
|
// advance state after going past the last card (exited the loop without returning)
|
||||||
play_state = PLAY_SCORING_JOKERS;
|
play_state = PLAY_SCORING_JOKERS;
|
||||||
joker_scored_index = 0;
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
scored_card_index = 0; // reuse this variable for held cards
|
scored_card_index = 0; // reuse this variable for held cards
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2299,13 +2331,14 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
|
|
||||||
tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT);
|
tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT);
|
||||||
|
|
||||||
if (check_and_score_joker_for_event(&joker_scored_index, NULL, JOKER_EVENT_INDEPENDENT))
|
if (check_and_score_joker_for_event(&_joker_scored_itr, NULL, JOKER_EVENT_INDEPENDENT))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger hand end effect for all jokers once they are done scoring
|
// Trigger hand end effect for all jokers once they are done scoring
|
||||||
if (check_and_score_joker_for_event(&joker_round_end_index, NULL, JOKER_EVENT_ON_HAND_SCORED_END))
|
ListItr round_end_itr = list_itr_create(&_owned_jokers_list);
|
||||||
|
if (check_and_score_joker_for_event(&round_end_itr, NULL, JOKER_EVENT_ON_HAND_SCORED_END))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2380,8 +2413,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
*played_selections = 0;
|
*played_selections = 0;
|
||||||
played_top = -1; // Reset the played stack
|
played_top = -1; // Reset the played stack
|
||||||
scored_card_index = 0;
|
scored_card_index = 0;
|
||||||
joker_scored_index = 0;
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
joker_round_end_index = 0;
|
|
||||||
timer = TM_ZERO;
|
timer = TM_ZERO;
|
||||||
break; // Break out of the loop to avoid accessing an invalid index
|
break; // Break out of the loop to avoid accessing an invalid index
|
||||||
}
|
}
|
||||||
@@ -2795,7 +2827,6 @@ static void game_round_end_dismiss_round_end_panel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shop
|
// Shop
|
||||||
static List *shop_jokers = NULL;
|
|
||||||
#define REROLL_BASE_COST 5 // Base cost for rerolling the shop items
|
#define REROLL_BASE_COST 5 // Base cost for rerolling the shop items
|
||||||
static int reroll_cost = REROLL_BASE_COST;
|
static int reroll_cost = REROLL_BASE_COST;
|
||||||
|
|
||||||
@@ -2823,78 +2854,76 @@ void erase_price_under_sprite_object(SpriteObject *sprite_object)
|
|||||||
tte_erase_rect_wrapper(price_rect);
|
tte_erase_rect_wrapper(price_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int game_shop_get_random_joker_idx()
|
static int game_shop_get_rand_available_joker_id(void)
|
||||||
{
|
{
|
||||||
// Roll for what rarity the joker will be
|
// Roll for what rarity the joker will be
|
||||||
int joker_rarity = joker_get_random_rarity();
|
int joker_rarity = joker_get_random_rarity();
|
||||||
|
|
||||||
// Now determine how many jokers are available based on the rarity
|
// Now determine how many jokers are available based on the rarity
|
||||||
int jokers_avail_size = list_get_size(jokers_available_to_shop);
|
int jokers_avail_size = _get_num_shop_jokers_avail();
|
||||||
int matching_indices[jokers_avail_size];
|
|
||||||
|
if(jokers_avail_size == 0) return UNDEFINED;
|
||||||
|
|
||||||
|
int matching_joker_ids[jokers_avail_size];
|
||||||
|
int fallback_random_idx = random() % jokers_avail_size;
|
||||||
|
int fallback_random_joker_id = UNDEFINED;
|
||||||
int match_count = 0;
|
int match_count = 0;
|
||||||
|
|
||||||
for (int i = 0; i < jokers_avail_size; i++)
|
BitsetItr itr = bitset_itr_create(&_avail_jokers_bitset);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int joker_id = UNDEFINED;
|
||||||
|
while((joker_id = bitset_itr_next(&itr)) != UNDEFINED)
|
||||||
{
|
{
|
||||||
intptr_t joker_id = int_list_get(jokers_available_to_shop, i);
|
if(i++ == fallback_random_idx) fallback_random_joker_id = joker_id;
|
||||||
const JokerInfo *info = get_joker_registry_entry(joker_id);
|
const JokerInfo *info = get_joker_registry_entry(joker_id);
|
||||||
if (info->rarity == joker_rarity)
|
if (info->rarity == joker_rarity)
|
||||||
{
|
{
|
||||||
matching_indices[match_count] = i;
|
matching_joker_ids[match_count++] = joker_id;
|
||||||
match_count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int selected_joker_idx = 0;
|
int selected_joker_id = (match_count > 0) ?
|
||||||
if (match_count > 0)
|
matching_joker_ids[random() % match_count] :
|
||||||
{
|
fallback_random_joker_id;
|
||||||
// If we counted at least one joker with matching rarity, pick one of them randomly
|
|
||||||
selected_joker_idx = matching_indices[random() % match_count];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Didn't find any jokers of matching rarity, just pick one at random instead
|
|
||||||
selected_joker_idx = random() % jokers_avail_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return selected_joker_idx;
|
return selected_joker_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void game_shop_create_items()
|
static void game_shop_create_items()
|
||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT);
|
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT);
|
||||||
if (list_get_size(jokers_available_to_shop) == 0)
|
|
||||||
{
|
|
||||||
// No jokers to create
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
shop_jokers = list_new(MAX_SHOP_JOKERS);
|
if (_no_avail_jokers()) return;
|
||||||
|
|
||||||
|
list_clear(&_shop_jokers_list);
|
||||||
|
_shop_jokers_list = list_create();
|
||||||
|
|
||||||
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
|
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
|
||||||
{
|
{
|
||||||
intptr_t joker_id = 0;
|
int joker_id = 0;
|
||||||
#ifdef TEST_JOKER_ID0 // Allow defining an ID for a joker to always appear in shop and be tested
|
#ifdef TEST_JOKER_ID0 // Allow defining an ID for a joker to always appear in shop and be tested
|
||||||
if (int_list_exists(jokers_available_to_shop, TEST_JOKER_ID0))
|
if (_get_shop_joker_avail(TEST_JOKER_ID0))
|
||||||
{
|
{
|
||||||
joker_id = TEST_JOKER_ID0;
|
joker_id = TEST_JOKER_ID0;
|
||||||
int_list_remove_by_value(jokers_available_to_shop, joker_id);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
#ifdef TEST_JOKER_ID1
|
#ifdef TEST_JOKER_ID1
|
||||||
if (int_list_exists(jokers_available_to_shop, TEST_JOKER_ID1))
|
if (_get_shop_joker_avail(TEST_JOKER_ID1))
|
||||||
{
|
{
|
||||||
joker_id = TEST_JOKER_ID1;
|
joker_id = TEST_JOKER_ID1;
|
||||||
int_list_remove_by_value(jokers_available_to_shop, joker_id);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
int joker_idx = game_shop_get_random_joker_idx();
|
joker_id = game_shop_get_rand_available_joker_id();
|
||||||
joker_id = int_list_get(jokers_available_to_shop, joker_idx);
|
|
||||||
list_remove_by_idx(jokers_available_to_shop, joker_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If for some reason only no joker is left, don't make another
|
||||||
|
if(joker_id == UNDEFINED) break;
|
||||||
|
|
||||||
|
_set_shop_joker_avail(joker_id, false);
|
||||||
|
|
||||||
JokerObject *joker_object = joker_object_new(joker_new(joker_id));
|
JokerObject *joker_object = joker_object_new(joker_new(joker_id));
|
||||||
|
|
||||||
@@ -2906,7 +2935,8 @@ static void game_shop_create_items()
|
|||||||
print_price_under_sprite_object(joker_object->sprite_object, joker_object->joker->value);
|
print_price_under_sprite_object(joker_object->sprite_object, joker_object->joker->value);
|
||||||
|
|
||||||
sprite_position(joker_object_get_sprite(joker_object), fx2int(joker_object->sprite_object->x), fx2int(joker_object->sprite_object->y));
|
sprite_position(joker_object_get_sprite(joker_object), fx2int(joker_object->sprite_object->x), fx2int(joker_object->sprite_object->y));
|
||||||
list_append(shop_jokers, joker_object);
|
|
||||||
|
list_push_back(&_shop_jokers_list, joker_object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2948,23 +2978,28 @@ static void game_shop_reroll(int *reroll_cost)
|
|||||||
{
|
{
|
||||||
money -= *reroll_cost;
|
money -= *reroll_cost;
|
||||||
display_money(money); // Update the money display
|
display_money(money); // Update the money display
|
||||||
for (int i = 0; i < list_get_size(shop_jokers); i++)
|
|
||||||
|
ListItr itr = list_itr_create(&_shop_jokers_list);
|
||||||
|
JokerObject* joker_object;
|
||||||
|
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker_object = list_get(shop_jokers, i);
|
|
||||||
if (joker_object != NULL)
|
if (joker_object != NULL)
|
||||||
{
|
{
|
||||||
int_list_append(jokers_available_to_shop, joker_object->joker->id);
|
_set_shop_joker_avail(joker_object->joker->id, true);
|
||||||
joker_object_destroy(&joker_object); // Destroy the joker object if it exists
|
joker_object_destroy(&joker_object); // Destroy the joker object if it exists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list_destroy(&shop_jokers);
|
list_clear(&_shop_jokers_list);
|
||||||
|
_shop_jokers_list = list_create();
|
||||||
|
|
||||||
game_shop_create_items();
|
game_shop_create_items();
|
||||||
|
|
||||||
for (int i = 0; i < list_get_size(shop_jokers); i++)
|
itr = list_itr_create(&_shop_jokers_list);
|
||||||
|
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker_object = list_get(shop_jokers, i);
|
|
||||||
if (joker_object != NULL)
|
if (joker_object != NULL)
|
||||||
{
|
{
|
||||||
joker_object->sprite_object->y = joker_object->sprite_object->ty; // Set the y position to the target position
|
joker_object->sprite_object->y = joker_object->sprite_object->ty; // Set the y position to the target position
|
||||||
@@ -2978,7 +3013,7 @@ static void game_shop_reroll(int *reroll_cost)
|
|||||||
|
|
||||||
static int jokers_sel_row_get_size()
|
static int jokers_sel_row_get_size()
|
||||||
{
|
{
|
||||||
return list_get_size(jokers);
|
return list_get_len(&_owned_jokers_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid,
|
static void jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid,
|
||||||
@@ -2988,16 +3023,22 @@ static void jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid,
|
|||||||
{
|
{
|
||||||
if (prev_selection->y == row_idx)
|
if (prev_selection->y == row_idx)
|
||||||
{
|
{
|
||||||
JokerObject* joker_object = list_get(jokers, prev_selection->x);
|
JokerObject* joker_object = (JokerObject*)list_get_at_idx(&_owned_jokers_list, prev_selection->x);
|
||||||
erase_price_under_sprite_object(joker_object->sprite_object);
|
if(joker_object != NULL)
|
||||||
sprite_object_set_focus(joker_object->sprite_object, false);
|
{
|
||||||
|
erase_price_under_sprite_object(joker_object->sprite_object);
|
||||||
|
sprite_object_set_focus(joker_object->sprite_object, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_selection->y == row_idx)
|
if (new_selection->y == row_idx)
|
||||||
{
|
{
|
||||||
JokerObject* joker_object = list_get(jokers, new_selection->x);
|
JokerObject* joker_object = (JokerObject*)list_get_at_idx(&_owned_jokers_list, new_selection->x);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, true);
|
if(joker_object != NULL)
|
||||||
print_price_under_sprite_object(joker_object->sprite_object, joker_get_sell_value(joker_object->joker));
|
{
|
||||||
|
sprite_object_set_focus(joker_object->sprite_object, true);
|
||||||
|
print_price_under_sprite_object(joker_object->sprite_object, joker_get_sell_value(joker_object->joker));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3005,21 +3046,20 @@ void joker_start_discard_animation(JokerObject *joker_object)
|
|||||||
{
|
{
|
||||||
joker_object->sprite_object->tx = int2fx(JOKER_DISCARD_TARGET.x);
|
joker_object->sprite_object->tx = int2fx(JOKER_DISCARD_TARGET.x);
|
||||||
joker_object->sprite_object->ty = int2fx(JOKER_DISCARD_TARGET.y);
|
joker_object->sprite_object->ty = int2fx(JOKER_DISCARD_TARGET.y);
|
||||||
list_append(discarded_jokers, joker_object);
|
list_push_back(&_discarded_jokers_list, joker_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_sell_joker(int joker_idx)
|
void game_sell_joker(int joker_idx)
|
||||||
{
|
{
|
||||||
if (joker_idx < 0 || joker_idx > list_get_size(jokers))
|
if (joker_idx < 0 || joker_idx >= list_get_len(&_owned_jokers_list))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JokerObject *joker_object = list_get(jokers, joker_idx);
|
JokerObject* joker_object = (JokerObject*)list_get_at_idx(&_owned_jokers_list, joker_idx);
|
||||||
money += joker_get_sell_value(joker_object->joker);
|
money += joker_get_sell_value(joker_object->joker);
|
||||||
display_money(money);
|
display_money(money);
|
||||||
erase_price_under_sprite_object(joker_object->sprite_object);
|
erase_price_under_sprite_object(joker_object->sprite_object);
|
||||||
|
|
||||||
remove_held_joker(joker_idx);
|
remove_owned_joker(joker_idx);
|
||||||
int_list_append(jokers_available_to_shop, (intptr_t)joker_object->joker->id);
|
|
||||||
|
|
||||||
joker_start_discard_animation(joker_object);
|
joker_start_discard_animation(joker_object);
|
||||||
}
|
}
|
||||||
@@ -3037,7 +3077,7 @@ static void jokers_sel_row_on_key_hit(SelectionGrid* selection_grid, Selection*
|
|||||||
// Shop input
|
// Shop input
|
||||||
static int shop_top_row_get_size()
|
static int shop_top_row_get_size()
|
||||||
{
|
{
|
||||||
return list_get_size(shop_jokers) + 1; // + 1 to account for next round button
|
return list_get_len(&_shop_jokers_list) + 1; // + 1 to account for next round button
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_to_held_jokers(JokerObject *joker_object)
|
static void add_to_held_jokers(JokerObject *joker_object)
|
||||||
@@ -3048,14 +3088,14 @@ static void add_to_held_jokers(JokerObject *joker_object)
|
|||||||
|
|
||||||
static void game_shop_buy_joker(int shop_joker_idx)
|
static void game_shop_buy_joker(int shop_joker_idx)
|
||||||
{
|
{
|
||||||
JokerObject *joker_object = list_get(shop_jokers, shop_joker_idx);
|
JokerObject *joker_object = (JokerObject*)list_get_at_idx(&_shop_jokers_list, shop_joker_idx);
|
||||||
|
|
||||||
money -= joker_object->joker->value; // Deduct the money spent on the joker
|
money -= joker_object->joker->value; // Deduct the money spent on the joker
|
||||||
display_money(money); // Update the money display
|
display_money(money); // Update the money display
|
||||||
erase_price_under_sprite_object(joker_object->sprite_object);
|
erase_price_under_sprite_object(joker_object->sprite_object);
|
||||||
sprite_object_set_focus(joker_object->sprite_object, false);
|
sprite_object_set_focus(joker_object->sprite_object, false);
|
||||||
add_to_held_jokers(joker_object);
|
add_to_held_jokers(joker_object);
|
||||||
list_remove_by_idx(shop_jokers, shop_joker_idx); // Remove the joker from the shop
|
list_remove_at_idx(&_shop_jokers_list, shop_joker_idx); // Remove the joker from the shop
|
||||||
}
|
}
|
||||||
|
|
||||||
static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection)
|
static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection)
|
||||||
@@ -3080,9 +3120,9 @@ static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* se
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int shop_joker_idx = selection->x - 1; // - 1 to account for next round button
|
int shop_joker_idx = selection->x - 1; // - 1 to account for next round button
|
||||||
JokerObject *joker_object = list_get(shop_jokers, shop_joker_idx);
|
JokerObject *joker_object = (JokerObject*)list_get_at_idx(&_shop_jokers_list, shop_joker_idx);
|
||||||
if (joker_object == NULL
|
if (joker_object == NULL
|
||||||
|| list_get_size(jokers) >= MAX_JOKERS_HELD_SIZE
|
|| list_get_len(&_owned_jokers_list) >= MAX_JOKERS_HELD_SIZE
|
||||||
|| money < joker_object->joker->value)
|
|| money < joker_object->joker->value)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -3109,8 +3149,9 @@ static void shop_top_row_on_selection_changed(SelectionGrid* selection_grid, int
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JokerObject *joker = list_get(shop_jokers, prev_selection->x - 1);
|
int idx = prev_selection->x - 1; // -1 to account for next round button
|
||||||
sprite_object_set_focus(joker->sprite_object, false);
|
JokerObject *joker_object = (JokerObject*)list_get_at_idx(&_shop_jokers_list, idx);
|
||||||
|
sprite_object_set_focus(joker_object->sprite_object, false);
|
||||||
// -1 to account for next round button
|
// -1 to account for next round button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3124,9 +3165,9 @@ static void shop_top_row_on_selection_changed(SelectionGrid* selection_grid, int
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JokerObject *joker = list_get(shop_jokers, new_selection->x - 1);
|
int idx = new_selection->x - 1; // -1 to account for next round button
|
||||||
sprite_object_set_focus(joker->sprite_object, true);
|
JokerObject *joker_object = (JokerObject*)list_get_at_idx(&_shop_jokers_list, idx);
|
||||||
// -1 to account for next round button
|
sprite_object_set_focus(joker_object->sprite_object, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3223,9 +3264,10 @@ static void game_shop_outro()
|
|||||||
{
|
{
|
||||||
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text
|
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text
|
||||||
|
|
||||||
for (int i = 0; i < list_get_size(shop_jokers); i++)
|
ListItr itr = list_itr_create(&_shop_jokers_list);
|
||||||
|
JokerObject* joker_object;
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker_object = list_get(shop_jokers, i);
|
|
||||||
if (joker_object != NULL)
|
if (joker_object != NULL)
|
||||||
{
|
{
|
||||||
joker_object->sprite_object->ty = int2fx(160);
|
joker_object->sprite_object->ty = int2fx(160);
|
||||||
@@ -3253,11 +3295,12 @@ static void game_shop_on_update()
|
|||||||
{
|
{
|
||||||
change_background(BG_ID_SHOP);
|
change_background(BG_ID_SHOP);
|
||||||
|
|
||||||
if (shop_jokers != NULL)
|
if (!list_is_empty(&_shop_jokers_list))
|
||||||
{
|
{
|
||||||
for (int i = 0; i < list_get_size(shop_jokers); i++)
|
ListItr itr = list_itr_create(&_shop_jokers_list);
|
||||||
|
JokerObject* joker_object;
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker_object = list_get(shop_jokers, i);
|
|
||||||
if (joker_object != NULL)
|
if (joker_object != NULL)
|
||||||
{
|
{
|
||||||
joker_object_update(joker_object);
|
joker_object_update(joker_object);
|
||||||
@@ -3283,18 +3326,20 @@ static void game_shop_on_update()
|
|||||||
|
|
||||||
static void game_shop_on_exit()
|
static void game_shop_on_exit()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < list_get_size(shop_jokers); i++)
|
ListItr itr = list_itr_create(&_shop_jokers_list);
|
||||||
|
JokerObject* joker_object;
|
||||||
|
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject* joker_object = list_get(shop_jokers, i);
|
|
||||||
if (joker_object != NULL)
|
if (joker_object != NULL)
|
||||||
{
|
{
|
||||||
// Make the joker available back to shop
|
// Make the joker available back to shop
|
||||||
int_list_append(jokers_available_to_shop, (intptr_t)joker_object->joker->id);
|
_set_shop_joker_avail(joker_object->joker->id, true);
|
||||||
}
|
}
|
||||||
joker_object_destroy(&joker_object); // Destroy the joker objects
|
joker_object_destroy(&joker_object); // Destroy the joker objects
|
||||||
}
|
}
|
||||||
|
|
||||||
list_destroy(&shop_jokers);
|
list_clear(&_shop_jokers_list);
|
||||||
|
|
||||||
increment_blind(BLIND_STATE_DEFEATED); // TODO: Move to game_round_end()?
|
increment_blind(BLIND_STATE_DEFEATED); // TODO: Move to game_round_end()?
|
||||||
}
|
}
|
||||||
@@ -3509,20 +3554,23 @@ static void game_main_menu_on_update()
|
|||||||
|
|
||||||
static void discarded_jokers_update_loop()
|
static void discarded_jokers_update_loop()
|
||||||
{
|
{
|
||||||
if (discarded_jokers == NULL)
|
if(list_is_empty(&_discarded_jokers_list)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Iterating backwards because of removal within loop
|
ListItr itr = list_itr_create(&_discarded_jokers_list);
|
||||||
for (int i = list_get_size(discarded_jokers) - 1; i >= 0; i--)
|
JokerObject* joker_object;
|
||||||
|
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject* joker_object = list_get(discarded_jokers, i);
|
|
||||||
joker_object_update(joker_object);
|
joker_object_update(joker_object);
|
||||||
if (joker_object->sprite_object->x == joker_object->sprite_object->tx
|
if (joker_object->sprite_object->x == joker_object->sprite_object->tx
|
||||||
&& joker_object->sprite_object->y == joker_object->sprite_object->ty)
|
&& joker_object->sprite_object->y == joker_object->sprite_object->ty)
|
||||||
{
|
{
|
||||||
list_remove_by_idx(discarded_jokers, i);
|
list_itr_remove_current_node(&itr);
|
||||||
joker_object_destroy(&joker_object);
|
joker_object_destroy(&joker_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3539,11 +3587,13 @@ static void held_jokers_update_loop()
|
|||||||
|
|
||||||
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
|
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
|
||||||
|
|
||||||
int jokers_top = list_get_size(jokers) - 1;
|
ListItr itr = list_itr_create(&_owned_jokers_list);
|
||||||
for (int i = jokers_top; i >= 0; i--)
|
JokerObject* joker;
|
||||||
|
int jokers_top = list_get_len(&_owned_jokers_list) - 1;
|
||||||
|
int i = 0;
|
||||||
|
while((joker = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker = list_get(jokers, i);
|
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i++]);
|
||||||
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]);
|
|
||||||
|
|
||||||
joker_object_update(joker);
|
joker_object_update(joker);
|
||||||
}
|
}
|
||||||
@@ -3579,9 +3629,11 @@ static void game_lose_on_update()
|
|||||||
// util we decide what we want to do after a game over.
|
// util we decide what we want to do after a game over.
|
||||||
static void game_over_on_exit()
|
static void game_over_on_exit()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < list_get_size(jokers); i ++)
|
ListItr itr = list_itr_create(&_owned_jokers_list);
|
||||||
|
JokerObject* joker_object;
|
||||||
|
|
||||||
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject *joker_object = list_get(jokers, i);
|
|
||||||
joker_object_destroy(&joker_object);
|
joker_object_destroy(&joker_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3595,7 +3647,10 @@ static void game_over_on_exit()
|
|||||||
sprite_destroy(&blind_select_tokens[BLIND_TYPE_SMALL]);
|
sprite_destroy(&blind_select_tokens[BLIND_TYPE_SMALL]);
|
||||||
sprite_destroy(&blind_select_tokens[BLIND_TYPE_BIG]);
|
sprite_destroy(&blind_select_tokens[BLIND_TYPE_BIG]);
|
||||||
sprite_destroy(&blind_select_tokens[BLIND_TYPE_BOSS]);
|
sprite_destroy(&blind_select_tokens[BLIND_TYPE_BOSS]);
|
||||||
list_destroy(&jokers_available_to_shop);
|
|
||||||
|
list_clear(&_owned_jokers_list);
|
||||||
|
list_clear(&_discarded_jokers_list);
|
||||||
|
list_clear(&_shop_jokers_list);
|
||||||
|
|
||||||
game_init();
|
game_init();
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
#define JOKER_SCORE_TEXT_Y 48
|
#define JOKER_SCORE_TEXT_Y 48
|
||||||
#define NUM_JOKERS_PER_SPRITESHEET 2
|
#define NUM_JOKERS_PER_SPRITESHEET 2
|
||||||
#define MAX_DEFINABLE_JOKERS 150
|
|
||||||
|
|
||||||
static const unsigned int *joker_gfxTiles[] =
|
static const unsigned int *joker_gfxTiles[] =
|
||||||
{
|
{
|
||||||
|
|||||||
+16
-17
@@ -3,6 +3,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "hand_analysis.h"
|
#include "hand_analysis.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
#include "pool.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
@@ -266,22 +267,20 @@ static JokerEffect joker_stencil_effect(Joker *joker, Card *scored_card, enum Jo
|
|||||||
|
|
||||||
SCORE_ON_EVENT_ONLY(JOKER_EVENT_INDEPENDENT, joker_event, effect)
|
SCORE_ON_EVENT_ONLY(JOKER_EVENT_INDEPENDENT, joker_event, effect)
|
||||||
|
|
||||||
List* jokers = get_jokers();
|
List* jokers = get_jokers_list();
|
||||||
|
|
||||||
// +1 xmult per empty joker slot...
|
// +1 xmult per empty joker slot...
|
||||||
int num_jokers = list_get_size(jokers);
|
int num_jokers = list_get_len(jokers);
|
||||||
|
|
||||||
effect.xmult = (MAX_JOKERS_HELD_SIZE) - num_jokers;
|
effect.xmult = (MAX_JOKERS_HELD_SIZE) - num_jokers;
|
||||||
|
|
||||||
// ...and also each stencil_joker adds +1 xmult
|
// ...and also each stencil_joker adds +1 xmult
|
||||||
|
ListItr itr = list_itr_create(jokers);
|
||||||
|
JokerObject* joker_object;
|
||||||
|
|
||||||
for (int i = 0; i < num_jokers; i++ )
|
while((joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject* joker_object = list_get(jokers, i);
|
if (joker_object->joker->id == JOKER_STENCIL_ID) effect.xmult++;
|
||||||
if (joker_object->joker->id == JOKER_STENCIL_ID)
|
|
||||||
{
|
|
||||||
effect.xmult++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return effect;
|
return effect;
|
||||||
@@ -491,7 +490,7 @@ static JokerEffect abstract_joker_effect(Joker *joker, Card *scored_card, enum J
|
|||||||
SCORE_ON_EVENT_ONLY(JOKER_EVENT_INDEPENDENT, joker_event, effect)
|
SCORE_ON_EVENT_ONLY(JOKER_EVENT_INDEPENDENT, joker_event, effect)
|
||||||
|
|
||||||
// +1 xmult per occupied joker slot
|
// +1 xmult per occupied joker slot
|
||||||
int num_jokers = list_get_size(get_jokers());
|
int num_jokers = list_get_len(get_jokers_list());
|
||||||
effect.mult = num_jokers * 3;
|
effect.mult = num_jokers * 3;
|
||||||
|
|
||||||
return effect;
|
return effect;
|
||||||
@@ -795,7 +794,6 @@ static JokerEffect triboulet_joker_effect(Joker *joker, Card *scored_card, enum
|
|||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static JokerEffect dusk_joker_effect(Joker *joker, Card *scored_card, enum JokerEvent joker_event)
|
static JokerEffect dusk_joker_effect(Joker *joker, Card *scored_card, enum JokerEvent joker_event)
|
||||||
{
|
{
|
||||||
JokerEffect effect = {0};
|
JokerEffect effect = {0};
|
||||||
@@ -832,15 +830,16 @@ static JokerEffect dusk_joker_effect(Joker *joker, Card *scored_card, enum Joker
|
|||||||
static JokerEffect blueprint_joker_effect(Joker *joker, Card *scored_card, enum JokerEvent joker_event)
|
static JokerEffect blueprint_joker_effect(Joker *joker, Card *scored_card, enum JokerEvent joker_event)
|
||||||
{
|
{
|
||||||
JokerEffect effect = {0};
|
JokerEffect effect = {0};
|
||||||
List* jokers = get_jokers();
|
List* jokers = get_jokers_list();
|
||||||
int list_size = list_get_size(jokers);
|
|
||||||
|
|
||||||
for (int i = 0; i < list_size - 1; i++ )
|
ListItr itr = list_itr_create(jokers);
|
||||||
|
JokerObject* curr_joker_object;
|
||||||
|
|
||||||
|
while((curr_joker_object = list_itr_next(&itr)))
|
||||||
{
|
{
|
||||||
JokerObject* curr_joker_object = list_get(jokers, i);
|
|
||||||
if (curr_joker_object->joker == joker)
|
if (curr_joker_object->joker == joker)
|
||||||
{
|
{
|
||||||
JokerObject* next_joker_object = list_get(jokers, i + 1);
|
JokerObject* next_joker_object = list_itr_next(&itr);
|
||||||
effect = joker_get_score_effect(next_joker_object->joker, scored_card, joker_event);
|
effect = joker_get_score_effect(next_joker_object->joker, scored_card, joker_event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -859,8 +858,8 @@ static JokerEffect brainstorm_joker_effect(Joker *joker, Card *scored_card, enum
|
|||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
List* jokers = get_jokers();
|
List* jokers = get_jokers_list();
|
||||||
JokerObject* first_joker = list_get(jokers, 0);
|
JokerObject* first_joker = list_get_at_idx(jokers, 0);
|
||||||
|
|
||||||
if (first_joker != NULL && first_joker->joker->id != JOKER_BRAINSTORM_ID)
|
if (first_joker != NULL && first_joker->joker->id != JOKER_BRAINSTORM_ID)
|
||||||
{
|
{
|
||||||
|
|||||||
+181
-89
@@ -1,121 +1,213 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "util.h"
|
#include "pool.h"
|
||||||
|
|
||||||
List *list_new(int init_size) {
|
/**
|
||||||
List *list = (List *)malloc(sizeof(List));
|
* Remove a node from a list.
|
||||||
if (list == NULL) return NULL;
|
*
|
||||||
list->_array = (void **)malloc(sizeof(void*) * init_size);
|
* Remove a @ref ListNode from a @ref List. There are no checks to ensure that the
|
||||||
if (!list->_array)
|
* passed `node` is actually part of the passed `list`. Handle with care.
|
||||||
{
|
* This is used with the @ref ListItr specifically.
|
||||||
free(list);
|
*
|
||||||
return NULL;
|
* @param list pointer to a @ref List
|
||||||
}
|
* @param node pointer to a @ref ListNode
|
||||||
list->size = 0;
|
*/
|
||||||
list->allocated_size = init_size;
|
static void _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* _list_itr_node_next(ListItr* itr);
|
||||||
|
|
||||||
|
|
||||||
|
List list_create(void)
|
||||||
|
{
|
||||||
|
List list = { .head = NULL, .tail = NULL, .len = 0 };
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void list_destroy(List **list) {
|
void list_clear(List* list)
|
||||||
if (list == NULL || *list == NULL)
|
{
|
||||||
return;
|
if(list_is_empty(list)) return;
|
||||||
|
|
||||||
|
ListItr itr = list_itr_create(list);
|
||||||
|
ListNode* ln;
|
||||||
|
|
||||||
|
while((ln = _list_itr_node_next(&itr)))
|
||||||
{
|
{
|
||||||
free((*list)->_array);
|
POOL_FREE(ListNode, ln);
|
||||||
free(*list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*list = NULL;
|
list->head = NULL;
|
||||||
|
list->tail = NULL;
|
||||||
|
list->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool int_list_append(List *list, intptr_t value)
|
bool list_is_empty(const List* list)
|
||||||
{
|
{
|
||||||
return list_append(list, (void*)value);
|
return list->len == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool list_append(List *list, void *value)
|
void list_push_front(List *list, void* data)
|
||||||
{
|
{
|
||||||
if (list->size >= list->allocated_size)
|
ListNode *node = POOL_GET(ListNode);
|
||||||
|
|
||||||
|
node->data = data;
|
||||||
|
node->prev = NULL;
|
||||||
|
node->next = list->head;
|
||||||
|
|
||||||
|
if (list_is_empty(list))
|
||||||
{
|
{
|
||||||
int new_size = list->allocated_size * 2;
|
list->tail = node;
|
||||||
void **new_arr = (void **)realloc(list->_array, sizeof(void*) * new_size);
|
}
|
||||||
if (new_arr == NULL)
|
else
|
||||||
return false;
|
{
|
||||||
list->_array = new_arr;
|
list->head->prev = node;
|
||||||
list->allocated_size = new_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list->_array[list->size++] = value;
|
list->head = node;
|
||||||
return true;
|
|
||||||
|
list->len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool list_remove_by_idx(List *list, int index) {
|
void list_push_back(List *list, void* data)
|
||||||
if (index < 0 || index >= list->size)
|
|
||||||
return false;
|
|
||||||
for (int i = index; i < list->size - 1; ++i)
|
|
||||||
{
|
|
||||||
list->_array[i] = list->_array[i + 1];
|
|
||||||
}
|
|
||||||
list->size--;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool list_remove_by_value(List *list, void* value)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < list->size; i++)
|
ListNode* node = POOL_GET(ListNode);
|
||||||
|
node->data = data;
|
||||||
|
node->prev = list->tail;
|
||||||
|
node->next = NULL;
|
||||||
|
|
||||||
|
if (list_is_empty(list))
|
||||||
{
|
{
|
||||||
if (list->_array[i] == value)
|
list->head = node;
|
||||||
{
|
}
|
||||||
return list_remove_by_idx(list, i);
|
else
|
||||||
}
|
{
|
||||||
}
|
list->tail->next = node;
|
||||||
|
}
|
||||||
return false;
|
|
||||||
}
|
list->tail = node;
|
||||||
|
|
||||||
bool int_list_remove_by_value(List *list, intptr_t value)
|
list->len++;
|
||||||
{
|
}
|
||||||
return list_remove_by_value(list, (void*)value);
|
|
||||||
}
|
static void _list_remove_node(List *list, ListNode *node)
|
||||||
|
{
|
||||||
void* list_get(List *list, int index)
|
if(node->prev && !node->next) // end of list
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= list->size)
|
node->prev->next = NULL;
|
||||||
return NULL;
|
list->tail = node->prev;
|
||||||
return list->_array[index];
|
}
|
||||||
}
|
else if(node->prev && node->next) // somewhere in between
|
||||||
|
{
|
||||||
intptr_t int_list_get(List *list, int index)
|
node->prev->next = node->next;
|
||||||
{
|
node->next->prev = node->prev;
|
||||||
return (intptr_t)list_get(list, index);
|
}
|
||||||
}
|
else if(node->next && !node->prev) // beginning of list
|
||||||
|
{
|
||||||
int list_get_size(List *list)
|
node->next->prev = NULL;
|
||||||
{
|
list->head = node->next;
|
||||||
if (list == NULL)
|
}
|
||||||
{
|
else if(!node->prev && !node->next) // only element in list
|
||||||
return UNDEFINED;
|
{
|
||||||
}
|
list->head = NULL;
|
||||||
return list->size;
|
list->tail = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool list_exists(List *list, void *value)
|
POOL_FREE(ListNode, node);
|
||||||
{
|
|
||||||
if (list == NULL) return false;
|
list->len--;
|
||||||
|
}
|
||||||
for (int i = 0; i < list->size; i++)
|
|
||||||
{
|
int list_get_len(const List* list)
|
||||||
if (list->_array[i] == value)
|
{
|
||||||
|
return list->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* list_get_at_idx(List* list, int n)
|
||||||
|
{
|
||||||
|
if(n >= list_get_len(list) || n < 0) return NULL;
|
||||||
|
|
||||||
|
int curr_idx = 0;
|
||||||
|
ListItr itr = list_itr_create(list);
|
||||||
|
void* data = NULL;
|
||||||
|
|
||||||
|
while((data = list_itr_next(&itr)))
|
||||||
|
{
|
||||||
|
if (n == curr_idx++) return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool list_remove_at_idx(List* list, int n)
|
||||||
|
{
|
||||||
|
if(n >= list_get_len(list) || n < 0) return false;
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
ListItr itr = list_itr_create(list);
|
||||||
|
ListNode* ln;
|
||||||
|
|
||||||
|
while((ln = _list_itr_node_next(&itr)))
|
||||||
|
{
|
||||||
|
if(n == len++)
|
||||||
{
|
{
|
||||||
|
_list_remove_node(list, ln);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool int_list_exists(List *list, intptr_t value)
|
ListItr list_itr_create(List* list)
|
||||||
{
|
{
|
||||||
return list_exists(list, (void*)value);
|
ListItr itr =
|
||||||
|
{
|
||||||
|
.list = list,
|
||||||
|
.next_node = !list_is_empty(list) ? list->head : NULL,
|
||||||
|
.current_node = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* list_itr_next(ListItr* itr)
|
||||||
|
{
|
||||||
|
ListNode* ln = _list_itr_node_next(itr);
|
||||||
|
return ln ? ln->data : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ListNode* _list_itr_node_next(ListItr* itr)
|
||||||
|
{
|
||||||
|
if(!itr->next_node) return NULL;
|
||||||
|
|
||||||
|
itr->current_node = itr->next_node;
|
||||||
|
|
||||||
|
ListNode* ln = itr->next_node;
|
||||||
|
|
||||||
|
if(ln->next)
|
||||||
|
{
|
||||||
|
itr->next_node = ln->next;
|
||||||
|
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;
|
||||||
|
_list_remove_node(itr->list, itr->current_node);
|
||||||
|
itr->current_node = tmp_prev;
|
||||||
}
|
}
|
||||||
@@ -1,47 +1,4 @@
|
|||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
void pool_bm_clear_idx(PoolBitmap *bm, int idx)
|
|
||||||
{
|
|
||||||
uint32_t i = idx / POOL_BITS_PER_WORD;
|
|
||||||
uint32_t b = idx % POOL_BITS_PER_WORD;
|
|
||||||
|
|
||||||
// Below are the "fast" forms of the above operations, respectively.
|
|
||||||
// These are more efficient, but removed for readability
|
|
||||||
// See: https://github.com/cellos51/balatro-gba/pull/132#discussion_r2365966071
|
|
||||||
// Divide by 32 to get the word index
|
|
||||||
//uint32_t i = idx >> 5;
|
|
||||||
// Get last 5-bits, same as a modulo (% 32) operation on positive numbers
|
|
||||||
//uint32_t b = idx & 0x1F;
|
|
||||||
bm->w[i] &= ~((uint32_t)1 << b);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pool_bm_get_free_idx(PoolBitmap *bm)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < bm->nwords; i++)
|
|
||||||
{
|
|
||||||
uint32_t inv = ~bm->w[i];
|
|
||||||
|
|
||||||
// guard so we don't call `ctz` with 0, since __builtin_ctz(0) is undefined
|
|
||||||
// https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz
|
|
||||||
//
|
|
||||||
// By using the bitwise inverse of the word, you can skip words that are full
|
|
||||||
// quickly (where the value is 0 or 'false' since all bits are '1', or 'in use'). Any value greater
|
|
||||||
// than 0 indicates there is a free slot. Then, when counting the trailing 0's, you can test very quickly
|
|
||||||
// where the first free slot is. This operation prevents looping through every bit of filled flags, and
|
|
||||||
// will instead operate only on the first word with free slots.
|
|
||||||
if (inv)
|
|
||||||
{
|
|
||||||
int bit = __builtin_ctz(inv);
|
|
||||||
bm->w[i] |= ((uint32_t)1 << bit);
|
|
||||||
int idx = i * POOL_BITS_PER_WORD + bit;
|
|
||||||
return (idx < bm->cap) ? idx : UNDEFINED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define POOL_ENTRY(name, capacity) \
|
#define POOL_ENTRY(name, capacity) \
|
||||||
POOL_DEFINE_TYPE(name, capacity);
|
POOL_DEFINE_TYPE(name, capacity);
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
CC := gcc
|
||||||
|
CFLAGS := -I../../include -I. \
|
||||||
|
-g -O3 -Wall -Werror
|
||||||
|
SRC := bitset_test.c \
|
||||||
|
../../source/bitset.c
|
||||||
|
OUT := build/bitset_test
|
||||||
|
|
||||||
|
$(OUT): $(SRC) | build
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir -p build
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OUT)
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#include "bitset.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
BITSET_DEFINE(test_bitset, BITSET_MAX_BITS)
|
||||||
|
|
||||||
|
// bitset_set_idx
|
||||||
|
// bitset_get_idx
|
||||||
|
// bitset_num_set_bits
|
||||||
|
// bitset_is_empty
|
||||||
|
// bitset_clear
|
||||||
|
void test_bitset_fill_all_and_empty(void)
|
||||||
|
{
|
||||||
|
assert(bitset_is_empty(&test_bitset));
|
||||||
|
|
||||||
|
for(int i = 0; i < BITSET_MAX_BITS; i++)
|
||||||
|
{
|
||||||
|
bitset_set_idx(&test_bitset, i, true);
|
||||||
|
assert(bitset_num_set_bits(&test_bitset) == (i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < BITSET_MAX_BITS; i++)
|
||||||
|
{
|
||||||
|
assert(bitset_get_idx(&test_bitset, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!bitset_is_empty(&test_bitset));
|
||||||
|
|
||||||
|
bitset_clear(&test_bitset);
|
||||||
|
|
||||||
|
for(int i = 0; i < BITSET_MAX_BITS; i++)
|
||||||
|
{
|
||||||
|
assert(!bitset_get_idx(&test_bitset, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bitset_is_empty(&test_bitset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitset_set_idx
|
||||||
|
// bitset_num_set_bits
|
||||||
|
// bitset_find_idx_of_nth_set
|
||||||
|
// bitset_is_empty
|
||||||
|
// bitset_clear
|
||||||
|
void test_bitset_insertions_at_boundry(void)
|
||||||
|
{
|
||||||
|
assert(bitset_is_empty(&test_bitset));
|
||||||
|
|
||||||
|
bitset_set_idx(&test_bitset, 30, true);
|
||||||
|
bitset_set_idx(&test_bitset, 31, true);
|
||||||
|
bitset_set_idx(&test_bitset, 32, true);
|
||||||
|
|
||||||
|
assert(bitset_num_set_bits(&test_bitset) == 3);
|
||||||
|
|
||||||
|
bitset_set_idx(&test_bitset, 31, false);
|
||||||
|
|
||||||
|
assert(bitset_num_set_bits(&test_bitset) == 2);
|
||||||
|
|
||||||
|
assert(bitset_find_idx_of_nth_set(&test_bitset, 0) == 30);
|
||||||
|
assert(bitset_find_idx_of_nth_set(&test_bitset, 1) == 32);
|
||||||
|
|
||||||
|
bitset_set_idx(&test_bitset, 0, true);
|
||||||
|
|
||||||
|
assert(bitset_find_idx_of_nth_set(&test_bitset, 0) == 0);
|
||||||
|
|
||||||
|
bitset_set_idx(&test_bitset, 0, false);
|
||||||
|
bitset_set_idx(&test_bitset, 0, 30);
|
||||||
|
|
||||||
|
bitset_clear(&test_bitset);
|
||||||
|
|
||||||
|
assert(bitset_is_empty(&test_bitset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitset_set_idx
|
||||||
|
// bitset_is_empty
|
||||||
|
// bitset_clear
|
||||||
|
// bitset_itr_create
|
||||||
|
// bitset_itr_next
|
||||||
|
void test_bitset_iterator(void)
|
||||||
|
{
|
||||||
|
assert(bitset_is_empty(&test_bitset));
|
||||||
|
|
||||||
|
int test_indices[6] = {0, 31, 32, 63, 64, 100};
|
||||||
|
|
||||||
|
for(int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
bitset_set_idx(&test_bitset, test_indices[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitsetItr itr = bitset_itr_create(&test_bitset);
|
||||||
|
|
||||||
|
int test_val = UNDEFINED;
|
||||||
|
int index = 0;
|
||||||
|
while((test_val = bitset_itr_next(&itr)) != UNDEFINED)
|
||||||
|
{
|
||||||
|
assert(test_val == test_indices[index++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(index == 6);
|
||||||
|
|
||||||
|
bitset_clear(&test_bitset);
|
||||||
|
|
||||||
|
assert(bitset_is_empty(&test_bitset));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("Testing Bitset Fill All and Empty.\n");
|
||||||
|
test_bitset_fill_all_and_empty();
|
||||||
|
printf("Testing Bitset Insertions At Boundry.\n");
|
||||||
|
test_bitset_insertions_at_boundry();
|
||||||
|
printf("Testing Bitset Iterator.\n");
|
||||||
|
test_bitset_iterator();
|
||||||
|
|
||||||
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
|
printf("Bitset Tests Passed :)\n");
|
||||||
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
CC := gcc
|
||||||
|
CFLAGS := -I../../include -I. \
|
||||||
|
-g -O3 -Wall -Werror -DPOOLS_TEST_ENV=yes
|
||||||
|
|
||||||
|
SRC := list_test.c \
|
||||||
|
../../source/list.c \
|
||||||
|
../../source/pool.c \
|
||||||
|
../../source/bitset.c
|
||||||
|
OUT := build/list_test
|
||||||
|
|
||||||
|
$(OUT): $(SRC) | build
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir -p build
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OUT)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#include "list.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
POOL_ENTRY(ListNode, MAX_LIST_NODES);
|
||||||
|
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// As simple as it gets, just needs to be initialized correctly
|
||||||
|
// - list_create
|
||||||
|
// - list_is_empty
|
||||||
|
// - list_get_len
|
||||||
|
// - list_clear
|
||||||
|
void create_and_clear_list(void)
|
||||||
|
{
|
||||||
|
List my_cool_list = list_create();
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_get_len(&my_cool_list) == 0);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
|
||||||
|
list_clear(&my_cool_list);
|
||||||
|
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push back one entry, make sure it looks as expected
|
||||||
|
// tests:
|
||||||
|
// - list_create
|
||||||
|
// - list_push_back
|
||||||
|
// - list_is_empty
|
||||||
|
// - list_get_len
|
||||||
|
// - list_get_at_idx
|
||||||
|
// - list_clear
|
||||||
|
void push_back_one_entry(void)
|
||||||
|
{
|
||||||
|
List my_cool_list = list_create();
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_get_len(&my_cool_list) == 0);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
|
||||||
|
int test_data = 1337;
|
||||||
|
|
||||||
|
list_push_back(&my_cool_list, &test_data);
|
||||||
|
|
||||||
|
// should have data and be the same head/tail
|
||||||
|
assert(my_cool_list.head != NULL);
|
||||||
|
assert(my_cool_list.tail != NULL);
|
||||||
|
assert(my_cool_list.head == my_cool_list.tail);
|
||||||
|
assert(list_get_len(&my_cool_list) == 1);
|
||||||
|
|
||||||
|
// pointer should be the same
|
||||||
|
assert(my_cool_list.head->data == &test_data);
|
||||||
|
// and consequently the value should be the same
|
||||||
|
assert(*(int*)(my_cool_list.head->data) == test_data);
|
||||||
|
assert(list_get_at_idx(&my_cool_list, 0) == &test_data);
|
||||||
|
assert(list_get_at_idx(&my_cool_list, 1) == NULL);
|
||||||
|
assert(!list_is_empty(&my_cool_list));
|
||||||
|
// clear the list
|
||||||
|
list_clear(&my_cool_list);
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push front one entry, make sure it looks as expected
|
||||||
|
// - list_create
|
||||||
|
// - list_push_front
|
||||||
|
// - list_is_empty
|
||||||
|
// - list_get_len
|
||||||
|
// - list_get_at_idx
|
||||||
|
// - list_clear
|
||||||
|
void push_front_one_entry(void)
|
||||||
|
{
|
||||||
|
List my_cool_list = list_create();
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_get_len(&my_cool_list) == 0);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
|
||||||
|
int test_data = 1337;
|
||||||
|
|
||||||
|
list_push_front(&my_cool_list, &test_data);
|
||||||
|
|
||||||
|
// should have data and be the same head/tail
|
||||||
|
assert(my_cool_list.head != NULL);
|
||||||
|
assert(my_cool_list.tail != NULL);
|
||||||
|
assert(my_cool_list.head == my_cool_list.tail);
|
||||||
|
assert(list_get_len(&my_cool_list) == 1);
|
||||||
|
assert(!list_is_empty(&my_cool_list));
|
||||||
|
|
||||||
|
// pointer should be the same
|
||||||
|
assert(my_cool_list.head->data == &test_data);
|
||||||
|
// and consequently the value should be the same
|
||||||
|
assert(*(int*)(my_cool_list.head->data) == test_data);
|
||||||
|
assert(list_get_at_idx(&my_cool_list, 0) == &test_data);
|
||||||
|
assert(list_get_at_idx(&my_cool_list, 1) == NULL);
|
||||||
|
// clear the list
|
||||||
|
list_clear(&my_cool_list);
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is meant to be a full test of every function including the use of
|
||||||
|
// the ListItr iterator
|
||||||
|
//
|
||||||
|
// Push back three entries. To cover the 3 states of nodes outside of single node
|
||||||
|
// lists. 3 states being, node at the front, node at the middle, and node at the end
|
||||||
|
//
|
||||||
|
// Afterwards remove one entry with `list_remove_at_idx` and then remove another
|
||||||
|
// with `list_remove_node`
|
||||||
|
//
|
||||||
|
// Lastly, push to the front the same 3 entries and verify the state of the list
|
||||||
|
// before detroying.
|
||||||
|
//
|
||||||
|
// - list_create
|
||||||
|
// - list_push_front
|
||||||
|
// - list_is_empty
|
||||||
|
// - list_get_len
|
||||||
|
// - list_get_at_idx
|
||||||
|
// - list_remove_at_idx
|
||||||
|
// - list_itr_create
|
||||||
|
// - list_itr_next
|
||||||
|
// - list_clear
|
||||||
|
// - list_itr_remove_node_current;
|
||||||
|
void push_back_three_remove_push_front_three_entries(void)
|
||||||
|
{
|
||||||
|
List my_cool_list = list_create();
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
|
||||||
|
int test_data[3] = {0, 1, 2};
|
||||||
|
|
||||||
|
list_push_back(&my_cool_list, &test_data[0]);
|
||||||
|
|
||||||
|
// Make sure with one entry it looks right
|
||||||
|
assert(my_cool_list.head == my_cool_list.tail);
|
||||||
|
|
||||||
|
list_push_back(&my_cool_list, &test_data[1]);
|
||||||
|
|
||||||
|
// Now with two it should be different
|
||||||
|
assert(my_cool_list.head != my_cool_list.tail);
|
||||||
|
|
||||||
|
list_push_back(&my_cool_list, &test_data[2]);
|
||||||
|
|
||||||
|
// 3 entries?
|
||||||
|
assert(list_get_len(&my_cool_list) == 3);
|
||||||
|
|
||||||
|
// now use an iterator to examine each node
|
||||||
|
int* data;
|
||||||
|
|
||||||
|
ListItr list_itr = list_itr_create(&my_cool_list);
|
||||||
|
|
||||||
|
int itr = 0;
|
||||||
|
|
||||||
|
ListNode* prev_ln = NULL;
|
||||||
|
|
||||||
|
while((data = list_itr_next(&list_itr)))
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
assert(data == &test_data[itr]);
|
||||||
|
ListNode* ln = list_itr.current_node;
|
||||||
|
switch(itr)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
assert(ln->prev == NULL);
|
||||||
|
assert(ln->next != NULL);
|
||||||
|
assert(ln->next->prev == ln);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
assert(ln->prev == prev_ln);
|
||||||
|
assert(ln->prev != NULL);
|
||||||
|
assert(ln->next != NULL);
|
||||||
|
assert(ln->next->prev == ln);
|
||||||
|
assert(ln->prev->next == ln);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
assert(ln->prev != NULL);
|
||||||
|
assert(ln->next == NULL);
|
||||||
|
assert(ln->prev->next == ln);
|
||||||
|
assert(ln->prev == prev_ln);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false); // shouldn't get here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
itr++;
|
||||||
|
prev_ln = ln;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the middle entry
|
||||||
|
assert(list_remove_at_idx(&my_cool_list, 1));
|
||||||
|
// can't remove entry 2 now (doesn't exist)
|
||||||
|
assert(!list_remove_at_idx(&my_cool_list, 2));
|
||||||
|
assert(list_get_len(&my_cool_list) == 2);
|
||||||
|
|
||||||
|
list_itr = list_itr_create(&my_cool_list);
|
||||||
|
data = NULL;
|
||||||
|
itr = 0;
|
||||||
|
prev_ln = NULL;
|
||||||
|
while((data = list_itr_next(&list_itr)))
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
ListNode* ln = list_itr.current_node;
|
||||||
|
switch(itr)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
assert(ln->prev == NULL);
|
||||||
|
assert(ln->next != NULL);
|
||||||
|
assert(ln->next->prev == ln);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
assert(ln->prev != NULL);
|
||||||
|
assert(ln->next == NULL);
|
||||||
|
assert(ln->prev->next == ln);
|
||||||
|
assert(ln->prev == prev_ln);
|
||||||
|
list_itr_remove_current_node(&list_itr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false); // shouldn't get here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
itr++;
|
||||||
|
prev_ln = ln;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(list_get_len(&my_cool_list) == 1);
|
||||||
|
assert(my_cool_list.head == my_cool_list.tail);
|
||||||
|
|
||||||
|
// now push the same 3 pointers to the front of the list
|
||||||
|
list_push_front(&my_cool_list, &test_data[0]);
|
||||||
|
list_push_front(&my_cool_list, &test_data[1]);
|
||||||
|
list_push_front(&my_cool_list, &test_data[2]);
|
||||||
|
|
||||||
|
// now, the list should be in the order...
|
||||||
|
// test_data[2] -> test_data[1] -> test_data[0] -> test_data[0]
|
||||||
|
list_itr = list_itr_create(&my_cool_list);
|
||||||
|
data = NULL;
|
||||||
|
itr = 0;
|
||||||
|
while((data = list_itr_next(&list_itr)))
|
||||||
|
{
|
||||||
|
assert(data != NULL);
|
||||||
|
ListNode* ln = list_itr.current_node;
|
||||||
|
|
||||||
|
switch(itr)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
assert(ln->data == &test_data[2]);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
assert(ln->data == &test_data[1]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
assert(ln->data == &test_data[0]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
assert(ln->data == &test_data[0]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false); // shouldn't get here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
itr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(list_get_len(&my_cool_list) == 4);
|
||||||
|
|
||||||
|
// clear the list
|
||||||
|
list_clear(&my_cool_list);
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("Testing List Create and Clear.\n");
|
||||||
|
create_and_clear_list();
|
||||||
|
|
||||||
|
printf("Testing List Push Back.\n");
|
||||||
|
push_back_one_entry();
|
||||||
|
|
||||||
|
printf("Testing List Push Front.\n");
|
||||||
|
push_front_one_entry();
|
||||||
|
printf("Testing List Complete Exercise.\n");
|
||||||
|
push_back_three_remove_push_front_three_entries();
|
||||||
|
|
||||||
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
|
printf("List Tests Passed :)\n");
|
||||||
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+1
-1
@@ -3,7 +3,7 @@ CC := gcc
|
|||||||
CFLAGS := -I../../include -I. \
|
CFLAGS := -I../../include -I. \
|
||||||
-g -O3 -Wall -Werror -DPOOLS_TEST_ENV=yes
|
-g -O3 -Wall -Werror -DPOOLS_TEST_ENV=yes
|
||||||
|
|
||||||
SRC := pool_test.c ../../source/pool.c
|
SRC := pool_test.c ../../source/pool.c ../../source/bitset.c
|
||||||
OUT := build/pool_test
|
OUT := build/pool_test
|
||||||
|
|
||||||
$(OUT): $(SRC) | build
|
$(OUT): $(SRC) | build
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ int main(void)
|
|||||||
printf("Testing Pool Fill and Empty.\n");
|
printf("Testing Pool Fill and Empty.\n");
|
||||||
if(!test_fill_and_empty()) return UNDEFINED;
|
if(!test_fill_and_empty()) return UNDEFINED;
|
||||||
|
|
||||||
printf("---------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
printf("Pool Tests Passed\n");
|
printf("Pool Tests Passed :)\n");
|
||||||
printf("---------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
printf("Testing execution time for fun :)\n\n");
|
printf("Testing execution time for fun :)\n\n");
|
||||||
|
|
||||||
timestamp_t t1 = get_time();
|
timestamp_t t1 = get_time();
|
||||||
|
|||||||
+15
-7
@@ -2,12 +2,20 @@
|
|||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
run_pool_test() {
|
run_test() {
|
||||||
cd pool
|
name="$1"
|
||||||
make clean
|
|
||||||
make
|
echo "==============================================================================="
|
||||||
./build/pool_test
|
echo "Running test for: $1"
|
||||||
cd - > /dev/null
|
echo "==============================================================================="
|
||||||
|
|
||||||
|
cd "$name" 2>&1 > /dev/null
|
||||||
|
make clean > /dev/null
|
||||||
|
make > /dev/null
|
||||||
|
./build/"$name"_test
|
||||||
|
cd - 2>&1 > /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
run_pool_test
|
run_test bitset
|
||||||
|
run_test pool
|
||||||
|
run_test list
|
||||||
|
|||||||
Reference in New Issue
Block a user