Stylistical remarks:
- if you use
ThreadLocalRandom
for generating position, you should also use it for other randomness (in other words: (int)(Math.random()*2)
could rather be random.nextBoolean()
, because actually a boolean could decide if ship should be horizontal or vertical)
nextInt(0,x)
is just a longer variant of nextInt(x)
.
Actual bugs:
- due to a presumably copy-paste issue,
column
(0-"9") and row
(0-"7") are generated in the same way in both cases, making it possible to index out of the array when placing a vertical ship
- which you seem to have noticed, but fixed it with that
row + 2 < 11
check which has two problems in itself:
- when
row+2
ends up being 10
(which is <11
), that is an invalid index (valid indices are 0
...9
)
- as row stays between 0 and "7", there will not be horizontal ships in the last few rows
nextInt(a,b)
generates numbers a
...b-1
, so it will not generate b
itself
- as the other answer points out string comparison with
==
generally and usually does not work, use equals()
Generally I would suggest having a single check+placement function, which can deal with an entire rectangle (given position+size). Also, I switched to array of characters, that simplifies both comparisons and printing.
boolean tryPlace(int x,int y,int width,int height) {
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
if(board[y+i][x+j]!='.') {
return false; // ship can not be placed
}
}
}
// if we reach here, ship can be placed
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
board[y+i][x+j]='#';
}
}
return true; // ship placed successfully
}
This routine could be called to place a pair of 3-long ships this way:
board=new char[10][10];
for(int i=0;i<10;i++)
for(int j=0;j<10;j++)
board[i][j]='.';
int size=3;
int amount=2;
while(amount>0) {
if(random.nextBoolean()) {
// horizontal
if(tryPlace(random.nextInt(10-size+1),random.nextInt(10),size,1)){
amount--; // one placed
}
} else {
// vertical
if(tryPlace(random.nextInt(10),random.nextInt(10-size+1),1,size)){
amount--; // one placed
}
}
}
// and a 4x2 mothership
while(!(random.nextBoolean()
?tryPlace(random.nextInt(7),random.nextInt(9),4,2)
:tryPlace(random.nextInt(9),random.nextInt(7),2,4)
));
for(int i=0;i<10;i++)
System.out.println(board[i]); // char[] has special overload for print/ln()
Test: https://ideone.com/DjYqjB
However, when I was a kid we had a rule that ships could not match, there had to be empty space (or a border of the board) around them. If you need that,
tryPlace()
could check a larger block, and put the ship into the middle of it. Also, a usual trick of implementing board games is that you can keep a larger array in the memory than what you will actually display. So instead of fighting with "check if field is empty or it is outside the board", it is simpler to have a 12x12 board, and place ships into the middle 10x10 portion of it:
boolean tryPlaceWithBorder(int x,int y,int width,int height) {
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
if(board[y+i][x+j]!='.')
return false; // ship can not be placed
// if we reach here, ship can be placed
for(int i=1;i<height-1;i++)
for(int j=1;j<width-1;j++)
board[y+i][x+j]='#';
return true; // ship placed successfully
}
and modified usage:
board=new char[12][12];
for(int i=0;i<12;i++)
for(int j=0;j<12;j++)
board[i][j]='.';
int size=3;
int amount=2;
while(amount>0) {
if(random.nextBoolean()) {
// horizontal
if(tryPlaceWithBorder(random.nextInt(12-size-1),random.nextInt(10),size+2,3))
amount--; // one placed
} else {
// vertical
if(tryPlaceWithBorder(random.nextInt(10),random.nextInt(12-size-1),3,size+2)){
amount--; // one placed
}
}
}
// and a 4x2 mothership
while(!(random.nextBoolean()
?tryPlaceWithBorder(random.nextInt(7),random.nextInt(9),6,4)
:tryPlaceWithBorder(random.nextInt(9),random.nextInt(7),4,6)
));
for(int i=1;i<11;i++)
System.out.println(String.valueOf(board[i],1,10));
Test: https://ideone.com/LXAD7T