I have to do a Battleship game. The game should have some kind of AI. The AI CAN place ships with a hard coded pattern, but i want to take things a step further and make the ship placement random.
I have implemented a "trial and error" method, where ships get randomly placed on the field with a random rotation, until the algorithm runs out of either ships to be placed or fields to place the ships in. In the second case the recursive implementation allows to try out other ship/field/rotation combos, until the first condition is reached. So to speak: try all possible ship/rotation/field combo's (picked randomly) until you find one valid one, where all ships are placed.
As you can imagine, this is a terrible effort, when it comes to runtime. The size of the "board" can be 5x5 up to 30x30 with exactly 30% of the fields beeing occupied by a ship.
Now, Runtime isn't my concern for sizes up to 14x14, but then runtime increases so badly, that i have to think of a way to reduce runtime.
Any suggestions? (I would like to get general thinking advice/idea's, not code)
In case, my explenation wasn't enough: Here's the class that tries to place the ships on the field:
public class RandomShipFactory {
private int size;
private Game game;
private ArrayList<Ship> toBeCheckedShips = new ArrayList<Ship>();
private ArrayList<Position> toBeCheckedPositions = new ArrayList<Position>();
public RandomShipFactory(int size, Game game) {
this.size = size;
this.game = game;
this.toBeCheckedShips.addAll(game.ownBoard.getShips());
for(int i = 0; i < this.size; i++) {
for(int j = 0; j < this.size; j++) {
this.toBeCheckedPositions.add(new Position(i,j));
}
}
}
public void makeRandomShipPlacements() {
this.makeRandomShipPlacements(this.toBeCheckedPositions, this.toBeCheckedShips);
game.ownBoard.printBoard();
}
private boolean makeRandomShipPlacements(ArrayList<Position> currentPositions, ArrayList<Ship> currentShips) {
if(currentPositions.isEmpty()) {
return false;
}
ArrayList<Position> checkedPositionsInThisRun = new ArrayList<Position>();
Random r = new Random();
boolean success = false;
while(!success && !currentShips.isEmpty() && !currentPositions.isEmpty()) {
int randomPositionIndex = r.nextInt(currentPositions.size());
Position randomPosition = currentPositions.remove(randomPositionIndex);
checkedPositionsInThisRun.add(randomPosition);
ArrayList<Ship> currentShipQueue = new ArrayList<Ship>();
currentShipQueue.addAll(currentShips);
while(!success && !currentShipQueue.isEmpty()) {
Ship currentShip = currentShipQueue.remove(0);
boolean shouldRotate = r.nextBoolean();
if(shouldRotate) {
currentShip.rotate();
}
boolean canPlaceShip = this.game.placeShipOnOwnBoard(currentShip, randomPosition.x, randomPosition.y);
if(canPlaceShip) {
currentShips.remove(currentShip);
ArrayList<Position> boxPositions = removeBoxSquarePositions(currentPositions, currentShip, randomPosition);
if(currentShips.isEmpty()) {
success = true;
}else {
boolean recursiveSuccess = this.makeRandomShipPlacements(currentPositions, currentShips);
if(!recursiveSuccess) {
this.game.removeShipFromOwnBoard(currentShip);
currentPositions.addAll(boxPositions);
currentShips.add(currentShip);
}else {
success = true;
}
}
}else {
currentShip.rotate();
canPlaceShip = this.game.placeShipOnOwnBoard(currentShip, randomPosition.x, randomPosition.y);
if(canPlaceShip) {
currentShips.remove(currentShip);
ArrayList<Position> boxPositions = removeBoxSquarePositions(currentPositions, currentShip, randomPosition);
if(currentShips.isEmpty()) {
success = true;
}else {
boolean recursiveSuccess = this.makeRandomShipPlacements(currentPositions, currentShips);
if(!recursiveSuccess) {
this.game.removeShipFromOwnBoard(currentShip);
currentPositions.addAll(boxPositions);
currentShips.add(currentShip);
}else {
success = true;
}
}
}
}
}
}
currentPositions.addAll(checkedPositionsInThisRun);
return success;
}
private ArrayList<Position> removeBoxSquarePositions(ArrayList<Position> positionList, Ship ship, Position pos) {
ArrayList<Position> boxPositions = new ArrayList<Position>();
for(int i = 0; i < ship.length + 2; i++) {
for(int j = 0; j < 3; j++) {
int nextX, nextY;
switch(ship.getRotation()) {
case HORIZONTAL:
nextX = pos.x - 1 + i;
nextY = pos.y - 1 + j;
break;
case VERTICAL:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
default:
nextX = pos.x - 1 + j;
nextY = pos.y - 1 + i;
break;
}
if(nextX >= 0 && nextX < this.size && nextY >= 0 && nextY < this.size) {
Position currentPosition = Position.findPosInList(positionList, nextX, nextY);
if(currentPosition != null) {
positionList.remove(currentPosition);
boxPositions.add(currentPosition);
}
}
}
}
return boxPositions;
}
}