Jump to content
  • Advertisement
Sign in to follow this  
NukeCorr

Kills log problem

This topic is 3327 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

C++, I'm writing a function to add a line to a kills log, the point is like this: e.g. first it would look like this
line 5
line 4
line 3
line 2
line 1
then when the function is called it adds a new line and deletes the last one
line 6
line 5
line 4
line 3
line 2
The following code works correctly if I call it in the game init, but during game loop, e.g. when someone gets killed and there's this function to add a new line to the killsList, it crashes the game. Why? MAX_KILLSLOG is defined as 5
void CGame::AddKillToList(std::string text)
{
	int i;

	for(i=0;i<MAX_KILLSLOG;i++)
	{
		if(MAX_KILLSLOG-(i+1) <= MAX_KILLSLOG)
		{
			killsList[MAX_KILLSLOG-i] = killsList[MAX_KILLSLOG-(i+1)];
		}
	}

	killsList[0] = text;
}

Share this post


Link to post
Share on other sites
Advertisement
killsList[MAX_KILLSLOG-i] = killsList[MAX_KILLSLOG-(i+1)];

Wouldn't that crash if MAX_KILLSLOG is 5 and i = 0 then you are trying to index out of the bounds?

Share this post


Link to post
Share on other sites
Let us think about the code, and look for boundary conditions.

void CGame::AddKillToList(std::string text)
{
int i;

for(i=0;i<MAX_KILLSLOG;i++)
{
if(MAX_KILLSLOG-(i+1) <= MAX_KILLSLOG)
{
killsList[MAX_KILLSLOG-i] = killsList[MAX_KILLSLOG-(i+1)];
}
}

killsList[0] = text;
}


Consider if i is 0. The condition evaluates to MAX_KILLSLOG-1 <= MAX_KILLSLOG, which is true. Then the next line executes this:

killsList[MAX_KILLSLOG] = killsList[MAX_KILLSLOG +1];

This is (probably, because you haven't shown us enough code) going beyond the bounds of the array, which could cause a crash (it is undefined behaviour).

Instead of fixing the logic, there is an alternative trick. Instead of moving the elements in the array around, we can access them in an odd fashion with an offset.

Example:

class Renderer
{
public:
void renderText(const std::string &text) { std::cout << text << std::endl; }
};

class KillList
{
public:
KillList()
:
start(0)
{
}

void add(const std::string &kill);
void render(Renderer &renderer);

private:
static const int MAX = 5;
std::string log[MAX];
int start;
};

void KillList::add(const std::string &kill)
{
log[start] = kill;
start = (start + 1) % MAX;
}

void KillList::render(Renderer &renderer)
{
for(int i = MAX - 1 ; i >= 0 ; --i)
{
int index = (start + i) % MAX;
renderer.renderText(/* position, etc ... */ log[index]);
}
}

int main()
{
Renderer renderer;
KillList list;

int i = 0;

std::string people[5] = {
"jack", "jim", "john", "dave", "donald sutherland"
};

while(++i < 20)
{
int i = rand() % 5;
int j = rand() % 5;

if(i != j)
{
list.add("\t" + people + " killed " + people[j]);
}

renderer.renderText("kills {");
list.render(renderer);
renderer.renderText("}");
}
}


So, what happens is that the array has a marker as to where we should next insert a kill. Every time we insert a kill the marker is moved to the next element (or looped around to point at the first if we run out).

When we print, we just print in reverse order. So our loop goes from MAX - 1 to 0. But, we need to take into account the offset variable "next". Hence the additional "index" calculation.

This is basically a ring buffer.

Share this post


Link to post
Share on other sites

template<typename T, size_t n>
struct RingBuffer {
T data[n];
size_t used;
RingBuffer():top(0) {}

void push_back( T const& src ) {
++used;
(*this)[used] = src;
}
T& operator[](size_t i) {
return data[i % n];
}
T const& operator[](size_t i) const {
return data[i % n];
}

struct iterator {
size_t index;
RingBuffer* buff;

T& operator*() { return (*buff)[index]; }
T* operator->() { return &( (*buff)[index] ); }
iterator& operator++() { ++index; return *this; }
iterator operator++(bool) { iterator tmp = *this; ++index; return tmp; }
iterator& operator+=(int n) { index+=n; return *this; }
iterator& operator-=(int n) { return (*this)+=(-n); }
iterator operator+(int n) const { iterator tmp=*this; tmp+= n; return tmp; }
iterator operator-(int n) const { iterator tmp=*this; tmp-= n; return tmp; }

iterator( RingBuffer* buff_, size_t index_ ): buff(buff_), index(index_) {}
};

iterator end() { return iterator( this, used ); }
iterator begin() {
size_t start = (used < n)?0:(used-n);
return iterator( this, start );
}
};



That was amusing. The idea is that now we can push_back data into the ringbuffer, and you can iterate over the contents of the ring buffer by going from begin() to end().

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!