src/ai/api/ai_abstractlist.cpp
author truebrain
Thu, 28 Feb 2008 01:04:50 +0000
branchnoai
changeset 9802 1095a6d029ed
parent 9796 83d54622190c
child 9814 be51ea0adc29
permissions -rw-r--r--
(svn r12308) [NoAI] -Fix: don't allow AddList on lists of different types
/* $Id$ */

/** @file ai_abstractlist.cpp Implementation of AIAbstractList */

#include "ai_abstractlist.hpp"
#include "../../debug.h"

/**
 * Base class for any AIAbstractList sorter.
 */
class AIAbstractListSorter {
protected:
	AIAbstractList *list;

public:
	/**
	 * Virtual dtor, needed to mute warnings.
	 */
	virtual ~AIAbstractListSorter() { }

	/**
	 * Get the first item of the sorter.
	 */
	virtual int32 Begin() = 0;

	/**
	 * Get the next item of the sorter.
	 */
	virtual int32 Next() = 0;

	/**
	 * See if there is a next item of the sorter.
	 */
	virtual bool HasNext() = 0;
};

/**
 * Sort by value, ascending.
 */
class AIAbstractListSorterValueAscending : public AIAbstractListSorter {
private:
	AIAbstractList::AIAbstractListBucket::iterator bucket_iter;
	AIAbstractList::AIItemList *bucket_list;
	AIAbstractList::AIItemList::iterator bucket_list_iter;

public:
	AIAbstractListSorterValueAscending(AIAbstractList *list)
	{
		this->list = list;
		this->bucket_list = NULL;
	}

	int32 Begin()
	{
		if (this->list->buckets.empty()) return 0;

		this->bucket_iter = this->list->buckets.begin();
		this->bucket_list = &(*this->bucket_iter).second;
		this->bucket_list_iter = this->bucket_list->begin();
		return *bucket_list_iter;
	}

	int32 Next()
	{
		if (this->list->buckets.empty() || this->bucket_list == NULL) return 0;

		this->bucket_list_iter++;
		if (this->bucket_list_iter == this->bucket_list->end()) {
			this->bucket_iter++;
			if (this->bucket_iter == this->list->buckets.end()) return 0;
			this->bucket_list = &(*this->bucket_iter).second;
			this->bucket_list_iter = this->bucket_list->begin();
		}
		return *bucket_list_iter;
	}

	bool HasNext()
	{
		if (this->list->buckets.empty() || this->bucket_list == NULL) return false;

		return this->bucket_iter != this->list->buckets.end() && this->bucket_list_iter != this->bucket_list->end();
	}
};

/**
 * Sort by value, descending.
 */
class AIAbstractListSorterValueDescending : public AIAbstractListSorter {
private:
	AIAbstractList::AIAbstractListBucket::reverse_iterator bucket_iter;
	AIAbstractList::AIItemList *bucket_list;
	AIAbstractList::AIItemList::reverse_iterator bucket_list_iter;

public:
	AIAbstractListSorterValueDescending(AIAbstractList *list)
	{
		this->list = list;
		this->bucket_list = NULL;
	}

	int32 Begin()
	{
		if (this->list->buckets.empty()) return 0;

		this->bucket_iter = this->list->buckets.rbegin();
		this->bucket_list = &(*this->bucket_iter).second;
		this->bucket_list_iter = this->bucket_list->rbegin();
		return *bucket_list_iter;
	}

	int32 Next()
	{
		if (this->list->buckets.empty() || this->bucket_list == NULL) return 0;

		this->bucket_list_iter++;
		if (this->bucket_list_iter == this->bucket_list->rend()) {
			this->bucket_iter++;
			if (this->bucket_iter == this->list->buckets.rend()) return 0;
			this->bucket_list = &(*this->bucket_iter).second;
			this->bucket_list_iter = this->bucket_list->rbegin();
		}
		return *bucket_list_iter;
	}

	bool HasNext()
	{
		if (this->list->buckets.empty() || this->bucket_list == NULL) return false;

		return this->bucket_iter != this->list->buckets.rend() && this->bucket_list_iter != this->bucket_list->rend();
	}
};

/**
 * Sort by item, ascending.
 */
class AIAbstractListSorterItemAscending : public AIAbstractListSorter {
private:
	AIAbstractList::AIAbstractListMap::iterator item_iter;

public:
	AIAbstractListSorterItemAscending(AIAbstractList *list)
	{
		this->list = list;
	}

	int32 Begin()
	{
		if (this->list->items.empty()) return 0;

		this->item_iter = this->list->items.begin();
		return (*this->item_iter).first;
	}

	int32 Next()
	{
		if (this->list->items.empty()) return 0;

		this->item_iter++;
		return (*this->item_iter).first;
	}

	bool HasNext()
	{
		if (this->list->items.empty()) return false;

		return this->item_iter != this->list->items.end();
	}
};

/**
 * Sort by item, descending.
 */
class AIAbstractListSorterItemDescending : public AIAbstractListSorter {
private:
	AIAbstractList::AIAbstractListMap::reverse_iterator item_iter;

public:
	AIAbstractListSorterItemDescending(AIAbstractList *list)
	{
		this->list = list;
	}

	int32 Begin()
	{
		if (this->list->items.empty()) return 0;

		this->item_iter = this->list->items.rbegin();
		return (*this->item_iter).first;
	}

	int32 Next()
	{
		if (this->list->items.empty()) return 0;

		this->item_iter++;
		return (*this->item_iter).first;
	}

	bool HasNext()
	{
		if (this->list->items.empty()) return false;

		return this->item_iter != this->list->items.rend();
	}
};



AIAbstractList::AIAbstractList()
{
	/* Default sorter */
	this->sorter         = new AIAbstractListSorterValueDescending(this);
	this->sorter_type    = SORT_BY_VALUE;
	this->sort_ascending = false;
	this->initialized    = false;
}

AIAbstractList::~AIAbstractList()
{
	delete this->sorter;
}

bool AIAbstractList::HasItem(int32 item)
{
	return this->items.count(item) == 1;
}

void AIAbstractList::Clear()
{
	this->items.clear();
	this->buckets.clear();
}

void AIAbstractList::AddItem(int32 item)
{
	if (this->HasItem(item)) return;

	this->items[item] = 0;
	this->buckets[0].insert(item);
}

void AIAbstractList::RemoveItem(int32 item)
{
	if (!this->HasItem(item)) return;

	this->buckets[this->GetValue(item)].erase(item);
	if (this->buckets[this->GetValue(item)].empty()) this->buckets.erase(this->GetValue(item));
	this->items.erase(item);
}

int32 AIAbstractList::Begin()
{
	this->initialized = true;
	return this->sorter->Begin();
}

int32 AIAbstractList::Next()
{
	if (this->initialized == false) {
		DEBUG(ai, 0, "ERROR: Next() is invalid as Begin() is never called");
		return false;
	}
	return this->sorter->Next();
}

bool AIAbstractList::IsEmpty()
{
	return this->items.empty();
}

bool AIAbstractList::HasNext()
{
	if (this->initialized == false) {
		DEBUG(ai, 0, "ERROR: HasNext() is invalid as Begin() is never called");
		return false;
	}
	return this->sorter->HasNext();
}

int32 AIAbstractList::Count()
{
	return this->items.size();
}

int32 AIAbstractList::GetValue(int32 item)
{
	if (!this->HasItem(item)) return 0;

	return this->items[item];
}

bool AIAbstractList::SetValue(int32 item, int32 value)
{
	if (!this->HasItem(item)) return false;

	this->buckets[this->GetValue(item)].erase(item);
	if (this->buckets[this->GetValue(item)].empty()) this->buckets.erase(this->GetValue(item));
	this->items[item] = value;
	this->buckets[value].insert(item);

	return true;
}

void AIAbstractList::Sort(SorterType sorter, bool ascending)
{
	if (sorter == this->sorter_type && ascending == this->sort_ascending) return;

	delete this->sorter;
	switch (sorter) {
		case SORT_BY_ITEM:
			if (ascending) this->sorter = new AIAbstractListSorterItemAscending(this);
			else           this->sorter = new AIAbstractListSorterItemDescending(this);
			break;

		case SORT_BY_VALUE:
			if (ascending) this->sorter = new AIAbstractListSorterValueAscending(this);
			else           this->sorter = new AIAbstractListSorterValueDescending(this);
			break;

		default:
			this->Sort(SORT_BY_ITEM, false);
			return;
	}
	this->sorter_type    = sorter;
	this->sort_ascending = ascending;
}

void AIAbstractList::AddList(AIAbstractList *list)
{
	if (this->GetListName() != NULL && list->GetListName() != NULL && strcmp(this->GetListName(), list->GetListName()) != 0) {
		DEBUG(ai, 0, "WARNING: You are trying to add a list %s to a list which\n", list->GetListName());
		DEBUG(ai, 0, " is based on %s. In general, this can't work. Expect fuzzy results!\n", this->GetListName());
	}

	AIAbstractListMap *list_items = &list->items;
	for (AIAbstractListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
		this->AddItem((*iter).first);
		this->SetValue((*iter).first, (*iter).second);
	}
}

void AIAbstractList::RemoveAboveValue(int32 value)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second > value) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first > value) this->buckets.erase(iter);
	}
}

void AIAbstractList::RemoveBelowValue(int32 value)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second < value) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first < value) this->buckets.erase(iter);
	}
}

void AIAbstractList::RemoveBetweenValue(int32 start, int32 end)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second > start && (*iter).second < end) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first > start && (*iter).first < end) this->buckets.erase(iter);
	}
}

void AIAbstractList::RemoveValue(int32 value)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second == value) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first == value) this->buckets.erase(iter);
	}
}

void AIAbstractList::RemoveTop(int32 count)
{
	if (!this->sort_ascending) {
		this->Sort(this->sorter_type, !this->sort_ascending);
		this->RemoveBottom(count);
		this->Sort(this->sorter_type, !this->sort_ascending);
		return;
	}

	switch (this->sorter_type) {
		default: NOT_REACHED();
		case SORT_BY_VALUE:
			for (AIAbstractListBucket::iterator iter = this->buckets.begin(); iter != this->buckets.end(); iter = this->buckets.begin()) {
				AIItemList *items = &(*iter).second;
				size_t size = items->size();
				for (AIItemList::iterator iter = items->begin(); iter != items->end(); iter = items->begin()) {
					if (--count < 0) return;
					this->RemoveItem(*iter);
					/* When the last item is removed from the bucket, the bucket itself is removed.
					 * This means that the iterators can be invalid after a call to RemoveItem.
					 */
					if (--size == 0) break;
				}
			}
			break;

		case SORT_BY_ITEM:
			for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter = this->items.begin()) {
				if (--count < 0) return;
				this->RemoveItem((*iter).first);
			}
			break;
	}
}

void AIAbstractList::RemoveBottom(int32 count)
{
	if (!this->sort_ascending) {
		this->Sort(this->sorter_type, !this->sort_ascending);
		this->RemoveTop(count);
		this->Sort(this->sorter_type, !this->sort_ascending);
		return;
	}

	switch (this->sorter_type) {
		default: NOT_REACHED();
		case SORT_BY_VALUE:
			for (AIAbstractListBucket::reverse_iterator iter = this->buckets.rbegin(); iter != this->buckets.rend(); iter = this->buckets.rbegin()) {
				AIItemList *items = &(*iter).second;
				size_t size = items->size();
				for (AIItemList::reverse_iterator iter = items->rbegin(); iter != items->rend(); iter = items->rbegin()) {
					if (--count < 0) return;
					this->RemoveItem(*iter);
					/* When the last item is removed from the bucket, the bucket itself is removed.
					 * This means that the iterators can be invalid after a call to RemoveItem.
					 */
					if (--size == 0) break;
				}
			}

		case SORT_BY_ITEM:
			for (AIAbstractListMap::reverse_iterator iter = this->items.rbegin(); iter != this->items.rend(); iter = this->items.rbegin()) {
				if (--count < 0) return;
				this->RemoveItem((*iter).first);
			}
			break;
	}
}

void AIAbstractList::RemoveList(AIAbstractList *list)
{
	AIAbstractListMap *list_items = &list->items;
	for (AIAbstractListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
		this->RemoveItem((*iter).first);
	}
}

void AIAbstractList::KeepAboveValue(int32 value)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second <= value) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first <= value) this->buckets.erase(iter);
	}
}

void AIAbstractList::KeepBelowValue(int32 value)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second >= value) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first >= value) this->buckets.erase(iter);
	}
}

void AIAbstractList::KeepBetweenValue(int32 start, int32 end)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second <= start || (*iter).second >= end) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first <= start || (*iter).first >= end) this->buckets.erase(iter);
	}
}

void AIAbstractList::KeepValue(int32 value)
{
	for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).second != value) this->items.erase(iter);
	}

	for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) {
		next_iter = iter; next_iter++;
		if ((*iter).first != value) this->buckets.erase(iter);
	}
}

void AIAbstractList::KeepTop(int32 count)
{
	this->RemoveBottom(this->Count() - count);
}

void AIAbstractList::KeepBottom(int32 count)
{
	this->RemoveTop(this->Count() - count);
}

void AIAbstractList::KeepList(AIAbstractList *list)
{
	AIAbstractList tmp;
	for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) {
		tmp.AddItem((*iter).first);
		tmp.SetValue((*iter).first, (*iter).second);
	}

	tmp.RemoveList(list);
	this->RemoveList(&tmp);
}

void AIAbstractList::Valuate(const AIAbstractList::Valuator &proc)
{
	if (this->GetListName() != NULL && proc.GetListName() != NULL && strcmp(this->GetListName(), proc.GetListName()) != 0) {
		DEBUG(ai, 0, "WARNING: You are trying to use a valuator for %s on a list which\n", this->GetListName());
		DEBUG(ai, 0, " is based on %s. In general, this can't work. Expect fuzzy results!\n", proc.GetListName());
	}

	this->buckets.clear();
	for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) {
		int32 value = proc.Valuate((*iter).first);
		(*iter).second = value;
		this->buckets[value].insert((*iter).first);
	}
}