6

I am trying to develop a 1-sided Battleship game and I have nearly everything set up. I only need to incorporate an array which holds at this time, 5, Ships objects. The class I created for each ship is called Ships.java. Earlier I was having problems initializing the array but that has been settled.

The problem arises when I try to pull in the length of a ship (2, 3, 4 or 5) from an index in the array. I'm not sure how to conceptually go about placing ships.

I feel like I've tried every combination of do-whiles, for loops and if statements. Even tried a switch-case.

The goal is to have the computer select positions for the five ships and set each cell in a grid (ROWSxCOLS) to be equal to NC_SHIP (not clicked, plus ship). The problem is making it check the positions of cells adjacent to a random location on the grid. Also has to check if the ship in question will fit (pulling from the ships[i].getShipLength()).

Here is the code I have so far:

int shipsPlaced = 0;

    for (int i = 0; i < ships.length; i++)
    {
        boolean shipPlaced = false;

        do
        {
            int randomRow = (int)(Math.random()*ROWS);
            int randomCol = (int)(Math.random()*COLS);
            int p = 0;

            if (randomRow - ships[p].getShipLength() >= 0 && gameBoard[(randomRow - p)][randomCol] == NC_EMPTY)
            {
                for (int x = 0; x < ships[x].getShipLength(); x++)
                {
                    gameBoard[(randomRow - x)][randomCol] = NC_SHIP;
                    shipsPlaced = shipsPlaced + 1;
                    if (x == ships[x].getShipLength())
                    {
                        shipPlaced = true;
                        p = p + 1;
                    }
                }
            }
        }while (shipPlaced == false);

    }

Everything has been initialized and set if it's not visible here. The problem is about the math/logic used to place the ships in 'random' locations.

Tim Cooper
  • 144,163
  • 35
  • 302
  • 261
Joe
  • 61
  • 1
  • 1
  • 2
  • The code here is the contents of the placeShips() method. – Joe Apr 11 '11 at 13:39
  • To me, it seams that you are just checking if the postion [randomRow][randomCol] is empty and then place the ship regardless if the other fields are empty or not. Another thing is, that every ship seems to have the same name so pulling the lengh out if you just ask for one index seems cant work as easy as you wihs – Stefan Apr 11 '11 at 13:57
  • some comments on your code: for getting a random row you just need `random.nextInt(ROWS);` (and similarly for random column); as noted by others, you're only placing ships horizontally; when generating random row and column you may want to track those values that you've already tried and failed to place your ship at, so that you don't try the same (row, column) twice when trying to place the same ship. For all this points: see the code in my answer below. – MarcoS Apr 12 '11 at 08:44

3 Answers3

2

First of all: all your ships will go horizontal, you should also randomize the placement direction of the ship.

There are two ways I would face that problem:

  1. First fit initial pos then look if ship fits.
  2. First list all the available positions, then randomize for a number equal to the list length

1 - Make a recursive look for random initial position (x,y) (which should be free, if not re-throw a position). In the recursive "lookForPos" method, make a randomPlacementDirection, and from that, a flag (eg isHorizontal). If it doesn't fit (length from start to final position overflows size of the matrix), re-throw. Cover the positions (position, position+1, position+2, ..., position+n) where 'n' is the length of your ship and position is the x,y pair and the +1 affects only one of the cardinals (depending if isHorizontal or not) if any of them is also occupied re-throw. Eventually you'll have what you need.

2 - Make a list of all the positions where it fits (a 'for' structure), both horizontal and vertical, then randomize for the length of the list.

apacay
  • 1,622
  • 5
  • 18
  • 40
  • I also like MarcoS's [idea](http://stackoverflow.com/questions/5622135/need-help-programming-battleship-location-selector-checker/5624379#5624379), except when he states that there are 4 directions. I feel like that is overcoding... Still, it works. Optimizing this I see two possible directions, horizontal and vertical and I saved the code done for the other two cardinals. – apacay Apr 11 '11 at 16:41
  • if you consider only two directions (horizontal and vertical) starting from a random cell at row `r` and column `c`, then you're still left with the problem of how to place your ship. For example you may decided that `(r,c)` is the cell at the middle of your ship, or the one at the left-most side, or the upper side ... if you consider 4 directions, then when you have the random cell `(r,c)` and a `direction` you know exactly how to place your ship :) – MarcoS Apr 12 '11 at 08:47
  • But if you consider always _left to right_ and _up to down_ as default (and only) directions, how to place your ship is not a problem, because you know you are in your _initial position_ (start position to the ship placement) and from there, depending on the _isHorizontal_ or _isVertical_ flag, you know where to go _end position_. (eg _randShipFreeXY()_ returns [2,3] randPlacement() returns Horizontal, and your ship size is 3 then your _start pos_ is [2,3] your _end pos_ is [5,3], and you have to check, first if it fits in the gameBoard, and then if [3,3]; [4,3] and [5;3] are ship free). – apacay Apr 12 '11 at 12:32
0

What I would do:

  1. get random place to try place your ship there. - as you did

  2. In a loop iterate till this point (go through whole array - seems like you need 2 loops - one till end of array, and next one from start to that point)

  3. invoke methods freeRowsInARow() and freeColsInARow() witch returns number of biggest ship that can be inserted there, starting from that given point (x,y)

  4. Check if your ship has size <= returnedValue (from methods mentioned above), if yes - invoke method inserting it there (choose way - vertical or horizontical) and break loop (with return); if no - ofc continue searching.

dantuch
  • 8,697
  • 4
  • 42
  • 65
0

So, your problem is that of randomly placing battleships on a board. Interesting. Here it is how I would do it.

First, let's assume that we have a Ship class:

public class Ship {

    int size;

    public Ship(int size) {
        this.size = size;
    }

    public int getSize() {
        return size;
    }

}

Then, I would have a BattleshipBoard class as follows:

public class BattleshipBoard {

    private final int rows;
    private final int cols;

    private char[][] board;

    public BattleshipBoard(int rows, int cols) {
        this.rows = rows;
        this.cols = cols;
        board = new char[rows][cols];
    }

    public void place(Ship[] ships) {

        Arrays.sort(ships, new Comparator<Ship>() {

            @Override
            public int compare(Ship s1, Ship s2) {
                return Integer.valueOf(s1.size).compareTo(Integer.valueOf(s2.size));
            }
        });

        for (int j = 0; j < rows; j++)
            for (int k = 0; k < cols; k++)
                board[j][k] = '-'; // Empty position

        char[][] checked = new char[rows][cols];
        Random random = new Random();
        for (int i = ships.length - 1; i >=0; i--) {
            for (int j = 0; j < rows; j++)
                for (int k = 0; k < cols; k++)
                    checked[j][k] = 'U'; // Unchecked position
            boolean placed = false;
            while (! placed) {
                int r = random.nextInt(rows);
                int c = random.nextInt(cols);
                if (checked[r][c] == 'U') {
                    checked[r][c] = 'C'; // Checked position
                    if (board[r][c] == '-') {
                        int direction = random.nextInt(4);
                        // 0 = North; 1 = East; 2 = South; 3 = West;
                        if (canPlace(ships[i], r, c, direction)) {
                            place(ships[i], r, c, direction);
                            placed = true;
                        }
                    }               
                }
            }
        }
    }

    private void place(Ship ship, int row, int col, int direction) {
        int size = ship.getSize();
        switch (direction) {
        case 0: // North
            for (int  i = row; i >= row - (size - 1); i--)
                board[i][col] = 'S';
            break;

        case 1: // East
            for (int i = col; i <= col + (size - 1); i++)
                board[row][i] = 'S';
            break;

        case 2: // South
            for (int i = row; i <= row + (size - 1); i++)
                board[i][col] = 'S';
            break;

        default: // West
            for (int i = col; i >= col - (size - 1); i--) 
                board[row][i] = 'S';
            break;
        }       
    }

    private boolean canPlace(Ship ship, int row, int col, int direction) {
        int size = ship.getSize();
        boolean thereIsRoom = true;
        switch (direction) {
        case 0: // North
            if (row - (size - 1) < 0)
                thereIsRoom = false;
            else 
                for (int  i = row; i >= row - (size - 1) && thereIsRoom; i--)
                    thereIsRoom = thereIsRoom & (board[i][col] == '-');
            break;

        case 1: // East
            if (col + (size - 1) >= cols)
                thereIsRoom = false;
            else
                for (int i = col; i <= col + (size - 1) && thereIsRoom; i++)
                    thereIsRoom = thereIsRoom & (board[row][i] == '-');
            break;

        case 2: // South
            if (row + (size - 1) >= rows)
                thereIsRoom = false;
            else
                for (int i = row; i <= row + (size - 1) && thereIsRoom; i++)
                    thereIsRoom  = thereIsRoom & (board[i][col] == '-');
            break;

        default: // West
            if (col - (size - 1) < 0) 
                thereIsRoom = false;
            else
                for (int i = col; i >= col - (size - 1) && thereIsRoom; i--) 
                    thereIsRoom = thereIsRoom & (board[row][i] == '-');
            break;
        }
        return thereIsRoom;
    }

    public void printBoard() {
        for (int i = 0; i < rows; i++)
            System.out.println(Arrays.toString(board[i]));
    }

}

Then, if you have something like this:

public static void main(String[] args) {
    Ship[] ships = new Ship[] {
            new Ship(1),
            new Ship(3),
            new Ship(2),
            new Ship(3)
    };
    BattleshipBoard battleshipBoard = new BattleshipBoard(7, 7);
    battleshipBoard.place(ships);
    battleshipBoard.printBoard();
}

you may get the following output (depending on your Random generator):

[-, -, -, -, -, -, -]
[-, -, -, -, -, -, -]
[-, -, S, -, -, -, -]
[-, -, -, -, -, -, -]
[-, S, -, S, S, -, -]
[-, S, -, -, -, -, -]
[-, S, -, -, S, S, S]

which is a random placement of four ships of size 1, 2, 3, and 3.

A few comments:

  • the place() method attempts to randomly placing the ships by choosing random starting position (r,c) and random direction

  • the ships array is sorted, so that we start with biggest ships first (it's easier to place them when our board has fewer occupied positions)

  • the checked matrix is used to avoid checking the same random position (r,c) more then once when attempting to place a ship

  • the canPlace() method returns true if the passed ship can be placed on the board starting at position (r,c) and going towards the passed direction

  • the place() method places the passed ship on the board starting at position (r,c) and going towards the passed direction

MarcoS
  • 12,788
  • 5
  • 36
  • 61