Page 1 of 1

Fast Unit Mapper

Posted: 05 Jan 2006, 19:11
by cain

Code: Select all


#ifndef GLOBALUNITMAPPER
#define GLOBALUNITMAPPER

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/**
*	GlobalUnitMapper.h
*	
*	a complete unit mapping function to store unit data.
*	this is technically a double linked hash map, higly optimized
*	for the special case of univocal and continuos limited key
*
*	here key refers to the game unitID. it is assumed that never a
*	player will have more than 5000 units. If the limit needs to be 
*	expanded, edit the MAX_UNIT definition that follow
*
*	the iterator is sorted by insertion order
*
*	it is alghoritmically the fastest implementation possible;
*	table of comparision:
*
*	GlobalUnitMapper:
*	insertion O(1), deletion O(1), retrieval O(1)
*	
*	HashMap:
*	insertion O(1), deletion O(1), retrieval O(1) but those 
*	include "real hashing" costs not need here as keys are know
*	in number and structure.
*
*	Map:
*	insertion O(log(n)), deletion O(log(n)), retrieval O(log(n))
*
*	released under the terms of BSD license. copyright by cain.
*/

#include "IAICallback.h"
#include <string>
#include "UnitDef.h"

//modify those to use custom structures.
//having my class called unitWrapper, I use #define UNIT_DEFINITION unitWrapper *
#define UNIT_DEFINITION const UnitDef *
//this is the placeholder put on the map on empty spot
//getting a non-existant unit will return this
#define INVALID_UNIT_DEFINITION NULL
//here put the "constructor"
#define UNIT_DEFINITION_GENERATOR(x) cb->GetUnitDef(x)

#define STRING_LOGGER(x) cb->SendTextMsg(x,0)
//uncomment this to disable the logging (improves performance)
// #define STRING_LOGGER(X)
#define MAX_UNIT 5000


//note that the add, get and remove functions are NOT synchronized
//if you have a multithreaded concurrent access, you need to define
//your custom synchronization mutex.
//if you do modify this code, adding for example access mutex,
//send the modified version back to me so I can clean it up and
//mantain a unified distribution.

typedef struct {
	int unitID;
	UNIT_DEFINITION unitdef;
} unitMap;

typedef struct {
	int prev;
	int next;
} doubleLink;

class GlobalUnitMapper
{
private:
	IAICallback *cb;

	unitMap units[MAX_UNIT];
	doubleLink links[MAX_UNIT];

	int tailPos;

	int headPos;

	int size;

public:
	GlobalUnitMapper(IAICallback *cb) {
		this->cb=cb;
		for (int i=0;i<MAX_UNIT;i++) {
			units[i].unitID=-1;
			units[i].unitdef=INVALID_UNIT_DEFINITION;
			links[i].next=-1;
			links[i].prev=-1;
		}
		tailPos=-1;
		headPos=-1;
		size=0;
	}

	~GlobalUnitMapper(void){

	}
	int getSize() {
		return size;
	}

	void addUnit(int unitID) {
		int position=getUnitPosition(unitID,false);
		if (units[position].unitID==-1) {
			++size;
		} else {
			STRING_LOGGER("unit was already here");
		}
		units[position].unitID=unitID;
		units[position].unitdef=UNIT_DEFINITION_GENERATOR(unitID);

		if(headPos==-1)	headPos=position;
		if (tailPos!=-1) links[tailPos].next=position;

		links[position].next=-1;
		links[position].prev=tailPos;
		tailPos=position;

	}

	UNIT_DEFINITION getUnit(int unitID) {
		int position=getUnitPosition(unitID,true);
		if (units[position].unitID==-1) {
			STRING_LOGGER("Unit not Found");
		}
		return units[position].unitdef;
	}


	UNIT_DEFINITION removeUnit(int unitID) {
		int position=getUnitPosition(unitID,true);
		UNIT_DEFINITION r=units[position].unitdef;
		if (units[position].unitID!=-1) --size;
		else {
			STRING_LOGGER("Removing non existant unit");
		}
		if (units[(position+1)%MAX_UNIT].unitID==-1) {
			units[position].unitID=-1;
		}else {
			units[position].unitID=-1;
		}
		units[position].unitdef=INVALID_UNIT_DEFINITION;
		int prev=links[position].prev;
		int next=links[position].next;

		if (prev==-1) {
			if (next==-1) { //was the only one 
				tailPos=-1;
				headPos=-1;
			} else { //was the head
				links[next].prev=-1;
				headPos=next;
			}
		} else { 
			if (next==-1) { //was the tail
				links[prev].next=-1;
				tailPos=prev;

			} else { //in the middle
				links[next].prev=prev;
				links[prev].next=next;
			}
		}


		return r;
	}

	/**
	* fast iteator usage:
	* unit_iterator ui=map.getIterator() 
	* while (ui.hasNext()) {
	*	unitMap m=ui.next();
	*	int unitID=m.unitID;
	*	UNIT_DEFINITION def=m.unitdef;
	*	//blah blah
	* }
	*/

	class unit_iterator {
	protected:	
		int current;
		doubleLink *links;
		unitMap *units;
		unit_iterator() {
			current=-1;
		}

	public:
		unit_iterator(unitMap *units,doubleLink *links,int headPos) {

			this->units=units;
			this->links=links;
			current=headPos;
		}
		/**
		* true if there is another element avaiable
		*/
		bool hasNext() {
			if (current==-1)return false;
			return true;
		};
		/**
		* return the current element and increment the counter
		*/
		unitMap &next() {
			unitMap &p=units[current];
			current=links[current].next;
			return p;
		};

	};

	class thread_safe_unit_iterator : public  unit_iterator {

	public:
		thread_safe_unit_iterator(unitMap *units,doubleLink *links,int headPos):unit_iterator() {

			memcpy(this->units,units,sizeof(unitMap)*(MAX_UNIT));
			memcpy(this->links,links,sizeof(links)*(MAX_UNIT));
			current=headPos;
		}

	};


	/**
	* Returns a fast unit iterator
	* fast iteator usage:
	* unit_iterator ui=map.getIterator() 
	* while (ui.hasNext()) {
	*	unitMap m=ui.next();
	*	int unitID=m.unitID;
	*	UNIT_DEFINITION def=m.unitdef;
	*	//blah blah
	* }
	*
	* If the map is modified during the existance of
	* the iterator, undefined behaviour may occour.
	*/
	unit_iterator &getIterator() {
		return (*new unit_iterator(units,links,headPos));
	};

	/**
	* thread safe version of the iterator.
	* same usage, but allows modification of the map.
	* the unit returned are the ones that were present
	* at the moment of the function call. 
	* 
	* note that while the iterator IS thread safe, the
	* iteration constructor (this function) it is NOT.
	*
	* while the iteration speed is the same of the other,
	* the constructor present a little overhead for the
	* replicating of the info.
	*/
	unit_iterator &getTSIterator() {
		return (*new thread_safe_unit_iterator(units,links,headPos));
	};

private:
	int getUnitPosition(int unitID,bool consiterTombstoneFull){
		int position=(unitID)%MAX_UNIT;
		while(units[position].unitID!=unitID) {
			if (units[position].unitID==-1) return position;
			if (!consiterTombstoneFull) if(units[position].unitID==-2) return position;
			++position;
			if (position==MAX_UNIT) {position=0;}
		};

		return position;
	}

};



#endif


Posted: 05 Jan 2006, 20:45
by Veylon
I hope this isn't a stupid question, but here goes:
What's it do?

Posted: 05 Jan 2006, 20:50
by jcnossen
This is already possible I think....

struct UnitInfo { ...... };
stdext::hash_map<int, struct UnitInfo> unitMap;

EDIT:
I'm not sure how much dynamic allocation the stdext::hash_map does though, your version does that faster for sure (Since there is none).
Maybe you can change it to a template class instead of using all the macros?

EDIT2:
Ah I edited too slow ;)

Posted: 05 Jan 2006, 20:58
by cain
well, should respond for the need of a fast implementation. hashmap have some problem: the rehashing cost, the hashing cost itself. this respond to the spped optimizations thread.

I've started with templates, but that would get into the way of the automatized constructor, I needed at that point an allocator funtor, and things was getting weird...

Posted: 07 Jan 2006, 13:56
by krogothe
Yep, sucks to not only not get what that code does but also not a single sentence of the two posts above...

but ill give it a good guess:
Does it simply hold all the units currently in the game, being a fast way to add/remove them since a vector is slow for insertion/deletion and a list doesnt give good random access?
If im not completely off the mark, and thats the case, does it support me making a huge struct to hold more data about each unit while keeping the fast speed?

Posted: 08 Jan 2006, 10:53
by cain
krogothe wrote: Does it simply hold all the units currently in the game, being a fast way to add/remove them
yes. holds all unit for one player (limit = 5000)
krogothe wrote: If im not completely off the mark, and thats the case, does it support me making a huge struct to hold more data about each unit while keeping the fast speed?
yes. edit the define on the top. you need a constructor for the structure that can be initialized using only the unitID

for example you have a

class myunitdef;

and a constructor

new myunitdef(int unitid);

then

#define UNIT_DEFINITION myunitdef *

#define UNIT_DEFINITION_GENERATOR(x) new myunitdef(x)


[/quote]