Mirai's Miscellaneous Misadventures

M34 / core / behaviors.c

// copyright 2022 zamfofex
// license: AGPLv3 or later

#include <mimimi/behaviors.h>
#include <mimimi/compound-behaviors.h>
#include <mimimi/chapters.h>
#include <mimimi/allocators.h>

struct mimimi_compound_behavior
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	int count;
	struct mimimi_behavior **behaviors;
};

static void mimimi_compound_behave(void *data)
{
	struct mimimi_compound_behavior *compound = data;
	for (int i = 0 ; i < compound->count ; i++)
	{
		struct mimimi_behavior *behavior = compound->behaviors[i];
		(*behavior->behave)(behavior->data);
	}
}

static void mimimi_compound_finish(void *data)
{
	struct mimimi_compound_behavior *compound = data;
	for (int i = 0 ; i < compound->count ; i++)
	{
		struct mimimi_behavior *behavior = compound->behaviors[i];
		(*behavior->finish)(behavior->data);
	}
	
	if (compound->count != 0)
		mimimi_deallocate(compound->allocator, compound->behaviors);
	mimimi_deallocate(compound->allocator, compound->behavior);
	mimimi_deallocate(compound->allocator, compound);
}

struct mimimi_behavior *mimimi_compound_behavior(struct mimimi_allocator *allocator)
{
	struct mimimi_compound_behavior *data = mimimi_allocate(allocator, sizeof *data);
	
	data->allocator = allocator;
	data->behavior = mimimi_allocate(allocator, sizeof *data->behavior);
	data->behavior->behave = &mimimi_compound_behave;
	data->behavior->finish = &mimimi_compound_finish;
	data->behavior->data = data;
	
	data->count = 0;
	data->behaviors = allocator->null;
	
	return data->behavior;
}

void mimimi_compound(struct mimimi_behavior *behavior, struct mimimi_behavior *other)
{
	struct mimimi_compound_behavior *compound = behavior->data;
	
	compound->count++;
	compound->behaviors = mimimi_reallocate(compound->allocator, compound->behaviors, compound->count * sizeof *compound->behaviors);
	compound->behaviors[compound->count - 1] = other;
}

void mimimi_compound_remove(struct mimimi_behavior *behavior, struct mimimi_behavior *other)
{
	struct mimimi_compound_behavior *compound = behavior->data;
	for (int i = 0 ; i < compound->count ; i++)
	{
		struct mimimi_behavior *behavior = compound->behaviors[i];
		if (behavior == other)
		{
			compound->count--;
			for (int j = i ; j < compound->count ; j++)
				compound->behaviors[j] = compound->behaviors[j + 1];
			
			if (compound->count == 0)
				mimimi_deallocate(compound->allocator, compound->behaviors);
			else
				compound->behaviors = mimimi_reallocate(compound->allocator, compound->behaviors, compound->count * sizeof *compound->behaviors);
			
			break;
		}
	}
}

char mimimi_compound_empty(struct mimimi_behavior *behavior)
{
	struct mimimi_compound_behavior *compound = behavior->data;
	if (compound->count == 0) return 1;
	else return 0;
}

static void mimimi_ignore(void *data) { (void) data; }

static struct mimimi_behavior mimimi_noop_value = {&mimimi_ignore, &mimimi_ignore, 0};
struct mimimi_behavior *mimimi_noop = &mimimi_noop_value;

struct mimimi_function
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	struct mimimi_behavior *other;
};

static void mimimi_function_behave(void *data)
{
	struct mimimi_function *function = data;
	(*function->other->behave)(function->other->data);
}

static void mimimi_function_finish(void *data)
{
	struct mimimi_function *function = data;
	(*function->other->finish)(function->other->data);
	mimimi_deallocate(function->allocator, function->other);
	mimimi_deallocate(function->allocator, function->behavior);
	mimimi_deallocate(function->allocator, function);
}

struct mimimi_behavior *mimimi_function(void *data, void (*behave)(void *data), struct mimimi_allocator *allocator)
{
	struct mimimi_function *function = mimimi_allocate(allocator, sizeof *function);
	function->allocator = allocator;
	
	function->other = mimimi_allocate(allocator, sizeof *function->other);
	function->other->behave = behave;
	function->other->finish = &mimimi_ignore;
	function->other->data = data;
	
	function->behavior = mimimi_allocate(allocator, sizeof *function->behavior);
	function->behavior->behave = &mimimi_function_behave;
	function->behavior->finish = &mimimi_function_finish;
	function->behavior->data = function;
	
	return function->behavior;
}

struct mimimi_behavior *mimimi_finalizer(void *data, void (*finish)(void *data), struct mimimi_allocator *allocator)
{
	struct mimimi_function *function = mimimi_allocate(allocator, sizeof *function);
	function->allocator = allocator;
	
	function->other = mimimi_allocate(allocator, sizeof *function->other);
	function->other->behave = &mimimi_ignore;
	function->other->finish = finish;
	function->other->data = data;
	
	function->behavior = mimimi_allocate(allocator, sizeof *function->behavior);
	function->behavior->behave = &mimimi_function_behave;
	function->behavior->finish = &mimimi_function_finish;
	function->behavior->data = function;
	
	return function->behavior;
}

void *mimimi_compound_allocate(struct mimimi_behavior *compound, unsigned int size, struct mimimi_allocator *allocator)
{
	struct mimimi_allocator *compound_allocator = mimimi_compound_allocator(compound, allocator);
	return mimimi_allocate(compound_allocator, size);
}

struct mimimi_choice
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	unsigned char *predicate;
	struct mimimi_behavior *nonzero;
	struct mimimi_behavior *zero;
};

static void mimimi_choose(void *data)
{
	struct mimimi_choice *choice = data;
	struct mimimi_behavior *nonzero = choice->nonzero;
	struct mimimi_behavior *zero = choice->zero;
	
	if (*choice->predicate == 0)
		(*zero->behave)(zero->data);
	else
		(*nonzero->behave)(nonzero->data);
}

static void mimimi_choice_finish(void *data)
{
	struct mimimi_choice *choice = data;
	(*choice->nonzero->finish)(choice->nonzero->data);
	(*choice->zero->finish)(choice->zero->data);
	mimimi_deallocate(choice->allocator, choice->behavior);
	mimimi_deallocate(choice->allocator, choice);
}

struct mimimi_behavior *mimimi_choice(unsigned char *predicate, struct mimimi_behavior *nonzero, struct mimimi_behavior *zero, struct mimimi_allocator *allocator)
{
	struct mimimi_choice *choice = mimimi_allocate(allocator, sizeof *choice);
	choice->allocator = allocator;
	
	choice->predicate = predicate;
	choice->nonzero = nonzero;
	choice->zero = zero;
	
	choice->behavior = mimimi_allocate(allocator, sizeof *choice->behavior);
	choice->behavior->behave = &mimimi_choose;
	choice->behavior->finish = &mimimi_choice_finish;
	choice->behavior->data = choice;
	
	return choice->behavior;
}

struct mimimi_throttle
{
	struct mimimi_allocator *allocator;
	struct mimimi_behavior *behavior;
	struct mimimi_behavior *underlying;
	int time;
	int countdown;
};

static void mimimi_throttle_behave(void *data)
{
	struct mimimi_throttle *throttle = data;
	if (throttle->countdown == 0)
	{
		throttle->countdown = throttle->time;
		(*throttle->underlying->behave)(throttle->underlying->data);
	}
}

static void mimimi_throttle_finish(void *data)
{
	struct mimimi_throttle *throttle = data;
	(*throttle->underlying->finish)(throttle->underlying->data);
	mimimi_deallocate(throttle->allocator, throttle->behavior);
	mimimi_deallocate(throttle->allocator, throttle);
}

static void mimimi_countdown(void *data)
{
	int *n = data;
	if (*n != 0) n[0]--;
}

struct mimimi_behavior *mimimi_throttle(struct mimimi_behavior *compound, struct mimimi_behavior *underlying, int time, struct mimimi_allocator *allocator)
{
	struct mimimi_throttle *throttle = mimimi_allocate(allocator, sizeof *throttle);
	throttle->allocator = allocator;
	
	throttle->underlying = underlying;
	throttle->time = time;
	throttle->countdown = 0;
	
	throttle->behavior = mimimi_allocate(allocator, sizeof *throttle->behavior);
	throttle->behavior->behave = &mimimi_throttle_behave;
	throttle->behavior->finish = &mimimi_throttle_finish;
	throttle->behavior->data = throttle;
	
	mimimi_compound(compound, mimimi_function(&throttle->countdown, mimimi_countdown, allocator));
	
	return throttle->behavior;
}