Snake game stops after I reach 18 points. Why? [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
Okay so this is some very basic code for a classic snake game that I wrote. I need it as a part of a bigger project I'm working on but while testing I ran into this very annoying error. If someone could just execute it and tell me what I'm missing, that'd be great. Any extra suggestions are also welcome. I've attached the code below and written some very basic comments, hope that they are enough to understand it.
The Error: My code stops after the player achieves 18 points. To be very specific, No more fruits appear on the console at score = 18.
#include<iostream>
#include<conio.h>
#include<vector>
#include<queue>
#include<utility>
#include<windows.h>
using namespace std;
bool play = true;
int score = 0;
class Arena
{
private:
int length, w, h, size, fx, fy;
char snakerep, fruitrep, dir;
queue<pair<int,int>> snakey, marker;
vector<vector<char>> arena;
public:
Arena()
{
//snake stuff
length = 1;
snakerep = 'O';
for(int i = 0 ; i < length ; i++)
snakey.push({1, i+1});
dir = 'R';
//arena stuff
size = 25;
h = size, w = 2*size;
arena.resize(h, vector<char>(w, ' '));
//fruit stuff
fruitrep = 'F';
fx = 1 + (rand() % h);
fy = 1 + (rand() % w);
arena[fx][fy] = fruitrep;
}
void setFruit()
{
fx = 1 + (rand() % h);
fy = 1 + (rand() % w);
arena[fx][fy] = fruitrep;
}
void setArena()
{
for(int i = 0 ; i < w ; i++)
{
arena[0][i] = '*';
arena[h-1][i] = '*';
}
for(int i = 1 ; i < h-1 ; i++)
{
arena[i][0] = '*';
arena[i][w-1] = '*';
}
for(int i = 1 ; i < h-1; i++)
{
for(int j = 1; j < w-1 ; j++)
{
arena[i][j] = ' ';
}
}
arena[fx][fy] = fruitrep;
}
void setSnake()
{
queue<pair<int,int>> temp_snakey = snakey;
while(!temp_snakey.empty())
{
pair<int, int> xy = temp_snakey.front();
temp_snakey.pop();
int x = xy.first, y = xy.second;
//condition for hitting any of the boundaries
if (x <= 0 || y <= 0 || x >= size-1 || y >= 2*size-1)
play = false;
//condition for hitting itself
if (arena[x][y] == snakerep)
play = false;
//condition for if a fruit is eaten
if (arena[x][y] == fruitrep)
{
setFruit();
//increase length of the snake
length++, score++;
marker.push({x,y});
}
arena[x][y] = snakerep;
}
}
void moveSnake(char dir)
{
//calculate the new location of the head or whatever according to the direction given within the brackets
pair<int,int> prevHead = snakey.back(), newHead;
if (dir == 'R')
newHead = {prevHead.first, prevHead.second+1};
else if (dir == 'L')
newHead = {prevHead.first, prevHead.second-1};
else if (dir == 'U')
newHead = {prevHead.first-1, prevHead.second};
else if (dir == 'D')
newHead = {prevHead.first+1, prevHead.second};
//pop the tail from the queue and add the new head
pair<int, int> tail = snakey.front();
if (!marker.empty() && tail == marker.front())
marker.pop();
else
snakey.pop();
snakey.push(newHead);
}
void printAll()
{
//prints the matrix
for(int i = 0 ; i < h ; i++)
{
for(int j = 0 ; j < w ; j++)
{
cout<<arena[i][j];
}
cout<<endl;
}
}
void getDirection()
{
if(_kbhit())
{
char ch = _getch();
if (dir == 'R' || dir == 'L')
{
if (ch == 'w')
dir = 'U';
else if (ch == 's')
dir = 'D';
else if (ch == 'x')
play = false;
}
else if (dir == 'U' || dir == 'D')
{
if (ch == 'd')
dir = 'R';
else if (ch == 'a')
dir = 'L';
else if (ch == 'x')
play = false;
}
}
}
void playGame()
{
getDirection();
setArena();
moveSnake(dir);
setSnake();
printAll();
}
};
int main()
{
Arena A;
while(play)
{
A.playGame();
cout<<" SCORE = "<<score<<endl;
//better than using system("cls"), because it moves cursor to beginning of console, flickering stops
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0, 0});
}
return 0;
}

The 18th fruit is getting generated out of bounds. Your random location function is incorrect. You have
void setFruit()
{
fx = 1 + (rand() % h);
fy = 1 + (rand() % w);
arena[fx][fy] = fruitrep;
}
Okay, consider fx, rand() % h will give you a number in [0 .. h-1] inclusive but you are adding one to it because the zeroth position is a wall. So now you have [1..h] but h is out of bounds. Also there is a wall on the right too. So putting that all together, I think what you want is
void setFruit()
{
fx = 1 + (rand() % (h-2));
fy = 1 + (rand() % (w-2));
arena[fx][fy] = fruitrep;
}
Also you should change the Arena constructor to use this function. It is good to not repeat bits of code like that.

Related

Tic tac toe Minimax Algorithm Having Weird Behavior (C++)

The other day, I wrote a console game of Tic-Tac-Toe in c++ for my son. He wanted me to add a computer, and I ended us using the minimax algorithm for the first time. I did some quick testing, but really just gave my laptop to my son as soon as it was printing stuff, who played with it for a couple minuets. I looked over his sholder once or twice, and noticed that it wasn't playing optimally, iv'e been trying to debug it, but I can't see where it goes wrong. I tried getting rid of alpha beta prunning, but that did not change anything.
For context, on the board the computer is -1, blank is 0, and the player is 1.
Here is the minimax function:
int minimax(int board[9], int depth, int alpha, int beta, bool isMaxizimaizingPlayer)
{
bool found = false;
for (int i = 0; i < 9; i++)
{
if (board[i] == 0)
{
found = true;
}
}
if (!found)
{
return eval(board);
}
if (depth == 0 || eval(board) != 0)
{
return eval(board);
}
if (isMaxizimaizingPlayer)
{
int maxEval = -2;
for (int spot = 0; spot < 9; spot++)
{
if (board[spot] == 0)
{
board[spot] = 1;
int e = minimax(board, depth - 1, alpha, beta, false);
if (e > maxEval)
{
maxEval = e;
}
//if (beta < alpha)
//{
// break;
//}
board[spot] = 0;
}
}
return maxEval;
}
else {
int minEval = 2;
for (int spot = 0; spot < 9; spot++)
{
if (board[spot] == 0)
{
board[spot] = -1;
int e = minimax(board, depth - 1, alpha, beta, true);
if (e < minEval)
{
minEval = e;
}
//if (beta < alpha)
//{
// break;
//}
board[spot] = 0;
}
}
return minEval;
}
}
To be compleate, here is my eval function:
int eval(int board[9])
{
/*horizontial*/
for (int i = 0; i < 3; i++)
{
if (board[i * 3] == board[i * 3 + 1] && board[i * 3 + 2] == board[i * 3] && board[i * 3] != 0)
{
return board[i * 3];
}
}
/*vertical*/
for (int i = 0; i < 3; i++)
{
if (board[i] == board[i + 3] && board[i] == board[i + 6] && board[i] != 0)
{
return board[i];
}
}
/*Both diags*/
if (board[4] != 0) {
if (board[0] == board[4] && board[0] == board[8])
{
return board[4];
}
if (board[2] == board[4] && board[4] == board[6])
{
return board[4];
}
}
return 0;
}
And here is the inital call:
int spot = 0;
int minEval = 2;
for (int i = 0; i < 9; i++)
{
if (board[i] == 0)
{
board[i] = -1;
int score = minimax(board, 3, -2, 2, false);
if (score < minEval) {
minEval = score;
spot = i;
}
board[i] = 0;
}
}
std::cout << "The computer went in spot " << spot + 1 << std::endl;
board[spot] = -1;
printBoard(board);
It looks like you only call minimax with a depth of three, so the algorithm will only look up to three moves ahead, if you want optimal play you need to set the depth to > 9, so that the agent is always looking ahead to the end of the game.

Conway's Game of Life help me understand this unexpected output

I would like some help understanding why my program is printing a grid of
....................
....................
....................
....................
....................
...............OOOOO
OOOOOOOOOOOOO.......
....................
....................
....................
The correct output would be so:
....................
....................
....................
....................
....................
....................
....................
.............O.O....
..............OO....
..............O.....
The way I wrote it is to create a copy of the old state and manipulate it using the rules of the game. After I check every cell, I store count of the number of neighbors alive for that cell. IF the count is greater than 3 or less than two, the cell will die.
If a cell has a count of 2 or 3 neighbors, it remains alive.
If a dead cell has a count of 3, it becomes alive.
These rules are directly applied to the copy version instead of the old and then print the copy.
I've tried using a debugger but I'm still unsure of how to use it properly. I haven't notice any red flags as of yet.
Here's my code:
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
vector<vector<bool> > world = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
void generate(const vector<vector<bool> >&g,vector<vector<bool> >&newworld)
{
int count = 0;
newworld = g;
for(size_t i = 0; i < g.size();i++) {
for(size_t j = 0; j < g[i].size();j++) {
int x = g.size(); //I rows
int y = g[i].size(); //J columns
//wrap the edges with formula (x+n)%n where n = NumOfRows or NumOfCol
if(g[(((i+1)+x)%x)][(((j-1)+y)%y)]==true){//top left
count++;
}
else if(g[(((i+1)+x)%x)][j]==true){//top middle
count++;
}
else if(g[(((i+1)+x)%x)][(((j+1)+y)%y)]==true){//top right
count++;
}
else if(g[i][(((j-1)+y)%y)]==true){//left cell
count++;
}
else if(g[i][(((j+1)+y)%y)]==true){//right cell
count++;
}
else if(g[(((i-1)+x)%x)][(((j-1)+y)%y)]==true){ //bottom left
count++;
}
else if(g[(((i-1)+x)%x)][j]==true){//bottom middle
count++;
}
else if(g[(((i-1)+x)%x)][(((j+1)+y)%y)]==true){//bottom right
count++;
}
if (g[i][j]) {
if(count > 3 || count < 2) {//if alive cell has more than 3 or less than 2, die
newworld[i][j] = false;
}
else if (count == 2 || count == 3) { //remain the same
newworld[i][j] = g[i][j];
}
}
else if (g[i][j] == false) {//dead come alive
if(count == 3) {
newworld[i][j] = true;
}
}
}
}
}
void display(vector<vector<bool> >&a)
{
for(size_t row = 0; row <a.size(); row++) {
for(size_t column = 0; column <a[row].size(); column++){
if (a[row][column]) {
cout << 'O';
}
else {
cout << '.';
}
}
cout << '\n';
}
}
int main()
{
vector<vector<bool> > newworld;
generate(world,newworld);
display(newworld);
return 0;
}
The function generate has (at least) two problem.
count is initialized outside the nested loops, so it's never reset to zero (as it should, for every cell) and keeps growing.
All the conditions are mutually exclusive, so whenever one it's met, the others are skipped. There shouldn't be any else if, but only ifs.
Keeping the data structure you chose, you can rewrite that function as
using gof_t = std::vector<std::vector<bool>>;
void generate(gof_t& g, gof_t& newworld)
{
for(size_t i = 0, x = g.size(); i < x; i++)
{
for(size_t j = 0, y = g[i].size(); j < y; j++)
{
size_t i_prev = (i + x - 1) % x;
size_t i_next = (i + 1) % x;
size_t j_prev = (j + y - 1) % y;
size_t j_next = (j + 1) % y;
int count = g[i_prev][j_prev] + g[i_prev][j] + g[i_prev][j_next]
+ g[i ][j_prev] + g[i ][j_next]
+ g[i_next][j_prev] + g[i_next][j] + g[i_next][j_next];
newworld[i][j] = g[i][j] ? (count == 2 || count == 3) : (count == 3);
}
}
std::swap(g, newworld); // <-- Passing by non const reference, we can swap without copying
}
Live (pun intended), here.

Run-Time Check Failure #2 Stack around the variable 'maze' were corrupted

Run-Time Check Failure #2 Stack around the variable 'maze' were corrupted.
Whenever I compile and run my program, I receive this error whenever the program finishes running. I believe the problem is happening in my addPaths function in my implementation. I posted all my code just in case. This program is creating a maze and the addPaths function is "digging" the paths for the user to move through. The path direction is chosen at random and the paths are only drawn at even number spaces on the maze.
HEADER:
const int HEIGHT = 3;
const int WIDTH = 5;
class Coordinate
{
public:
int row, column;
};
class Maze
{
public:
Maze();
~Maze();
void buildMaze();
void displayMaze();
void addPaths();
void startGame();
void movePlayer(int);
bool solved();
int getKey();
void addDestinationToGrid();
private:
char grid[WIDTH][HEIGHT];
Coordinate player;
const static char PLAYER = 'P';
const static char DESTINATION = 'X';
const static char START = 'S';
const static char PATH = ' ';
const static char WALL = (char)219;
};
IMPLEMENTATION:
#include <iostream>
#include "maze.h"
#include <windows.h>
#include <stack>
using std::cout;
using std::endl;
Maze::Maze()
{
buildMaze();
}
Maze::~Maze()
{
cout << "yay";
}
void Maze::buildMaze()
{
for (int x = 0; x <= HEIGHT-1; x++)
{
for (int y = 0; y <= WIDTH-1; y++)
{
grid[x][y] = WALL;
//SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
}
}
}
void Maze::displayMaze()
{
for (int x = 0; x <= HEIGHT-1; x++)
{
for (int y = 0; y <= WIDTH-1; y++)
{
cout << grid[x][y];
}
cout << endl;
}
}
void Maze::startGame()
{
int input;
do
{
input = getKey();
movePlayer(input);
} while (!solved());
}
bool Maze::solved()
{
return true;
}
void Maze::movePlayer(int direction)
{
if (direction == VK_UP || direction == VK_DOWN || direction == VK_LEFT || direction == VK_RIGHT)
{
COORD newCoord = { player.column + 1, player.row + 1 };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), newCoord);
if (grid[player.row][player.column] == START)
{
cout << START;
}
else
{
cout << PATH;
}
}
}
int Maze::getKey()
{
int result = 0;
while (!solved() && result == 0)
{
short MAX_SHORT = 0x7FFF; //111111111111111
if (GetAsyncKeyState(VK_LEFT) & MAX_SHORT)
{
result = VK_LEFT;
}
else if (GetAsyncKeyState(VK_UP) & MAX_SHORT)
{
result = VK_UP;
}
else if (GetAsyncKeyState(VK_RIGHT) & MAX_SHORT)
{
result = VK_RIGHT;
}
else if (GetAsyncKeyState(VK_DOWN) & MAX_SHORT)
{
result = VK_DOWN;
}
}
return result;
}
void Maze::addPaths()
{
Coordinate currentLocation;
Coordinate startLocation;
//Coordinate endLocation; //not used yet
std::stack<Coordinate> myStack;
currentLocation.row = (((rand() % HEIGHT) / 2) * 2);
currentLocation.column = (((rand() % WIDTH) / 2) * 2);
startLocation = currentLocation;
grid[currentLocation.row][currentLocation.column] = START;
player = currentLocation;
do
{
bool canMoveUp = !(currentLocation.row == 0 || grid[currentLocation.row - 2][currentLocation.column] != WALL);
bool canMoveDown = !(currentLocation.row == HEIGHT - 1 || grid[currentLocation.row + 2][currentLocation.column] != WALL);
bool canMoveLeft = !(currentLocation.column == 0 || grid[currentLocation.row][currentLocation.column - 2] != WALL);
bool canMoveRight = !(currentLocation.column == WIDTH - 1 || grid[currentLocation.row][currentLocation.column + 2] != WALL);
if (canMoveUp || canMoveDown || canMoveLeft || canMoveRight)
{
myStack.push(currentLocation);
//choose random location to dig
bool moveFound = false;
while (moveFound != true)
{
int direction = rand() % 4;
if (direction == 0 && canMoveUp)
{
moveFound = true;
grid[currentLocation.row - 2][currentLocation.column] = PATH;
grid[currentLocation.row - 1][currentLocation.column] = PATH;
currentLocation.row -= 2;
}
else if (direction == 1 && canMoveDown)
{
moveFound = true;
grid[currentLocation.row + 2][currentLocation.column] = PATH;
grid[currentLocation.row + 1][currentLocation.column] = PATH;
currentLocation.row += 2;
}
else if (direction == 2 && canMoveLeft)
{
moveFound = true;
grid[currentLocation.row][currentLocation.column - 2] = PATH;
grid[currentLocation.row][currentLocation.column - 1] = PATH;
currentLocation.column -= 2;
}
else if (direction == 3 && canMoveRight)
{
moveFound = true;
grid[currentLocation.row][currentLocation.column + 2] = PATH;
grid[currentLocation.row][currentLocation.column - 2] = PATH;
currentLocation.column += 2;
}
}
}
else if (!myStack.empty())
{
currentLocation = myStack.top();
myStack.pop();
}
}
while (!myStack.empty());
addDestinationToGrid();
}
void Maze::addDestinationToGrid()
{
int randomRow = rand() % HEIGHT;
int randomColumn = rand() % WIDTH;
while (grid[randomRow][randomColumn] != PATH)
{
randomRow = rand() % HEIGHT;
randomColumn = rand() % WIDTH;
}
grid[randomRow][randomColumn] = DESTINATION;
}
MAIN:
#include <iomanip>
#include <iostream>
#include "maze.h"
using namespace std;
int main()
{
Maze maze;
maze.addPaths();
maze.displayMaze();
maze.startGame();
/*if (maze.solved())
{
cout << " You Win!";
}*/
}
There are two problems. One is that you are not consistent in the order you access elements of grid. It is declared grid[WIDTH][HEIGHT] but most (but not all) accesses use a HEIGHT based index first. This isn't causing your problems since WIDTH is greater than HEIGHT and you stay within the object's memory when doing normal accesses to it.
The problem is this line:
grid[currentLocation.row][currentLocation.column - 2] = PATH;
in the moveRight handler. The column offset should be + 1, not - 2. The way it is can cause you to write to memory before the first element of grid.

Turtle Graphics Program

#include <iostream>
#include <fstream>
using namespace std;
void printArray(void);
char grid[6][34] = {'0'}; // empty floor
void movement(int command);
int direction = 2; // 1 for north, 2 for east, 3 for south, 4 for west. Turtle starts facing east.
void turtleMovement(int, int);
int spacesMoved; // used after F command.
int yCoord = 0; // Y position assumed 0;
int xCoord = 0; // X position assumed 0;
bool pen = 0; // assume pen is up
void initialize(void);
char command; // U for pen up, D for pen Down, R for right turn, L for left turn, F for forward movement with spaces moved, P for array print, and Q for end.
int main(){
initialize();
ifstream infile;
infile.open("commands.txt");
infile >> command;
while(command != 'Q'){
if(command == 'U'){
pen = 0;
}
else if(command == 'D'){
pen = 1;
}
if(command == 'R' || command == 'L'){
movement(command);
}
if(command == 'F'){
infile >> spacesMoved;
turtleMovement(yCoord, xCoord);
}
else if(command == 'P')
printArray();
infile >> command;
}
}
//=====================================================================================================================================================
//
// Function Name: intialize
//
// Pre: none
//
// Post: sets every element of the array grid to all zeroes.
//
//======================================================================================================================================================
void initialize()
{
for(int i = 0; i < 6; i++){ //
for(int j = 0; j < 34; j++){ // Initialization of Array to all zeros.
grid[i][j] = '0'; //
}
}
}
//=====================================================================================================================================================
//
// Function Name: printArray
//
// Pre: none
//
// Post: prints out the array grid to the console
//
//======================================================================================================================================================
void printArray()
{
for(int i = 0; i < 6; i++)
for(int j = 0; j < 34; j++){
grid[i][j];
cout << grid[i][j] << (j == 33 ? "\n" : ""); // if the row gets to 33 characters a new line will be made.
}
}
void movement(int command)
{
if (direction == 2){ // direction is east
if (command == 'R')
direction = 3;// if you're facing east and you turn right you're now facing south
else if (command == 'L') // if you're facing east and you turn left you're now facing north.
direction = 1;
}
else if (direction == 3) // direction is south
{
if (command == 'R') // turn right to go west
direction = 4;
else if (command == 'L') // turn left to go east
direction = 2;
}
else if (direction == 4) // direction is west
{
if (command == 'R') // right for north
direction = 1;
else if (command == 'L') // left for south
direction = 3;
}
else if (direction == 1) // direction is north
{
if (command == 'R') // right for east
direction = 2;
else if (command == 'L') // left for west
direction = 4;
}
}
void turtleMovement(int y, int x){
if(direction == 2){
int i = y;
for(int j = x; j < x + spacesMoved; j++){
if(pen == 1)
grid[i][j] = '1';
else
grid[i][j] = '0';
}
xCoord += spacesMoved - 1; //updates the x coordinate
}
else if(direction == 3){
for(int i = y; i < y + spacesMoved; i++){
int j = x;
if(pen == 1)
grid[i][j] = '1';
else
grid[i][j] = '0';
}
yCoord += spacesMoved - 1; //updates the y coordinate
}
else if(direction == 4){
int i = y;
for(int j = x; j > x - spacesMoved; j++){
if(pen == 1)
grid[i][j] = '1';
else if (pen == 0)
grid[i][j] = '0';
}
xCoord -= spacesMoved - 1; //updates the x coordinate
}
else if(direction == 1){
for(int i = y; i > y- spacesMoved; i++){
int j = x;
if(pen == 1)
grid[i][j] = '1';
else
grid[i][j] = '0';
}
yCoord -=spacesMoved - 1; //updates the y coordinate
}
}
I'm having extreme frustration with my program, when I try and do a 180 turn, i.e. a R R or a L L the whole program just freezes. Is it something with my logic? or a syntax? because it doesn't give me any of those errors.
Sorry, but your program is annoying me and it is a little hard to read.
Try this:
struct Position
{
int x;
int y;
};
struct Direction_Vector : Position
{
unsigned int direction;
};
const unsigned int MAX_DIRS = 4u;
void movement(char command,
const Direction_Vector present_dir,
Direction_Vector& new_dir)
{
unsigned int dir = present_dir.direction;
switch (command)
{
case 'R': // rotate clockwise;
dir = (dir + 1) % MAX_DIRS;
break;
case 'L': // rotate counter clockwise
dir = (dir + (MAX_DIRS - 1)) % MAX_DIRS;
break;
case 'M': // Move one position in the given direction
switch (dir)
{
case 0: // Assume north, Y decreases
new_dir.y = present_dir.y - 1;
break;
case 1: // Assume east, X increases
new_dir.x = present_dir.x + 1;
break;
case 2: // Assume south, Y increases
new_dir.y = present_dir.y - 1;
break;
case 3: // Assume west, X decreases
new_dir.x = present_dir.x - 1;
break;
}
break;
default:
break;
}
new_dir.direction = dir;
}
You may be able to optimize the movement section by using algebra and realizing a relationship between the direction and the ordinate that gets adjusted.
Also, I have not put in any boundary checking.
Sorry if I ruined your day by simplifying a lot of your posted code.
Use a debugger and step through the above code and figure out if it handles the 180 degree turns correctly.
Edit 1: Truth Tables
Here are the truth tables for the movement and ordinate changes
Direction | Delta X | Delta Y| // "delta" means change
----------+---------+--------+
0 | 0 | -1 |
----------+---------+--------+
1 | +1 | 0 |
----------+---------+--------+
2 | 0 | +1 |
----------+---------+--------+
3 | -1 | 0 |
----------+---------+--------+
Can you come up with an algebraic equation for the ordinates based on the direction?

Placing random numbers in a grid

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...