Battleship: place ships [closed] - c

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
I wrote a battleship game on AVR device. Everything works just fine, only placing ships on game map causes little problems. Sometimes ships are placed next to each others even if I thought my code would prevent that. Have tried to debug it now for two days. Thought to just place the code of the place_ships -function here if someone of you could notice where I'm wrong with it.
int data_map[10][10]; //This has the data of where ships are, where player has shot etc.
char game_map[10][10]; //This is printed in UI. O for miss, X for hit.
int ship_sizes[] = {0,1,2,3,3,4,5};
int ships[] = {0,1,2,3,4,5,6};
void place_ships() { //Places the ships in the game map.
int other_ships; //Variabel for counting if there are already other ships in the area where trying place the ship.
for (i=6; i>0; i--) {
while (1) {
other_ships = 0; //Initialize.
ship_direction = rand() % 10; //Get random ship direction (1-4 = horizontal, 6-10 = vertical)
top_x = (rand() % 8) + 1; //Get random x-coordinate, not from map edges
top_y = (rand() % 8) + 1; //Get random y-coordinate, not from map edges
if (ship_direction < 5) {
if ((top_x-ship_sizes[i]) > -2) { //Make sure that ship has room in game map.
for (j=(top_y-1); j<(top_y+2); j++) { //Following 2 for-loops and if-statement inside makes sure that no other ships are in
//the area where the ship is tried to place.
for (k=(top_x+1); k>(top_x-(ship_sizes[i]-2)); k--) {
if ((data_map[j][k] == 1) || (data_map[j][k] == 2) || (data_map[j][k] == 3) || (data_map[j][k] == 4) || (data_map[j][k] == 5) || (data_map[j][k] == 6)) {
other_ships = 1; //Following 2 'breaks' and 'continue' are there for the situation if
break; //there are other ships in the area, stop placing ship and get new random coordinate and try again.
}
}
if (other_ships == 1) {
break;
}
}
if (other_ships == 1) {
continue;
}
for (l=top_x; l>(top_x-ship_sizes[i]); l--) {
data_map[top_y][l] = ships[i]; //If no other ships in the area, place the ship.
}
loading(); //Wait to optimize harware working. There are known timing issues on AVR. And this
//is here to try to avoid them.
break;
}
}
else if (ship_direction > 5) {
if ((top_y-ship_sizes[i]) > -2) { //Make sure that ship has room in game map.
for (j=(top_y+1); j>(top_y-(ship_sizes[i]-2)); j--) { //Following 2 for-loops and if-statement inside makes sure that no other ships are in
//the area where the ship is tryied toplace.
for (k=(top_x-1); k<(top_x+2); k++) {
if ((data_map[j][k] == 1) || (data_map[j][k] == 2) || (data_map[j][k] == 3) || (data_map[j][k] == 4) || (data_map[j][k] == 5) || (data_map[j][k] == 6)) {
other_ships = 1; //Following 2 'breaks' and 'continue' are there for the situation if
break; //there are other ships in the area, stop placing ship and get new random coordinate and try again.
}
}
if (other_ships == 1) {
break;
}
}
if (other_ships == 1) {
continue;
}
for (l=top_y; l>((top_y-(ship_sizes[i]))); l--) {
data_map[l][top_x] = ships[i]; //If no other ships in the area, place the ship.
}
loading(); //Wait to optimize harware working. There are known timing issues on AVR. And this
//is here to try to avoid them.
break;
}
}
}
}
}
Now the original problem is solved. Still some ships aren't placed. What ships function skips, varies.

In the last variant:
for (k=(top_x+1); k>(top_x-(ship_sizes[i]-2)); k--) {
I think the condition should be:
k > ( top_x - (ship_sizes[i] + 2) ) // not -2 but + 2
because as I suppose you want to check that the ship will not overlap all the length of the ship_sizes[i] and adjacent ones. The same is for y:
top_y-(ship_sizes[i] + 2) // or top_y - ship_sizes[i] - 2
I recommend you to extract this code to the function, such as CheckOverlap, because it will simplify the code reading and debugging.
Also, move up the valuation of the condition from the loop:
int length = top_x - (ship_sizes[i] + 2);
for (k= top_x+1; k > length; k--) {
Clearer code will also be:
int IsPlaced (j,k) {
return 1 <= data_map[j][k] && data_map[j][k] <=6;
}
In general, try to avoid code duplication, placing all doubling to functions. It will reduce the doubling errors and strongly simplify the design of your program, what is good for you and for all other readers.

The tests you're doing on data_map to find other ships are reading past the end of the array. You should clean up those loop conditions. For example, top_x can be as high as 8 after being assigned (rand() % 8) + 1. Then ship_sizes[i] can be as high as 5. So top_x+(ship_sizes[i]+2) can be 15.
I'm not sure fixing that will solve your problem, though, but it's a problem of its own.
You will find that the problem is in array indexing. Arrays in C are 0-indexed, and in several places you treat them as if they're 1-indexed.

Related

How to have a turn by turn management in C?

I want to make a game; say there are 50 turns and up to 4 players.
How should the code manage the turn by turn?
For 2 player I think it is that:
if (nbr_gamers == 2)
{
if ((turn % 2) == 0)
player = 1;
else
player = 0;
}
where the turn is the position of turn.
Is that about right?
Try this :
player = turn % nbr_gamers;

Tic Tac Toe, playing with PC (random)

if (turn == tick) {
/*first player*/
Form1->Label1->Caption = "X pyr";
fields[row][kol] = 1;
Form1->BitBtn1->Glyph->LoadFromFile("tick.bmp");
turn = tack;
}
else {
do {
//random
row = rand() % 3;
kol = rand() % 3;
}
while (fields[row][kol] == 0);
/*cpu*/
Form1->Label1->Caption = "CPU";
fields[row][kol] = 2;
Form1->BitBtn1->Glyph->LoadFromFile("tack.bmp");
turn = tick;
}
}
The main problem is that when I make my move, computer just clicks on first element and after every next move it does the same.
Computer just uses first TicTacToe game board square.
If i understand correctly the fields variable contain the board with 0 for unoccupied cell, 1 for human player, 2 for CPU.
In this case the terminal condition of the while is wrong while (fields[row][kol] == 0);, you must loop when the cell is occupied (trying to search for free cells).
do {
//random
row = rand() % 3;
kol = rand() % 3;
}
while (fields[row][kol] != 0);
Note: you are initializing all elements of fields to 0, that don't appear in the code.
CPU player loops until it finds a row and col value which is not equal to 0. do-while loop below loops if fields[row][col] is equal to 0 meaning after the exit fields[row][col] will be different than 0.
do {
...
} while(fields[row][col] == 0)
// fields[row][col] is different than 0 here
In your case field value not equal to 0 means a square already used by human or computer, so computer does the same move every time.

While(1) works first four times that function is called, then becomes infinite. (C) [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have a tic tac toe board that I'm working on for fun and I've come across a problem. Basically I generate the two x,y coordinates randomly and then run them through a while loop. If the coordinate position hasn't been marked, then it marks the spot. Else it'll continue running and generate a new pair of coordinates. This seems to work for the first four times that the function is called, but then any subsequent uses of the function cause it to spiral out of control. Hoping someone might be able to point me in the right direction as well as tell me if logic is incorrect somewhere. Constructive criticism is great.
Thanks in advance and here's the code for the X's move function (the O has almost the same code):
void Xmove(struct config *cp) {
int ran1, ran2;
srand((unsigned) time(NULL));
ran1 = rand() % 3;
ran2 = rand() % 3;
if(cp->grid[1][1] == ' ') {
cp->grid[1][1] = 'X';
printGrid(cp);
return;
}
while(ran1 == 1 && ran2 == 1) {
ran1 = (rand() % 3);
ran2 = (rand() % 3);
}
int looper = 1;
while (looper) {
if(cp->grid[ran1][ran2] != 'O' && cp->grid[ran1][ran2] != 'X') {
cp->grid[ran1][ran2] = 'X';
printGrid(cp);
looper = 0;
}
ran1 = (rand() % 3);
ran2 = (rand() % 3);
}
}
Back to the the original question about your infinite loop and your code, You cannot make the loop break solely on the assumption of finding an empty slot and filling it. You should not be even entering the loop if there are no slots left available. In fact, you should not even call either Move() function at all if there are no open tiles to fill, and honestly that should be maintained as a decrement-counter of the config struct.
But that aside, detection of open-tiles-remaining could be done a number of ways. One is presented below that requires no other modifications to the config table, etc. This you can easily do by building a list of viable tiles, and choosing a single random entry from that. The following replaces everything past your initial check for the center slot being open.
// build a table of open tiles
int ar[9] = {0};
int n=0,i=0,j=0;
for (i=0;i<3;++i)
for (j=0;j<3;++j)
if (cp->grid[i][j] != 'O' && cp->grid[i][j] != 'X')
ar[n++] = i*3+j;
// now you have the list of available tiles
// in `ar[0..n-1]`. choose ONE via `rand()`
if (n > 0)
{
n = ar[rand()%n];
cp->grid[n/3][n%3] = 'X'; // or 'O'
}
I'd eliminate the random looping altogether, instead every time I needed to make a move I would generate a random permutation of the digits 0-9 inclusive, and use that to walk the board, looking for where to put an X or an O as needed. Something like this:
int randperm[9], i = 0;
/* fill the array randperm with the numbers 0 through 8 in
* order
*/
for(i = 0; i != 9; i++)
randperm[i] = i;
/* Now mix the numbers up, so that the array ends up in
* some "random" order: that is, generate a permutation
* of the digits 0-8.
*/
for(i = 8; i > 0; i--)
{
int j = rand() % (i+1);
int temp = randperm[j];
randperm[j] = randperm[i];
randperm[i] = temp;
}
/* Now try to find a spot. We will use our randperm array to
* determine which cell to look at. Remember that the array
* contains the numbers 0-8 in some random order.
*/
for(i = 0; i != 9; i++)
{
/* We split a number from 0-8 into two numbers (x and y),
* each from 0 to 2:
*
* 0 -> 0 0, 1 -> 1 0, 2 -> 2 0,
* 3 -> 0 1, 4 -> 1 1, 5 -> 2 1,
* 6 -> 0 2, 7 -> 1 2, 8 -> 2 2
*
* notice that (y * 3) + x = randperm[i]
*/
int x = randperm[i] % 3;
int y = randperm[i] / 3;
/* check if the spot at grid[x][y] is available, if it is
* take it and return.
*/
}
/* If we get here there's no spot to put an X or an O... board is full */

State Machine of different Variables in C

I have the following code which is doing lot of if-else check and consuming lot of time.
while(i<1000) {
if ( a == x1)
{
if(b == y1)
{
array[i] = z1;
}
else if( b = y2)
{
array[i] = z2;
}
else if (b = z3);
{
array[i] = z3;
}
-
-
-
-
-
some 20 else --
}/*end of first if*/
else if (a == xx1)
{
if(b == yy1)
{
array[i] = zz1;
}
else (b == yyy3)
{
array[i] = zz2;
}
else ( b == yy4)
{
array[i] = zz3;
}
-
-
-
-
-
some 20 else --
}
else if(/*more conditions*/)
{
/* same as above with too many else loops */
}
-
-
-
-
-
-
-some 100 else-ifs
-
-
-
-
-
i++;
}/*end of while*/
How can i minimize the time consumption.I though of a replacing if else with switch(switch within the switch) , but still i think i can do better with
some tables(2d-3d array) but the numbers x1,y1,z1 ,zz3,zz1,zz2 are not continous numbers and have no relation among them they exists between 1 to 1 billion with no
repetetion.Any thoughts for implementing this lookup table.
Rgds,
softy
(1) If the numbers are constants change it to case statement - your compiler should output a jump table if it can (given your constraints it can't - so go to step 2).
(2) If your data is non-contiguous a jump table won't work. You can make your data contiguous (or close enough to it) by using a hash table. Now just search the hash table. This should be O(1) (or close to it). Though you'll have to bear the cost of creating the hash table
If you need code for a hash table you can use C++ STL or if you need C write your own or find an implementation on the web, this question might help.

Having trouble making ships appear on the command prompt!

I building a battleship game in C. I have the following code to validate that the points of the randomly placed ship don't exceed the board limit. The code doesn't work very smoothly and the application hangs while generating the random points.
Could you recommend some optimizations to what I have?
while(1 == 1)
{
//Generates the x, y coordenates of the point
int x = rand() % 9;
int y = rand() % 9;
//Calculating the ship direction
char direction = ((rand() % 10) > 5) ? 'V' : 'H';
if(direction == 'H')
{
//Verifies that the ship placed on the acquired x coordenate of the point does not exceed the board size
//if so recalculates the value of x
while(!(((x + ships[i].length) - 1) < 10)) x = (rand() % 5);
}
else
{
//Verifies that the ship placed on the acquired y coordenate of the point does not exceed the board size
//if so recalculates the value of y
while(!(((y + ships[i].length) - 1) < 10)) y = (rand() % 5);
}
//Calculating the coordenates for each point of the ship
for(j = 0; j < ships[i].length; j++)
{
if(direction == 'H')
{
points[j].x = (x + j);
points[j].y = y;
}
else
{
points[j].x = x;
points[j].y = (y + j);
}
//Validating that the coordenate asigned to a point has not been assigned to another ship
if(verifyPos(points[j].x, points[j].y, ships, length))
{
invalid = 1;
break;
}
}
//if all the points of the ship are valid, move to the next ship
//if not recalculate the initial point and the subsequent coordenates
if(invalid == 0) break;
}
ships[i].points = points;
}
}
You need to seed the randomizer.
#include <time.h>
srand(time(NULL));
is a trivial way to do it.
Well one way of improving it would be to choose which direction the ship is going to go V or H and then only use the section of the board that it will fit into so if the ship is 4 long and verticle only use rows 1-7 (of a 10 row board) for the verticle starting point. THen you don't need to check if it will fit at all.
Looking at the code, I don't see where the points assigned in
points[j].x = x;
points[j].y = (y + j);
are cleared if a ship is invalid. As far as I can see teh points array may be filling up with invalid points.

Resources