Battleship console game

This is a battleship console game I wrote in C++.

#include <string>
#include <iostream>
#include <process.h>
#include <cstdlib>
#include <time.h>
using namespace std;




int const ROW = 3;
int const COL = 3;


int subX, subY;


int guesses = 5;


int board[ROW][COL];


bool hasWon = false;


void winScreen()
{
    cout << "You won with " << guesses << " guesses left! :) " << endl;
}


void generateGrid()
{
    for (int x = 0; x < ROW; x++)
    {
        for (int y = 0; y < COL; y++)
        {
            //0 = empty
            //1 = has guessed
            //2 = sub


            //empty board
            board[x][y] = 0;
            cout << "-" << x << "," << y << "- ";
        }
        cout << endl;
    }
}


void generateSubCoords()
{
    subX = 0 + (rand() % ROW);
    subY = 0 + (rand() % COL);
    //2 = sub
    board[subX][subY] = 2;
}


void displayGrid(int changeX, int changeY)
{

    cout << "_______________________________________" << endl;
    for (int x = 0; x < ROW; x++)
    {
        for (int y = 0; y < COL; y++)
        {
            if (x == changeX && y == changeY)
            {
                if (board[x][y] != 2)
                {
                    board[x][y] = 1;
                }
            }

            if (board[x][y] == 0)
            {
                cout << "-" << x << "," << y << "- ";
            }
            else if (board[x][y] == 1)
            {
                cout << "-X,X- ";
            }

            if (board[x][y] == 2 )
            {
                if (x == changeX && y == changeY)
                {
                    cout << "-HIT- ";
                }
                else
                {
                    cout << "-" << x << "," << y << "- ";
                }
            }

        }
        cout << endl;
    }
}


void playerGuess()
{

    int guessX, guessY;


    bool goodInput = false;

    cout << endl;
    do {
        cout << guesses << " guesses left. " << endl;


        //get X pick
        cout << "X: ";
        cin >> guessX;
        if (guessX < 0 || guessX>2)
        {
            goodInput = false;
            cin.clear();
            cin.ignore(256, '\n');
            cout << "Out of Range!" << endl;
            //display grid again with X's on board where guessed
            break;
        }
        else
        {
            goodInput = true;
        }




        //get Y pick
        cout << "Y: ";
        cin >> guessY;
        if (guessY < 0 || guessY>2)
        {
            goodInput = false;
            cin.clear();
            cin.ignore(256, '\n');
            cout << "Out of Range!" << endl;
            //display grid again with X's on board where guessed
            break;
        }
        else
        {
            goodInput = true;
        }





        displayGrid(guessX, guessY);
        cout << endl;
        guesses--;


    } while (!goodInput);


    //if guessed right
    if (guessX == subX && guessY == subY)
    {
        hasWon = true;
        winScreen();
    }
}




void main()
{
    srand(time(NULL));
    cout << "BATTLE-SHIP 2000" << endl << endl;
    int board[ROW][COL];


    generateGrid();
    generateSubCoords();



    do
    {
        if (hasWon)
        {
            break;
        }


        playerGuess();
    } while (guesses > 0);

    //if lost
    if (!hasWon)
        cout << "Sorry, you lost. :(" << endl;

    cout << "The sub was located at -" << subX << "," << subY << "-." << endl;
}

I really feel like I’m overcomplicating things. I feel like I overuse do whiles and I should try to use a catch and throw option or something a little more creative. Any suggestions would be appreciated.

Answer

Using namespace std

Please review this post on why you should not use using namespace std: Why is “using namespace std” considered bad practice?

Enums

//0 = empty
//1 = has guessed
//2 = sub

You should use an enum to represent those values:

enum SquareType {
    Empty = 0,
    Guessed = 1,
    Sub = 2
};

Now, instead of doing the confusing:

if (board[x][y] == 0)
{
    ...
}
else if (board[x][y] == 1)
{
    ...
}
if (board[x][y] == 2)
{
    ...
}

You can do:

if (board[x][y] == SquareType.Empty)
{
    ...
}
else if (board[x][y] == SquareType.Guessed)
{
    ...
}
if (board[x][y] == SquareType.Sub)
{
    ...
}

Style

You have way too much vertical space in your program. You should really only use 1 empty line at a time.

Your bracing is inconsistent. Either always use them (preferred), or use them as little as possible.

Input

You never use the do-while loop in playerGuess. If you carefully check the runtime, you break directly out of the loop every time you have bad input. That gives the loop a chance to continue if, and only if, you get two good inputs, but getting two good inputs automatically invalidates the continuation condition.

Notice how similar the two sections in the following code is. Duplication everywhere.

//get X pick
cout << "X: ";
cin >> guessX;
if (guessX < 0 || guessX>2)
{
    goodInput = false;
    cin.clear();
    cin.ignore(256, '\n');
    cout << "Out of Range!" << endl;
    //display grid again with X's on board where guessed
    break;
}
else
{
    goodInput = true;
}

//get Y pick
cout << "Y: ";
cin >> guessY;
if (guessY < 0 || guessY>2)
{
    goodInput = false;
    cin.clear();
    cin.ignore(256, '\n');
    cout << "Out of Range!" << endl;
    //display grid again with X's on board where guessed
    break;
}
else
{
    goodInput = true;
}

You should join these into a single input function:

int input(string prompt)
{
    int guess = -1;

    do {
        cout << prompt;
        cin >> guess;
        if (guess < 0 || guess > 2)
        {
            guess = -1;
            cin.clear();
            cin.ignore(256, '\n');
            cout << "Out of Range!" << endl;
        }
    } while (guess == -1)

    return guess;
}

Then call it like:

//get X pick
guessX = input("X: ")

//get Y pick
guessY = input("Y: ")

This will also ensure the function gets good input back the first time input() returns.

main()

Your main() function should only be the entrance to your program. You typically should not use main() to do work–create a function who’s sole responsibility is doing that work. Right now, you gave main() two responsibilities–starting the program and running it.

Exceptions

Only use exceptions for exceptional behavior, not for control flow. Getting bad input is never exceptional–it is to be expected. Simple programs like this should almost never throw exceptions deliberately.

Attribution
Source : Link , Question Author : megachuck64 , Answer Author : Community

Leave a Comment