I am creating a simple console-based Battleships in Java, for people who is unfamiliar with the game, it's played on a 2D-grid where you can put down different sized ships either horizontally or vertically. In my example it is a 10x10 grid created by using a 2D char-array. The ships are NOT allowed to overlap each other, they can't share the same 1x1 grid.
I have managed to fix so they can't overlap each other, but the problem I have is if a ship share the same column or row (depending on if the ship is placed vertically or horizontally) at the STARTING POSITION, I can't place it down.
See example picture below for better understanding.
'0' is "empty" slots, 'S' is current placed ships(3-sized). I can place (in this example) 4-sized ships at blue markers, but I can't place them like the red markers
public void placeShip(ShipType shipType, int posX, int posY, int shipSize, Placement placement) {
boolean success = true;
char tempChar = 'x';
if(shipType == ShipType.BATTLESHIP)
tempChar = 'B';
else if(shipType == ShipType.CARRIER)
tempChar = 'C';
else if(shipType == ShipType.DESTROYER)
tempChar = 'D';
else if(shipType == ShipType.SUBMARINE)
tempChar = 'S';
if(placement == Placement.HORIZONTAL) {
for(int i = 0; i < posX+shipSize; i++) {
for(int j = 0; j < posX+shipSize; j++) {
if(board[i][posX-1] != '0' || board[posY-1][i] != '0') {
System.out.println("Can't place down the ship ");
success = false;
break;
}
}
}
if(success) {
System.out.println("Got space");
for(int i = 0; i < shipSize; i++) {
board[posY-1][posX-1+i] = tempChar;
success = false;
}
}
}
if(placement == Placement.VERTICAL) {
for(int i = 0; i < posY+shipSize; i++) {
for(int j = 0; j < posX+shipSize; j++) {
if(board[posY-1][i] != '0' || board[i][posX-1] != '0') {
System.out.println("Can't place down the ship ");
success = false;
break;
}
}
}
if(success) {
System.out.println("Got space");
for(int i = 0; i < shipSize; i++) {
board[posY-1+i][posX-1] = tempChar;
success = false;
}
}
}
}
Above is the code I use to place ships, where I send in Position X and Y and the size of the ship, and the direction of the ship.
You need to re-think your loops for checking if a ship can be placed down. Let's break down one of them into plain English:
if(placement == Placement.HORIZONTAL) {
for(int i = 0; i < posX+shipSize; i++) {
for(int j = 0; j < posX+shipSize; j++) {
if(board[i][posX-1] != '0' || board[posY-1][i] != '0') {
System.out.println("Can't place down the ship ");
success = false;
break;
}
}
}
First issue:
for(int i = 0; i < posX+shipSize; i++)
Why are we iterating from 0 to posX+shipSize? we only need to check shipSize spaces. So you should be looping from posX to posX+shipSize, or from 0 to shipSize, not a combination.
Second issue:
Despite your position being horizontal or vertical, you are nesting your for loops. Which means you're doing your loop shipSize times for no reason. You only need to do it once.
Third issue:
if(board[i][posX-1] != '0' || board[posY-1][i] != '0')
The position board[i][posX-1] has no meaning in this context, and is not related to the position of your ship, because you always start at i = 0. So no matter where you're trying to place your ship, if you have one in the same row or col, we have a problem. This is what's causing the issue in your question. Instead, it should be something like: if(board[posY-1][posX-1] != '0').
Lastly, why not just set the position of posX and posY properly? So you don't have to subtract 1 and confuse yourself.
Ultimately, your new block should look something like this (but I can't be sure since you didn't post a minimum reproducible example):
if(placement == Placement.HORIZONTAL) {
for(int i = 0; i < shipSize; i++) {
if(board[posY-1][posX-1+i] != '0') {
System.out.println("Can't place down the ship ");
success = false;
break;
}
}
// your code
Related
I am to write a program that prints ONLY the NON-BOUNDARY AND CORNER elements of an (n*n) array, for my assignment, and this is the main part of the code:
The output I am getting is this:
As you can see, the non-boundary elements (6,7,10,11) are not in their correct positions, which I believe, is because of incorrect printing of tab spaces within the loop. (My code is totally a mess) I would like some help or suggestions to fix this. Thanks!
I generally find that flattening things (the if-conditions in particular), and putting conditions into boolean-returning methods helps. Try something like
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++ {
if (isCorner(i,j,n) || !isEdge(i,j,n)) {
//...
} else {
//...
}
}
System.out.println();
}
where isCorner(i,j,n) and isEdge(i,j,n) are defined something like
public boolean isCorner(int row, int column, int gridSize) {
//...
}
A you got a solution, just missing spaces, I'll add some smart things:
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
boolean visible = (i % (n - 1) == 0) == (j % (n - 1) == 0);
if (visible) {
System.out.printf(" %4d", a[i][j]);
} else {
System.out.print(" ");
}
}
System.out.println();
}
No longer any problems with tabs "\t", though I used spaces here.
Keep it simple, too many cases just cause problems - as you experienced.
The trick here is to consider whether to print or not. Hence I started with
a variable visible.
The border condition
i == 0 || i == n - 1
could also be written with modulo as
i % (n - 1) == 0
If this is "too smart", hard to grasp reading:
boolean iOnBorder = i % (n - 1) == 0;
boolean jOnBorder = j % (n - 1) == 0;
boolean visible = iOnBorder == jOnBorder;
The "X" pattern checks the _equivalence of i-on-border and j-on-border.
For the rest: formatted printf allows padding of a number.
Try this i have optimized your if condition
No need to again check for i == 0 or i == n-1
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==0 || i==n-1){
if(j==0 || j==n-1){
System.out.print(a[i][j]);
}
}else{
if(j != 0 && j!= n-1){
System.out.print(a[i][j]);
}
}
System.out.print("\t");
}
System.out.println();
}
Just gave a try in case you might find it helpful.
public static void main(String[] args) throws ParseException {
int[][] ar = new int[4][4];
int[] input = new int[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int pointer=0;
int imin=0,jmin=0,imax=3,jmax=3;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
ar[i][j]=input[pointer];
pointer++;
}
}
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(!((i==imax && j==jmin)||(i==imin && j==jmax)||i==j) && //For skipping the corners
(i == imin || j == jmin || i == imax || j == jmax)){// Not to print the borders
continue;
}
else {
System.out.println(ar[i][j]);
}
}
}
}
This code is for one of my assignments (connect four). I need to make this code less than 25 lines and also make the 'if' statements shorter. Also, the board has 6 rows and 7 columns. My code is trying to figure out if a person has won.
I have tried to merge all the loops into one loop, but that does not give me a correct answer.
public static boolean determineWin(String[][] board) {
boolean won = false;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 4; j++) {
if (board[i][j] != ". ") {
if (board[i][j].equals(board[i][j+1]) && board[i][j+1].equals(board[i][j+2]) && board[i][j+2].equals(board[i][j+3])) {
won = true;
break;
}
}
}
}
for (int i = 5; i > 2; i--) {
for (int j = 6; j > 2; j--) {
if (board[i][j] != ". ") {
if (board[i][j].equals(board[i-1][j-1]) && board[i-1][j-1].equals(board[i-2][j-2]) && board[i-2][j-2].equals(board[i-3][j-3])){
won = true;
break;
}
}
}
for (int j = 0; j < 4; j++) {
if (board[i][j] != ". ") {
if (board[i][j].equals(board[i-1][j+1]) && board[i-1][j+1].equals(board[i-2][j+2]) && board[i-2][j+2].equals(board[i-3][j+3])){
won = true;
break;
}
}
}
for (int j = 0; j < 7; j++) {
if (board[i][j] != ". ") {
if (board[i][j].equals(board[i-1][j]) && board[i-1][j].equals(board[i-2][j]) && board[i-2][j].equals(board[i-3][j])){
won = true;
break;
}
}
}
}
return won;
}
The result should be the same as the code above, but I just need the code to be a bit smaller (25 lines) and the if statements to be shorter.
The code above is inefficient because it has 4 separate for loops (to track the 4 directions in which you can win: 1) Left to right, 2) Top to bottom, 3) Diagonal 4) Diagonal/other direction -AND- because the if statements must check 4 consecutive positions.
To optimize the solution you can recognize that you can maintain the state for how many consecutive same pieces have occurred at each position in the board, for each of the 4 possible directions you can win (4 unique states).
Consider as an example winning in the horizontal direction. As you move left to right along the same row, the state counter increments by 1 if the piece to the left is the same. If there is ever a '.', the counter resets to 0. If there is a different piece, the counter resets to 1. You are in a winning position if any of these 4 state counters gets to 4.
The code below is complete for the winning directions of horizontal (state variable 0), and vertical (state variable 1). It is left as an exercise to complete the two rows which represent each of the diagonal directions (state variables 2 and 3).
public static boolean determineWin(String[][] board) {
int[][][] counters = new int[board[0].length+1][board.length+1][4];
for (int y=0; y<board.length; y++) {
for (int x=0; x<board[0].length; x++) {
if (!board[y][x].equals(".")) {
counters[y][x][0] = (x>0 && board[y][x].equals(board[y][x-1])) ? counters[y][x-1][0] + 1 : 1;
counters[y][x][1] = (y>0 && board[y][x].equals(board[y-1][x])) ? counters[y-1][x][1] + 1 : 1;
// Diagonal 1 TODO: counters[y][x][2] =
// Diagonal 2 TODO: counters[y][x][3] =
if (counters[y][x][0] == 4 || counters[y][x][1] == 4 || counters[y][x][2] == 4 || counters[y][x][3] == 4)
return true;
}
}
}
return false;
}
I have a task to write a program which sets up 8 bishops in chess board to occupy whole board. It should end up when first solution is found and print everything out. Here's my written code in Java, and I struggle with finishing it using backtracking ( that place is commented in the code).
/*
* 0 - not occupied square
* 1 - bishop standing square
* 2 - occupied square (diagonal)
*/
public class BishopsBT {
public int [][] solution;
final int N = 8; // number of squares in column and row (chess board)
final int solved = 120; //Sum of 1's and 2's in case of all occupied board
int sum; //current sum of board
public BishopsBT(){
solution = new int [N][N] ;
}
public void solve() {
if(placeBishops(0)){
//print the result
clear(); // clears all 2's
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(" " + solution[i][j]);
}
System.out.println();
}
} else{
System.out.println("NO SOLUTION EXISTS");
}
}
public boolean placeBishops (int bishop){
for (int row = 0; row < N; row++) {
// check if bishop can be placed
if (canPlace(solution, row, bishop)) {
// place the bishop
solution[row][bishop] = 1;
}
}
if (allSpaceOccupied()) {
return true;
} else {
// SOME BACKTRACKING CODE HERE
return false;
}
}
// check if bishop can be placed at matrix[row][column]
public boolean canPlace(int[][] matrix, int row, int column) {
// we need to check all diagonals
// whether no bishop is standing there
for (int i = row, j = column; i >= 0 && j >= 0; i--, j--) {
if (matrix[i][j] == 1) {
return false;
}
}
for (int i = row, j = column; i >= 0 && j < matrix.length; i--, j++) {
if (matrix[i][j] == 1) {
return false;
}
}
for (int i = row, j = column; i < matrix.length && j >= 0; i++, j--) {
if (matrix[i][j] == 1) {
return false;
}
}
for (int i = row, j = column; i < matrix.length && j < matrix.length; i++, j++) {
if (matrix[i][j] == 1) {
return false;
}
}
// if we are here that means we are safe to place Bishop
return true;
}
public boolean allSpaceOccupied() {
// clears previously occupied space
clear();
// occupies new space
for (int i = 0; i < solution.length; i++) {
for ( int j = 0; j < solution.length; j++) {
if (solution[i][j] == 1) diagonalOccupy(i,j);
}
}
sum = 0;
// counts sum of occupied space
for (int i = 0; i < solution.length; i++) {
for ( int j = 0; j < solution.length; j++) {
sum += solution [i][j];
}
}
if (sum == solved) return true;
// else
return false;
}
public void diagonalOccupy(int row, int column) {
// writes 2 in each bishop's occupied square
for (int i = row, j = column; i >= 0 && j >= 0; i--, j--) {
if (solution[i][j] == 0) {
solution[i][j] = 2;
}
}
for (int i = row, j = column; i >= 0 && j < solution.length; i--, j++) {
if (solution[i][j] == 0) {
solution[i][j] = 2;
}
}
for (int i = row, j = column; i < solution.length && j >= 0; i++, j--) {
if (solution[i][j] == 0) {
solution[i][j] = 2;
}
}
for (int i = row, j = column; i < solution.length && j < solution.length; i++, j++) {
if (solution[i][j] == 0) {
solution[i][j] = 2;
}
}
}
// clears all 2's on the board
public void clear() {
for (int i = 0; i < solution.length; i++) {
for ( int j = 0; j < solution.length; j++) {
if (solution[i][j] == 2) solution[i][j] = 0;
}
}
}
public static void main(String[] args) {
BishopsBT q = new BishopsBT();
q.solve();
}
}
The thing is that at the moment my program puts bishops in first column and this layout does not occupy all space. Of course, I could simply put everything in third column and problem is solved. However, I have to use backtracking and have no idea how. If you have any ideas or tips, I would be really glad to hear them.
Your solution assumes that all bishops must be placed in different rows. This is not true for all solutions. (There is a solution where all bishops are in the third or fourth column. You are not looking for all solutions, but if you were, you'd be missing out on many solutions by this assumption.)
You also don't need the canPlace check: There is no restriction that the bishops can't threaten each other. (This might be a valid technique to speed up the search, but again, you'll miss out on some solutions when you apply it. If you want to use it, there's no need to check all diagonal cells for already placed bishops; it is enough to check whether the current cell has been marked as "occupied" or threatened.)
If you are going to use a brute force approach with backtracking, you could test all possible combinations of bishops. That's C(64, 8) or 4,426,165,368 combinations.
You can cut down on the possibilities drastically, but not by assuming that bishops must be in diferent rows. Instead, note that your solution consists of two independent solutions. A bishop on a white square can only threaten white squares and a bishop on a black square can only threaten black squares. So find a solution to place four bishops on the board that threaten all white squares. Then
(If you want to find all solutions, find all k sub-solutions and combine them to k² complete solutions.)
This separation of cases cuts down the possible arrangements to test to C(32, 8), or 35,960. Your strategy to consider only configurations where there is exaclty one bishop per row checks 8^8 (about 16 million) possibilities. It misses some solutions and checks meny configurations where not four bishops are on white squares and four on black squares.
The principle of backtracking was given in the other answer. If you label the 32 white squares like this:
01 02 03 04
05 06 07 08
09 10 11 12
13 14 15 16
17 18 19 20
21 22 23 24
25 26 27 28
29 30 31 32
you can use an recursive approach like this one (in pseudo-Java):
bool place(int i, int start) {
if (i == 8) {
if (allOccupied()) {
print();
return true;
}
} else {
for (int j = start, j < 32; j++) {
int row = j / 4;
int col = 2 * (j % 4) + row % 2;
// add bishop at (col, row)
// save occupancy matrix
// add threat by (col, row) to matrix
if (place(i + 1, j + 1)) return true;
// revert matrix to saved matrix
// remove bishop from (col, row)
}
}
return false;
}
and start it with
place(0, 0);
You should do something like this :
public boolean placeBishops (int bishop){
if(bishop == 8){
if(allSpaceOccupied()){
//Print the board here, i.e found the solution
//also check the indexing of the bishop,
//i have assumed that they start from 0
return true;
}else{
return false;
}
}
for (int row = 0; row < N; row++) {
// check if bishop can be placed
if (canPlace(solution, row, bishop)) {
// place the bishop
solution[row][bishop] = 1;
boolean found = placeBishops(bishop+1);
if(found == true) return true;
solution[row][bishop] = 0;
}
}
return false;
}
we can check if a place is good for a paticular bishop or not, and accordingly increment the bishop count, if we do not find the solution going down that path, we reset the solution array for the current bishop index and the corresponding row for that bishop, so that we can look for another possible solution.
Right now I have a school assignment where I have to create a connect 4 game in java without a gui using arrays. So far I have been able to get the game to detect a horizontal four in a row but I can't seem to get a vertical four in a row. Originally my array was a 6x7 but I have changed it to a 5x5 just to test the concept of swapping the arrays dementions. Just to make less clutter, I only included the part of the code with the vertical detection.
for (column = 0; column < board.length; column++) {
count_piece = 0;
max = 0;
for (row = 0; row < board.length; row++) {
if (max < count_piece) {
max = count_piece;
}
if (board[column][row] == 'X') {
count_piece++;
} else {
count_piece = 0;
}
}
}
System.out.println(max);
if (max == 4) {
System.out.println("\nYou Win!");
break;
}
If you need more information or if anything is not clear please let me know and i'll be glad to provide some more info.
EDIT:
Here is the one with the inverted loop.
for (row = 0; row < board.length; row++) {
count_piece = 0;
max = 0;
for (column = 0; column < board.length; column++) {
if (max < count_piece) {
max = count_piece;
}
if (board[column][row] == 'X') {
count_piece++;
} else {
count_piece = 0;
}
}
}
How about inverting your loop that way your inner loop changing will be the column instead of the row?
Also make your inner loop take the length of the single array you are in.
So instead of using board.length use something like board[i].length
Edit
I think your logic inside your inner loop is wrong...
Try this:
if (board[row][column] == 'X') {
count_piece++;
if(count_piece == 4) {
System.out.println("you win");
return;
}
} else {
count_piece = 0;
}
Here is both checks Horizontal and Vertical with any size array
int count_piece = 0;
//Checking Vertical Win
for (int row = 0; row < board.length; row++) {
count_piece = 0;
for (int column = 0; column < board[row].length; column++) {
if (board[row][column] == 'X') {
count_piece++;
if (count_piece == 4) {
System.out.println("you win");
return;
}
} else {
count_piece = 0;
}
}
}
//Checking Horizontal Win
for (int column = 0; column < board.length; column++) {
count_piece = 0;
for (int row = 0; row < board[column].length; row++) {
if (board[row][column] == 'X') {
count_piece++;
if (count_piece == 4) {
System.out.println("you win");
return;
}
} else {
count_piece = 0;
}
}
}
The main issue I see is assigning max before incrementing count_piece (meaning you would have to get 5 in a row).
Also as others have said length might have issue (not familiar with java so you might get away with it as length would either be 5 or 25 depending if it takes all indices or just the first columns indices. regardless when you go back to 6x7 it will break.
Also what your code is currently doing is going through each column and checking if the row has 4 in a row. What you want is to check for 4 in a row on both axis (my solution would be to have a row count variable and a column count variable (unless you need diagonal)).
adding
count_piece_Col=0; max=0;
above the first for loop and incrementing it similarly to the row count
*lazy way
for(column=0;column<board.length;column++)
{
count_piece=0; max_r=0;
for(row=0;row<board.length;row++)
{
if(max_r<count_piece)
{
max_r=count_piece;
}
if(board[column][row]=='X')
{
count_piece++;
}
else
{
count_piece=0;
}
}
}
for(row=0;row<board.length;row++)
{
count_piece=0; max_c=0;
for(column=0;column<board.length;column++)
{
if(max_c<count_piece)
{
max_c=count_piece;
}
if(board[column][row]=='X')
{
count_piece++;
}
else
{
count_piece=0;
}
}
}
is there a way to place a word in a 2d array in a specific position? For example,i want to give the word, choose vertical or horizontal and the position ((3,3) or (3,4) or (5,6) etc) and the word will be placed in that position.This is my code for the array...
char [][] Board = new char [16][16];
for (int i = 1; i<Board.length; i++) {
if (i != 1) {
System.out.println("\t");
System.out.print(i-1);
}
for (int j = 1; j <Board.length; j++) {
if ((j == 8 && i == 8) ||(j ==9 && i == 9) ||(j == 10 && i == 10) ||(j == 2 && i == 2) )
{
Board[i][j] = '*';
System.out.print(Board[i][j]);
}
else {
if (i == 1) {
System.out.print("\t");
System.out.print(j-1);
}
else {
Board[i][j] = '_';
System.out.print("\t");
System.out.print(""+Board[i][j]);
}
}
(the * means that the word cant be placed there)
Is there a way to place a word in a 2d array in a specific position?
Yes you can implement that. The pseudo-code is something like this:
public void placeWordHorizontally(char[][] board, String word, int x, int y) {
for (int i = 0; i < word.length(); i++) {
if (y + i >= board[x].length) {
// fail ... edge of board
} else if (board[x][y + i]) == '*') {
// fail ... blocked.
} else {
board[x][y + i] = word.charAt(i);
}
}
}
and to do the vertical case you add i etcetera to the x position.
I won't give you the exact code because you'll learn more if you fill in the details yourself.