I'm building Space Invaders in C++ (using the MBed platform) for a microcontroller. I've used a 2D Vector of object pointers to organise the invaders.
The movement algorithm is below, and runs in the main while loop for the game. Basically, I get the highest/lowest x and y values of invaders in the vector, and use those to set bounds based on screensize (the HEIGHT variable);
I also get the first invader's position, velocity, and width, which I apply changes to based on the bounds above.
Then I iterate through the whole vector again and apply all those changes. It sort of works – the invaders move – but the bounds don't seem to take effect, and so they fly off screen. I feel like I'm missing something really dumb, thanks in advance!
void Army::move_army() {
int maxy = HEIGHT - 20;
int Ymost = 0; // BOTTOM
int Yleast = 100; // TOP
int Xmost = 0; // LEFT
int Xleast = 100; // RIGHT
int first_row = _rows;
int first_column = _columns;
int firstWidth = 0;
Vector2D firstPos;
Vector2D firstVel;
for (int i = 0; i < _rows; i++) {
for (int n = 0; n < _columns; n++) {
bool state = invaders[i][n]->get_death();
if (!state) {
if (i < first_row && n < first_column) {
firstPos = invaders[i][n]->get_pos();
firstVel = invaders[i][n]->get_velocity();
firstWidth = invaders[i][n]->get_width();
}
Vector2D pos = invaders[i][n]->get_pos();
if (pos.y > Ymost) {Ymost = pos.y;} // BOTTOM
else if (pos.y < Yleast) {Yleast = pos.y;} // TOP
else if (pos.x > Xmost) {Xmost = pos.x;} // LEFT
else if (pos.x < Xleast) {Xleast = pos.x;} // RIGHT
}
}
}
firstVel.y = 0;
if (Xmost >= (WIDTH - 8) || Xleast <= 2) {
firstVel.x = -firstVel.x;
firstPos.y += _inc;
// reverse x velocity
// increment y position
}
else if (Ymost > maxy) {
_inc = -_inc;
// reverse increment
}
else if (Yleast < 2) {
_inc = -_inc;
// reverse increment
}
for (int i = 0; i < _rows; i++) {
int setx = firstPos.x;
if (i > 0) {firstPos.y += 9;}
for (int n = 0; n < _columns; n++) {
invaders[i][n]->set_velocity(firstVel);
invaders[i][n]->set_pos(setx,firstPos.y);
setx += firstWidth + 2;
}
}
It looks like you have your assignment cases reversed. Assignment always goes: right <- left, so in the first case you're changing the YMost value, not pos.y. It looks like if you swap those four assignments in your bounds checking it should work. Good luck!
I am trying to separate my collision detection into separate theads to improve the efficiency. The code I have now is:
/*CHECK COLLISION HERE*/
std::thread threads[6];
Vector2 birdPointsArr[6];
Vector2* birdPoints = getBirdPoints(birdPointsArr);
//check each pipe
for (int i = 0; i < 6; i++)
{
//only check pipes with viable x coords
if (pipes[i]->getX() < birdPoints[2].x + 20 && pipes[i]->getX() + pipes[i]->getW() > birdPoints[4].x - 20)
{
//check both upper and lower pipe
for (int j = 0; j < 2; j++)
{
Vector2 pipePointsArr[4];
Vector2* pipePoints = getPipePoints(pipePointsArr, i, j);
//only check pipes with viable y coords
if (j == 0 && birdPoints[0].y > pipePoints[2].y + 20)
{
continue;
}
if (j == 1 && birdPoints[3].y < pipePoints[0].y - 20)
{
continue;
}
threads[i] = std::thread([&]() {
//if x and y coords are viable, check collision
if (collisionDetection(birdPoints, pipePoints)) {
dead = true;
}
});
}
}
}
for (int i = 0; i < 6; i++)
{
if(threads[i].joinable())
threads[i].join();
}
My concern is that I am creating and destroying several threads each frame. I am concerned that all this creation and destruction makes my code slower than if I were to leave it synchronous; however, I don't know how to really measure that. Would it be faster if I just reverted it to serial form?
Integer Range = 1;
for(Integer k = -Range; k <= Range; ++k)
{
for(Integer j = -Range; j <= Range; ++j)
{
for(Integer i = -Range; i <= Range; ++i)
{
Integer MCID = GetCellID(&CONSTANT_BOUNDINGBOX,CIDX +i, CIDY + j,CIDZ
+ k);
if(MCID < 0 || MCID >= c_CellNum)
{
continue;
}
unsigned int TriangleNum = c_daCell[MCID].m_TriangleNum;
for(unsigned int l = 0; l < TriangleNum; ++l)
{
TriangleID=c_daCell[MCID].m_TriangleID[l];
if( TriangleID >= 0 && TriangleID < c_TriangleNum && TriangleID
!= NearestID)// No need to calculate again for the same triangle
{
CDistance Distance ;
Distance.Magnitude = CalcDistance(&c_daTriangles[TriangleID], &TargetPosition,
&Distance.Direction);
if(Distance.Magnitude < NearestDistance.Magnitude)
{
NearestDistance = Distance;
NearestID = TriangleID;
}
}
}
}
}
}
}
c_daSTLDistance[ID] = NearestDistance;
c_daSTLID[ID] = NearestID;
GetCellID is the function to return the cellid in the variable CID with CIDX,CIDY,CIDZ with its position in the 3 axes
here the above code is a function to calculate the distance ,actually STL distance between a point and the triangles of the stl. This code runs fine however the problem is it is too slow as it has large number of loops within the code. Now my concern is to optimize the loop. Is there any technique of optimizing the loops within the code?
I need to place numbers within a grid such that it doesn't collide with each other. This number placement should be random and can be horizontal or vertical. The numbers basically indicate the locations of the ships. So the points for the ships should be together and need to be random and should not collide.
I have tried it:
int main()
{
srand(time(NULL));
int Grid[64];
int battleShips;
bool battleShipFilled;
for(int i = 0; i < 64; i++)
Grid[i]=0;
for(int i = 1; i <= 5; i++)
{
battleShips = 1;
while(battleShips != 5)
{
int horizontal = rand()%2;
if(horizontal == 0)
{
battleShipFilled = false;
while(!battleShipFilled)
{
int row = rand()%8;
int column = rand()%8;
while(Grid[(row)*8+(column)] == 1)
{
row = rand()%8;
column = rand()%8;
}
int j = 0;
if(i == 1) j= (i+1);
else j= i;
for(int k = -j/2; k <= j/2; k++)
{
int numberOfCorrectLocation = 0;
while(numberOfCorrectLocation != j)
{
if(row+k> 0 && row+k<8)
{
if(Grid[(row+k)*8+(column)] == 1) break;
numberOfCorrectLocation++;
}
}
if(numberOfCorrectLocation !=i) break;
}
for(int k = -j/2; k <= j/2; k++)
Grid[(row+k)*8+(column)] = 1;
battleShipFilled = true;
}
battleShips++;
}
else
{
battleShipFilled = false;
while(!battleShipFilled)
{
int row = rand()%8;
int column = rand()%8;
while(Grid[(row)*8+(column)] == 1)
{
row = rand()%8;
column = rand()%8;
}
int j = 0;
if(i == 1) j= (i+1);
else j= i;
for(int k = -j/2; k <= j/2; k++)
{
int numberOfCorrectLocation = 0;
while(numberOfCorrectLocation != i)
{
if(row+k> 0 && row+k<8)
{
if(Grid[(row)*8+(column+k)] == 1) break;
numberOfCorrectLocation++;
}
}
if(numberOfCorrectLocation !=i) break;
}
for(int k = -j/2; k <= j/2; k++)
Grid[(row)*8+(column+k)] = 1;
battleShipFilled = true;
}
battleShips++;
}
}
}
}
But the code i have written is not able to generate the numbers randomly in the 8x8 grid.
Need some guidance on how to solve this. If there is any better way of doing it, please tell me...
How it should look:
What My code is doing:
Basically, I am placing 5 ships, each of different size on a grid. For each, I check whether I want to place it horizontally or vertically randomly. After that, I check whether the surrounding is filled up or not. If not, I place them there. Or I repeat the process.
Important Point: I need to use just while, for loops..
You are much better of using recursion for that problem. This will give your algorithm unwind possibility. What I mean is that you can deploy each ship and place next part at random end of the ship, then check the new placed ship part has adjacent tiles empty and progress to the next one. if it happens that its touches another ship it will due to recursive nature it will remove the placed tile and try on the other end. If the position of the ship is not valid it should place the ship in different place and start over.
I have used this solution in a word search game, where the board had to be populated with words to look for. Worked perfect.
This is a code from my word search game:
bool generate ( std::string word, BuzzLevel &level, CCPoint position, std::vector<CCPoint> &placed, CCSize lSize )
{
std::string cPiece;
if ( word.size() == 0 ) return true;
if ( !level.inBounds ( position ) ) return false;
cPiece += level.getPiece(position)->getLetter();
int l = cPiece.size();
if ( (cPiece != " ") && (word[0] != cPiece[0]) ) return false;
if ( pointInVec (position, placed) ) return false;
if ( position.x >= lSize.width || position.y >= lSize.height || position.x < 0 || position.y < 0 ) return false;
placed.push_back(position);
bool used[6];
for ( int t = 0; t < 6; t++ ) used[t] = false;
int adj;
while ( (adj = HexCoord::getRandomAdjacentUnique(used)) != -1 )
{
CCPoint nextPosition = HexCoord::getAdjacentGridPositionInDirection((eDirection) adj, position);
if ( generate ( word.substr(1, word.size()), level, nextPosition, placed, lSize ) ) return true;
}
placed.pop_back();
return false;
}
CCPoint getRandPoint ( CCSize size )
{
return CCPoint ( rand() % (int)size.width, rand() % (int)size.height);
}
void generateWholeLevel ( BuzzLevel &level,
blockInfo* info,
const CCSize &levelSize,
vector<CCLabelBMFont*> wordList
)
{
for ( vector<CCLabelBMFont*>::iterator iter = wordList.begin();
iter != wordList.end(); iter++ )
{
std::string cWord = (*iter)->getString();
// CCLog("Curront word %s", cWord.c_str() );
vector<CCPoint> wordPositions;
int iterations = 0;
while ( true )
{
iterations++;
//CCLog("iteration %i", iterations );
CCPoint cPoint = getRandPoint(levelSize);
if ( generate (cWord, level, cPoint, wordPositions, levelSize ) )
{
//Place pieces here
for ( int t = 0; t < cWord.size(); t++ )
{
level.getPiece(wordPositions[t])->addLetter(cWord[t]);
}
break;
}
if ( iterations > 1500 )
{
level.clear();
generateWholeLevel(level, info, levelSize, wordList);
return;
}
}
}
}
I might add that shaped used in the game was a honeycomb. Letter could wind in any direction, so the code above is way more complex then what you are looking for I guess, but will provide a starting point.
I will provide something more suitable when I get back home as I don't have enough time now.
I can see a potential infinite loop in your code
int j = 0;
if(i == 1) j= (i+1);
else j= i;
for(int k = -j/2; k <= j/2; k++)
{
int numberOfCorrectLocation = 0;
while(numberOfCorrectLocation != i)
{
if(row+k> 0 && row+k<8)
{
if(Grid[(row)*8+(column+k)] == 1) break;
numberOfCorrectLocation++;
}
}
if(numberOfCorrectLocation !=i) break;
}
Here, nothing prevents row from being 0, as it was assignd rand%8 earlier, and k can be assigned a negative value (since j can be positive). Once that happens nothing will end the while loop.
Also, I would recommend re-approaching this problem in a more object oriented way (or at the very least breaking up the code in main() into multiple, shorter functions). Personally I found the code a little difficult to follow.
A very quick and probably buggy example of how you could really clean your solution up and make it more flexible by using some OOP:
enum Orientation {
Horizontal,
Vertical
};
struct Ship {
Ship(unsigned l = 1, bool o = Horizontal) : length(l), orientation(o) {}
unsigned char length;
bool orientation;
};
class Grid {
public:
Grid(const unsigned w = 8, const unsigned h = 8) : _w(w), _h(h) {
grid.resize(w * h);
foreach (Ship * sp, grid) {
sp = nullptr;
}
}
bool addShip(Ship * s, unsigned x, unsigned y) {
if ((x <= _w) && (y <= _h)) { // if in valid range
if (s->orientation == Horizontal) {
if ((x + s->length) <= _w) { // if not too big
int p = 0; //check if occupied
for (int c1 = 0; c1 < s->length; ++c1) if (grid[y * _w + x + p++]) return false;
p = 0; // occupy if not
for (int c1 = 0; c1 < s->length; ++c1) grid[y * _w + x + p++] = s;
return true;
} else return false;
} else {
if ((y + s->length) <= _h) {
int p = 0; // check
for (int c1 = 0; c1 < s->length; ++c1) {
if (grid[y * _w + x + p]) return false;
p += _w;
}
p = 0; // occupy
for (int c1 = 0; c1 < s->length; ++c1) {
grid[y * _w + x + p] = s;
p += _w;
}
return true;
} else return false;
}
} else return false;
}
void drawGrid() {
for (int y = 0; y < _h; ++y) {
for (int x = 0; x < _w; ++x) {
if (grid.at(y * w + x)) cout << "|S";
else cout << "|_";
}
cout << "|" << endl;
}
cout << endl;
}
void hitXY(unsigned x, unsigned y) {
if ((x <= _w) && (y <= _h)) {
if (grid[y * _w + x]) cout << "You sunk my battleship" << endl;
else cout << "Nothing..." << endl;
}
}
private:
QVector<Ship *> grid;
unsigned _w, _h;
};
The basic idea is create a grid of arbitrary size and give it the ability to "load" ships of arbitrary length at arbitrary coordinates. You need to check if the size is not too much and if the tiles aren't already occupied, that's pretty much it, the other thing is orientation - if horizontal then increment is +1, if vertical increment is + width.
This gives flexibility to use the methods to quickly populate the grid with random data:
int main() {
Grid g(20, 20);
g.drawGrid();
unsigned shipCount = 20;
while (shipCount) {
Ship * s = new Ship(qrand() % 8 + 2, qrand() %2);
if (g.addShip(s, qrand() % 20, qrand() % 20)) --shipCount;
else delete s;
}
cout << endl;
g.drawGrid();
for (int i = 0; i < 20; ++i) g.hitXY(qrand() % 20, qrand() % 20);
}
Naturally, you can extend it further, make hit ships sink and disappear from the grid, make it possible to move ships around and flip their orientation. You can even use diagonal orientation. A lot of flexibility and potential to harness by refining an OOP based solution.
Obviously, you will put some limits in production code, as currently you can create grids of 0x0 and ships of length 0. It's just a quick example anyway. I am using Qt and therefore Qt containers, but its just the same with std containers.
I tried to rewrite your program in Java, it works as required. Feel free to ask anything that is not clearly coded. I didn't rechecked it so it may have errors of its own. It can be further optimized and cleaned but as it is past midnight around here, I would rather not do that at the moment :)
public static void main(String[] args) {
Random generator = new Random();
int Grid[][] = new int[8][8];
for (int battleShips = 0; battleShips < 5; battleShips++) {
boolean isHorizontal = generator.nextInt(2) == 0 ? true : false;
boolean battleShipFilled = false;
while (!battleShipFilled) {
// Select a random row and column for trial
int row = generator.nextInt(8);
int column = generator.nextInt(8);
while (Grid[row][column] == 1) {
row = generator.nextInt(8);
column = generator.nextInt(8);
}
int lengthOfBattleship = 0;
if (battleShips == 0) // Smallest ship should be of length 2
lengthOfBattleship = (battleShips + 2);
else // Other 4 ships has the length of 2, 3, 4 & 5
lengthOfBattleship = battleShips + 1;
int numberOfCorrectLocation = 0;
for (int k = 0; k < lengthOfBattleship; k++) {
if (isHorizontal && row + k > 0 && row + k < 8) {
if (Grid[row + k][column] == 1)
break;
} else if (!isHorizontal && column + k > 0 && column + k < 8) {
if (Grid[row][column + k] == 1)
break;
} else {
break;
}
numberOfCorrectLocation++;
}
if (numberOfCorrectLocation == lengthOfBattleship) {
for (int k = 0; k < lengthOfBattleship; k++) {
if (isHorizontal)
Grid[row + k][column] = 1;
else
Grid[row][column + k] = 1;
}
battleShipFilled = true;
}
}
}
}
Some important points.
As #Kindread said in an another answer, the code has an infinite loop condition which must be eliminated.
This algorithm will use too much resources to find a solution, it should be optimized.
Code duplications should be avoided as it will result in more maintenance cost (which might not be a problem for this specific case), and possible bugs.
Hope this answer helps...
I have started to study algorithms and software development and, as a small self evaluation project I have decided to write the A* search algorithm in C++. It uses Qt and OpenGL for the visual part (but that is not important).
Using mostly this source:
A* Pathfinding for Beginners
I have write a small app, however I am have found a bug that I cant fix. It appears that for some reason the parent of a node close to the wall is set to the wall.(?) And the parent of the wall is set to the the start point(?) because of the way I am storing the info.
I have used a stateMatrix[][] where
1 = entrance green;
2 = exit;
3 = wall and;
4 = path;
I have also used matrix to represent openNodes and closedNode. The closedNodes matrix is bool matrix the openNode matrix also stores some info:
The openNodes instructions are:
openNodes[100][100][6];
0 - bool open or closed
1 - F
2 - G
3 - H
4 - parentX
5 - parentY
I know that there are better ways to code this but I have not yet got to this lesson ;)
Here is the code of the astar file:
#include <math.h>
#include "apath.h"
aPath::aPath()
{
gridSize = 100;
int i, j, k;
for(i = 0; i < gridSize; i++)
for(j = 0; j < gridSize; j++)
{
stateMatrix[i][j] = 0;
for(int k = 0; k < 6; k++) openNodes[i][j][k] = 0;
closedNodes[i][j] = 0;
}
targetX = targetY =
openX = openY = entranceX = entranceY = 0;
}
void aPath::start()
{
bool testOK = false;
int G = 0;
openNodes[entranceX][entranceY][0] = 1;
openNodes[entranceX][entranceY][2] = 14;
openNodes[entranceX][entranceY][3] = euclidean(entranceX,
entranceY);
openNodes[entranceX][entranceY][1] =
openNodes[entranceX][entranceY][2] +
openNodes[entranceX][entranceY][3];
openNodes[entranceX][entranceY][4] = entranceX;
openNodes[entranceX][entranceY][5] = entranceY;
int i, j, x, y;
while(closedNodes[targetX][targetY] == 0)
{
searchLessOpen();
closedNodes[openX][openY] = 1;
openNodes[openX][openY][0] = 0;
//Check the 8 squares around
for(i = openX - 1; i <= openX + 1; i++)
for(j = openY - 1; j <= openY + 1; j++)
{
//check if the square is in the limits,
//is not a wall and is not in the closed list
if((i >= 0) && (j >= 0) &&
(i < gridSize) && (j < gridSize) &&
(stateMatrix[i][j] != 3) &&
(closedNodes[i][j] == 0))
{
//G calculus. If it is in the edge it costs more
x = i - openX + 1;
y = j - openY + 1;
if((x == 0 && y == 0) ||
(x == 0 && y == 2) ||
(x == 2 && y == 0) ||
(x == 2 && y == 2))
{
G = 14;
}
else G = 10;
//check if node is already open
if(openNodes[i][j][0] == 0)
{
openNodes[i][j][0] = 1;
openNodes[i][j][2] = G;
openNodes[i][j][3] = euclidean(i,j);
openNodes[i][j][1] = openNodes[i][j][2] + openNodes[i][j][3];
openNodes[i][j][4] = openX;
openNodes[i][j][5] = openY;
}
else //if node is open, check if this path is better
{
if(G < openNodes[i][j][2])
{
openNodes[i][j][2] = G;
openNodes[i][j][1] = openNodes[i][j][2] + openNodes[i][j][3];
openNodes[i][j][4] = openX;
openNodes[i][j][5] = openY;
}
}
}
}
}
reconstruct();
}
void aPath::reconstruct()
{
bool test = false;
int x = openNodes[targetX][targetY][4];
int y = openNodes[targetX][targetY][5];
do
{
stateMatrix[x][y] = 4;
x = openNodes[x][y][4];
y = openNodes[x][y][5];
if(x == entranceX && y == entranceY) test = true;
} while(test == false);
}
int aPath::euclidean(int currentX, int currentY)
{
int dx = targetX - currentX;
int dy = targetY - currentY;
return 10*sqrt((dx*dx)+(dy*dy));
}
void aPath::searchLessOpen()
{
int F = 1000000;
int i, j;
for(i = 0; i < gridSize; i++)
for(j = 0; j < gridSize; j++)
{
if(openNodes[i][j][0] == 1)
{
if(openNodes[i][j][1] <= F)
{
F = openNodes[i][j][1];
openX = i;
openY = j;
}
}
}
}
Does anyone know what I am doing wrong?
Thanks.
Edit: Here are some pictures:
In aPath::start(), you have:
openNodes[entranceX][entranceY][0] = 1;
openNodes[entranceX][entranceY][2] = 14;
openNodes[entranceX][entranceY][3] = euclidean(entranceX,
entranceY);
openNodes[entranceX][entranceY][3] =
openNodes[entranceX][entranceY][2] +
openNodes[entranceX][entranceY][3];
openNodes[entranceX][entranceY][4] = entranceX;
openNodes[entranceX][entranceY][5] = entranceY;
Why is there no value for subscript [1]? And why do you assign two different values to subscript [3]? Also, to be honest, the entranceX and entranceY names are too long for the job they're doing; they make the code less readable (though I'm sure you were told to use good meaningful names). For these array indexes, I'd probably use just x and y.
At the code:
//Check the 8 squares around
for(i = openX - 1; i <= openX + 1; i++)
for(j = openY - 1; j <= openY + 1; j++)
{
I would probably ensure that neither i nor j took on invalid values with code such as:
//Check the 8 squares around (openX, openY)
int minX = max(openX - 1, 0);
int maxX = min(openX + 1, gridsize);
int minY = max(openY - 1, 0);
int maxY = min(openY + 1, gridsize);
for (i = minX; i <= maxX; i++)
for (j = minY; j <= maxY; j++)
{
I am not sure whether you need to explicitly check for the case where i == openX && j == openY (the current cell); it is not one of the 8 cells around the current cell (because it is the current cell), but the other conditions may already deal with that. If not:
if (i == openX && j == openY)
continue;
I note that we have no definitions of openX and openY or a number of other non-local variables. This makes it hard to work out whether they are class member variables or global variables of some sort. We also can't see how they're initialized, nor the documentation on what they represent.
Most plausible source of trouble
In aPath::SearchLessOpen(), you have:
if(openNodes[i][j][0] == 1)
{
if(openNodes[i][j][6] <= F)
{
F = openNodes[i][j][7];
You indicated in your description that the subscripts on openNodes in the last place ranged over 0..5; your code, though, is accessing subscripts 6 and 7. This could easily lead to the sort of confusion you describe - you are accessing data out of bounds. I think this might easily be the root of your trouble. When you access openNodes[i][j][6], this is technically undefined behaviour, but the most likely result is that it is reading the same data as if you'd written openNodes[i][j+1][0] (when j < gridsize - 1). Similarly, openNodes[i][j][7] is equivalent to accessing openNodes[i][j+1][1], with the same caveats.