Problem having rectangles in different places

Started by
5 comments, last by Cantos 16 years ago
Hi. My program has 2 SDL_Rect's moving on the screen. There are 3 possible positions: x = 0, x = 220 and x = 440. So I every time I check and change their positions: if they were 0 and 220 the first time, make it 0 and 440 (or 220 and 440 etc). The problem is this is not happening. This is the function that does the job:

void setPosition( std::vector< Block >& blocks, bool started )
{
	const int POS_1 = 0, POS_2 = 220, POS_3 = 440;
	bool used[3] = {false, false, false};
	bool temp[3] = {false, false, false};

/*started indicates whether we are in the main loop, ie the timer is running. This because I use this function once outside the main loop*/
	if( started )
	{
		for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )
		{
		
			switch( iter->rect->x )
			{
			case POS_1:
				used[0] = true; break;
			case POS_2:
				used[1] = true; break;
			case POS_3:
				used[2] = true; break;
			}
		}
	}
	else
	{
		blocks[0].rect->x = POS_1;
		blocks[1].rect->x = POS_2;
	}

/*this is the function that 'creates' temp. It makes sure that different positions are taken. I have tested this function in a separate program without problems.*/
	int trues = 0;
	do
	{
		for( int i = 0; i < 3; ++i )
		{
			used = false;
			temp = false;
		}
		trues = 0;
		for( int i = 0; i < 3; ++i )
		{
			int j = rand() % 2;
			if( j == 0 )
				temp = true;
			else
				temp = false;

			if( temp )
				++trues;
		}

	}while( trues != 2 || equal( used, temp ) );
 /*give the blocks their positions*/
	for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )
	{
		for( int i = 0; i < 3; ++i )
		{
			if( temp )
			{
				if( i == 0 )
					iter->rect->x = POS_1;
				if( i == 1)
					iter->rect->x = POS_2;
				if( i == 2 )
					iter->rect->x = POS_3;
				
				temp = false;
				break;
			}
		}
		iter->rect->y = 0;
	}
}


bool equal( const bool* const used, const bool* const temp )
{
	return( used[0] == temp[0] && used[1] == temp[1] && used[2] == used[2] );
}

Well this is it. As I've said (in the code) the piece of code that fills the 'temp' array is ok. The problem must be somewhere else.. Thanks for your help, Sheep19.
Advertisement
One problem is that it always sets all of the elements to used to 0, so the check you do at the start of the function that sets them has no effect. So it won't know what blocks were used previously. Also for your start case you just set the position of the rects, but don't tell it that those spots are now used.

The loop that you have seems illogical, because it has to keep on trying until it happens to select 2 spots. Why not just do something like pick the unused spot, then pick one of the remaining two spots that aren't used? The way you have it makes it technically possible for an infinite loop (won't happen in practice).

It would also make more sense to have the positions in an array, rather than individual variables.

Another thing is that your function that checks for array equality is ugly(and also wrong, you have it checking used[2] == used[2] rather than used[2]==temp[2] ), it would make more sense to make a general purpose array equality function using templates that takes two arrays of the same types and then loops through the elements.

[Edited by - Mr_Threepwood on March 26, 2008 3:20:04 PM]
void setPosition( std::vector< Block >& blocks, bool started )


Don't do that. Use something separate to set the initial position. Like, for example, constructor parameters for the blocks that you put into the vector the first time.

	else	{		blocks[0].rect->x = POS_1;		blocks[1].rect->x = POS_2;	}


And here's why. What happens is, when 'started' is true, you use this code to set the start positions of the blocks, but then you go on ahead and do the rest of the function anyway, scrubbing out those values.

			if( j == 0 )				temp = true;			else				temp = false;


This is needlessly complicated. You can map integers to booleans directly:

temp = static_cast<bool>(j);


But in fact, we can do much, much better if we first take a step back and write out what we want to do in pseudocode, and then select the appropriate tools.

The x position 0 corresponds to column 0; 220 to 1; 440 to 2.That is, we can multiply the column index by 220 to get its pixel position, anddivide pixel position by 220 to get the column.Repeat:  Select as many columns as we have blocks.until our selection does not exactly correspond to the current positions.Put each block into the corresponding column, by setting its pixel position.


Now, you already know how to check if a two sequences of values are identical: with std::equal. Oh, wait, that's not std::equal you're using, judging by the arguments, but instead a function you whipped up quickly yourself to compare arrays of 3 booleans. Well, never mind that; the standard library provides a much more powerful comparer. :)

It so happens that the library also provides a function to select a specific number of elements from a sequence randomly: it's called random_sample. (Well, actually that will fill some output range with a sample, according to the size of that output range; but it's easy enough for us to just make a vector of the desired size and then fill the entire vector from .begin() to .end().) All we need to do let our values be column-indices, rather than Blocks. And then, the sequence we're "sampling" is just a set of numbers from 0 to N-1 in order. (The original STL provided a function to create that, too, but unfortunately it's one of the few things that didn't make it into the C++ standard library.)

It also provides a way to fill an output range with values corresponding to an input range, given the correspondence: it's called std::transform. We can define the process "given a block, figure out what column it's in" (trivially), and use that to get a vector giving the current column-indices.

The rest pretty much writes itself...

const int COLUMN_WIDTH = 220;int columnOf(Block& b){	return b.rect->x / 220;}std::vector<Block> initialBlocks(){	// Instead of trying to setPosition() outside the main loop,	// we us a function that creates the initial vector with everything	// already in position.	std::vector<Block> result(2);	result[0].rect->x = 0;	result[1].rect->x = 1 * COLUMN_WIDTH;	// or something like that, anyway.	return result;}void setPosition(std::vector<Block>& blocks){	std::vector<int> current_positions(blocks.size());	std::transform(blocks.begin(), blocks.end(), current_positions.begin(), &columnOf);	// For a small, known number of columns, this is easiest:	int all_positions[3] = {0, 1, 2};	// We have serious problems if there are more blocks than columns!	assert(blocks.size() <= 3);	std::vector<int> new_positions(blocks.size());	do {		std::random_sample(			all_positions, all_positions + 3,			new_positions.begin(), new_positions.end()		);	} while (std::equal(new_positions.begin(), new_positions.end(),	                    current_positions().begin));	// Now we have two "parallel" vectors to iterate over... in C++,	// this is probably easiest with an old-fashioned for-loop	for (int i = 0; i < blocks.size(); ++i)	{		blocks.rect->x = COLUMN_WIDTH * new_positions;		blocks.rect->y = 0;	}}


Yes, It's Really That Simple(TM).



We could probably do even better with a better-designed Block class, though.

In particular, why do you have a pointer to a rect? Why not just put the rect into the Block directly? Are you trying to share them or something?
I didn't have the time to do this, so I did it now.

I get some errors on in setPosition function:

<code>
1>------ Build started: Project: Blocks, Configuration: Debug Win32 ------
1>Compiling...
1>main.cpp
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(207) : error C3861: 'assert': identifier not found
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(211) : error C2039: 'random_sample' : is not a member of 'std'
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(211) : error C3861: 'random_sample': identifier not found
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(216) : error C2064: term does not evaluate to a function taking 0 arguments
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(216) : error C2228: left of '.begin' must have class/struct/union
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(216) : error C2780: 'bool std::equal(_InIt1,_InIt1,_InIt2,_Pr)' : expects 4 arguments - 3 provided
1> c:\program files\microsoft visual studio 8\vc\include\xutility(2714) : see declaration of 'std::equal'
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(220) : warning C4018: '<' : signed/unsigned mismatch
1>Build log was saved at "file://f:\My Documents\Visual Studio 2005\Projects\Blocks\Blocks\Debug\BuildLog.htm"
1>Blocks - 6 error(s), 1 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
</code>
Quote:Original post by sheep19
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(207) : error C3861: 'assert': identifier not found


It comes from <cassert>.

Quote:1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(211) : error C2039: 'random_sample' : is not a member of 'std'
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(211) : error C3861: 'random_sample': identifier not found


It comes from <algorithm>.

Quote:1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(216) : error C2064: term does not evaluate to a function taking 0 arguments
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(216) : error C2228: left of '.begin' must have class/struct/union


I misplaced the parentheses. I'm sure you can figure this one out for yourself.

Quote:1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(216) : error C2780: 'bool std::equal(_InIt1,_InIt1,_InIt2,_Pr)' : expects 4 arguments - 3 provided
1> c:\program files\microsoft visual studio 8\vc\include\xutility(2714) : see declaration of 'std::equal'


Er, no, it doesn't. (Or at least, it needn't.) The compiler must have gotten confused after the previous error.

Quote:1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(220) : warning C4018: '<' : signed/unsigned mismatch


So, .size() of a vector returns an unsigned int (size_t), and that gets compared to the loop counter. What type should the loop counter be instead? :)
Thanks for your help, but I still get (pretty much) the same errors. I have fixed
the parentheses and included the headers.

void setPosition(std::vector<Block>& blocks){	std::vector< int > current_positions( blocks.size() );	std::transform(blocks.begin(), blocks.end(), current_positions.begin(), &columnOf );	// For a small, known number of columns, this is easiest:	int all_positions[3] = {0, 1, 2};	// We have serious problems if there are more blocks than columns!	assert( blocks.size() <= 3 );	std::vector< int > new_positions( blocks.size() );	do 	{		std::random_sample( all_positions, all_positions + 3, new_positions.begin(), new_positions.end() ); /*1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(190) : error C2039: 'random_sample' : is not a member of 'std' & 1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(190) : error C3861: 'random_sample': identifier not found*/	} while( std::equal(new_positions.begin(), new_positions.end(), current_positions().begin() ) ) ; /*1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(191) : error C2064: term does not evaluate to a function taking 0 arguments & 1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(191) : error C2228: left of '.begin' must have class/struct/union & 1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(191) : error C2780: 'bool std::equal(_InIt1,_InIt1,_InIt2,_Pr)' : expects 4 arguments - 3 provided*/	// Now we have two "parallel" vectors to iterate over... in C++,	// this is probably easiest with an old-fashioned for-loop	for ( unsigned int i = 0; i < blocks.size(); ++i)	{		blocks.rect->x = COLUMN_WIDTH * new_positions;		blocks.rect->y = 0;	}}


1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(190) : error C2039: 'random_sample' : is not a member of 'std'
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(190) : error C3861: 'random_sample': identifier not found
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(191) : error C2064: term does not evaluate to a function taking 0 arguments
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(191) : error C2228: left of '.begin' must have class/struct/union
1>f:\my documents\visual studio 2005\projects\blocks\blocks\main.cpp(191) : error C2780: 'bool std::equal(_InIt1,_InIt1,_InIt2,_Pr)' : expects 4 arguments - 3 provided
1> c:\program files\microsoft visual studio 8\vc\include\xutility(2714) : see declaration of 'std::equal'

I think I'm still doing something wrong.
After some quick googling ...

random_sample does not appear to be part of the standard C++ library. My code reading powers don't seem to be as good as Zahlman's, but I think this will do what you want

	/* snip */	// We have serious problems if there are more blocks than columns!	assert( blocks.size() <= 3 );	std::vector< int > new_positions( blocks.size() );	// copy once	std::copy( all_positions, all_positions + 3, new_positions.begin());	do 	{		// do a shuffle in the loop ...		std::random_shuffle(new_positions.begin(),new_positions.end());	} while( std::equal(new_positions.begin(), new_positions.end(), current_positions.begin() ) ) ;	// Now we have two "parallel" vectors to iterate over... in C++,	// this is probably easiest with an old-fashioned for-loop	/* snip */

This topic is closed to new replies.

Advertisement