0

OK, so I have an assignment where we have to create a low end battleship program in Java using the GUI. We have to create an array of JLabel objects, with an image attached, (for the water). I have done this using GridLayout.

The board comes up fine, but if I try and add a ship, (I am at this point just trying to see how to add a ship onto the board), I end up either not having anything show up, or, as in the case of the code I have at the moment, I can get ships on the board to show up, but they tend to overlap each other. (there was a problem with them going over the edge, but I sorted that part out.

So I was wondering if someone wouldn't mind having a look at my code and tell me if I am going about this all wrong. Is GridLayout the way to go, or should I be utilizing some other method? I'd also like to know if there is a more efficent way of going about manipulating the ships, especially so they don't overlap, beacuse I can't seem to find any real effective way. Is this just not possible with GridLayout? Here's my code:

package battleship;

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SpringLayout;

public class Battleship extends JFrame {

private ImageIcon waterImg, shipCovered, boatImgEndH1, boatImgEndH2, boatImgMidH, boatImgFrntH2, boatImgFrntH1,
        boatImgEndV1, boatImgEndV2, boatImgMidV, boatImgFrntV2, boatImgFrntV1;
private Container content;
private JPanel boardFrame;
private JLabel[][] boardGrid;
public JLabel label, label2;
private int width, length;

public Battleship() {
}

public Battleship(int width, int length) {
    SpringLayout layout = new SpringLayout();
    this.width = width;
    this.length = length;
    this.setVisible(true);
    this.setSize(640, 540);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    content = this.getContentPane();
    content.setLayout(layout);

    try {
                 BufferedImage water = ImageIO.read(new URL("https://i.stack.imgur.com/gJmeJ.png"));
                 BufferedImage ship = ImageIO.read(new URL("https://i.stack.imgur.com/T5uTa.png"));

                 waterImg = new ImageIcon(new ImageIcon(water).getImage());
                 boatImgEndH1 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgEndH2 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgMidH = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgFrntH2 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgFrntH1 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgEndV2 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgEndV1 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgMidV = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgFrntV2 = new ImageIcon(new ImageIcon(ship).getImage());
                 boatImgFrntV1 = new ImageIcon(new ImageIcon(ship).getImage());
                } catch (MalformedURLException ex) {
                    throw new Error("One of the cameras you are trying to connect to doesn't exist!");
                } catch (IOException ex) {
                    throw new Error("CCTV couldn't connect to the required camera feeds!");
                }

    boardFrame = new JPanel(new GridLayout(width, length, 1, 1));

    content.add(boardFrame);
    layout.putConstraint(SpringLayout.WEST, boardFrame, 5, SpringLayout.WEST, content);
    layout.putConstraint(SpringLayout.NORTH, boardFrame, 5, SpringLayout.NORTH, content);

    boardGrid = new JLabel[width][length];

    for (int x = 0; x < width; x++) {
        for (int y = 0; y < length; y++) {
            boardFrame.add(boardGrid[x][y] = new JLabel(waterImg, JLabel.CENTER));
        }
    }
    twoShip();
    threeShip();

    this.setVisible(true);
    boardFrame.setVisible(true);

}

public int random(int n) {

Random randNumGenerator = new Random();
    int random= randNumGenerator.nextInt(n) + 1;
return random;
}

private void twoShip() {
    int ran5 = random(width - 1);
    int ran4 = random(width - 2);
    int ran2 = random(2);

    int max = (ran5 - 1);
    while (boardGrid[ran5][ran5].getIcon().equals(waterImg)) {
            if (ran2 == 2) {
                if (ran5 >= (width - 1)) {
                    while (!boardGrid[max][ran4].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[max][ran4].getIcon().equals(waterImg)) {
                        boardGrid[max][ran4].setIcon(boatImgEndV1);
                    }
                } else {
                    while(!boardGrid[ran5][ran4].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[ran5][ran4].getIcon().equals(waterImg)) {
                        boardGrid[ran5][ran4].setIcon(boatImgEndV1);
                    }
                }
                if (ran5 >= (width - 1)) {
                    while (!boardGrid[max + 1][ran4].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[max + 1][ran4].getIcon().equals(waterImg)) {
                        boardGrid[max + 1][ran4].setIcon(boatImgFrntV1);
                    }
                } else {
                    while(!boardGrid[ran5 + 1][ran4].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[ran5 + 1][ran4].getIcon().equals(waterImg)) {
                        boardGrid[ran5 + 1][ran4].setIcon(boatImgFrntV1);
                    }
                }
            } else {
                if (ran5 >= (width - 1)) {
                    while(!boardGrid[ran4][max].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[ran4][max].getIcon().equals(waterImg)) {
                        boardGrid[ran4][max].setIcon(boatImgEndH1);
                    }
                } else if (ran5 <= (width - 1)) {
                    while(!boardGrid[ran4][ran5].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[ran4][ran5].getIcon().equals(waterImg)) {
                        boardGrid[ran4][ran5].setIcon(boatImgEndH1);
                    }
                }
                if (ran5 >= (width - 1)) {
                    while (!boardGrid[ran4][max + 1].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[ran4][max + 1].getIcon().equals(waterImg)) {
                        boardGrid[ran4][max + 1].setIcon(boatImgFrntH1);
                    }
                } else if (ran5 <= (width - 1)) {
                    while (!boardGrid[ran4][ran5 + 1].getIcon().equals(waterImg)) {
                        return;
                    } while (boardGrid[ran4][ran5 + 1].getIcon().equals(waterImg)) {
                        boardGrid[ran4][ran5 + 1].setIcon(boatImgFrntH1);
                    }
                }
            }
    }
}

private void threeShip() {
    int ran3 = random(width - 1);
    int ran2 = random(2);

    int max = (ran3 - 2);
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < length; y++) {
            if (ran2 == 2) {
                if (boardGrid[x][y].getIcon().equals(waterImg)) {
                    if (ran3 >= (width - 3)) {
                        boardGrid[max][ran3].setIcon(boatImgEndV1);
                        boardGrid[max][ran3].validate();
                    } else {
                        boardGrid[ran3][ran3].setIcon(boatImgEndV1);
                    }
                }
                if (boardGrid[x][y].getIcon().equals(waterImg)) {
                    if (ran3 >= (width - 3)) {
                        boardGrid[max + 1][ran3].setIcon(boatImgMidV);
                    } else {
                        boardGrid[ran3 + 1][ran3].setIcon(boatImgMidV);
                    }
                }
                if (boardGrid[x][y].getIcon().equals(waterImg)) {
                    if (ran3 >= (width - 3)) {
                        boardGrid[max + 2][ran3].setIcon(boatImgFrntV1);
                    } else {
                        boardGrid[ran3 + 2][ran3].setIcon(boatImgFrntV1);
                    }
                }
            } else {
                if (boardGrid[x][y].getIcon().equals(waterImg)) {
                    if (ran3 >= (width - 3)) {
                        boardGrid[ran3][max].setIcon(boatImgEndH1);
                    } else {
                        boardGrid[ran3][ran3].setIcon(boatImgEndH1);
                    }
                }
                if (boardGrid[x][y].getIcon().equals(waterImg)) {
                    if (ran3 >= (width - 3)) {
                        boardGrid[ran3][max + 1].setIcon(boatImgMidH);
                    } else {
                        boardGrid[ran3][ran3 + 1].setIcon(boatImgMidH);
                    }
                }
                if (boardGrid[x][y].getIcon().equals(waterImg)) {
                    if (ran3 >= (width - 3)) {
                        boardGrid[ran3][max + 2].setIcon(boatImgFrntH1);
                    } else {
                        boardGrid[ran3][ran3 + 2].setIcon(boatImgFrntH1);
                    }
                }
            }
        }
    }
}

public static void main(String[] args) {
    new Battleship(17, 17);
}

}

Any help or insights would be greatly appreciated. EDIT I have updated to code I have so far, which is allowing me to insert ships, but they either overlap or just don't show up, and I have setup the images to run via html, so you can run it and see.

Xanthian23
  • 55
  • 9
  • I've even considered creating a big Generic Pair class, with 15 generic elements, and then using that to create an `ArrayList` of `JLabel`'s 15 long. then I would just have to fill them, 15 times, and I would have a grid without the `GridLayout`, and could then, (at least I think), pop a ship any ol place I felt like it. I have no idea if that would work though. I honestly think my main problem is dealing with `GridLayout`, but I don't know what else I could use that would allow me to do this. – Xanthian23 Dec 11 '13 at 12:05
  • I sorted out how to get the `JLabel` for the ship inserted into where I want it to go, but the problem now is that every time I insert a `JLabel`, it shifts everything over by one, which then throws off the location of of where to place the next ship `JLabel`, (At least when it comes to the vertically placed ships). Is there a way to remove the specific `JLabel` directly in front of the new I just placed onto the grid? – Xanthian23 Dec 11 '13 at 23:30
  • 1
    I looked at this question yesterday. Copy/pasted and compiled the code, looked again at the text and realized ..I had no idea what you're talking about. One of my main pieces of confusion is related to *"We have to create an array of JFrame objects, with an image attached, (for the water). I have done this using GridLayout."* That makes exactly 0 sense to me, since layouts are not used to position frames. Could you try reading your question to a technically oriented friend to check it makes sense to them? – Andrew Thompson Dec 13 '13 at 04:43
  • Sorry, I meant that we have to create an array of JLabels, and then attach an image, in this case, of water to them. I managed to sort that part doing this `for (int x = 0; x < width; x++) { for (int y = 0; y < length; y++) { boardFrame.add(boardGrid[x][y] = new JLabel(waterImg, JLabel.CENTER)); } }` boardFrame is a nested JPanel. This works, but I am wondering if there is a better way to do this. As it stands, it is very difficult to manipulate anything the way I have it. – Xanthian23 Dec 13 '13 at 20:28
  • *"I managed to sort that part doing this.."* Maybe there are people who can read code in comments but I'm not one of them. Please [edit the question](http://stackoverflow.com/posts/20512293/edit) instead. – Andrew Thompson Dec 13 '13 at 20:30
  • Edited, with the updated code I have going right now. – Xanthian23 Dec 13 '13 at 20:50
  • *"shipCovered = new ImageIcon("shpcvrd.gif");"* BTW - forgot to mention. One way to get image(s) for an example is to hot-link to the images seen in [this answer](http://stackoverflow.com/a/19209651/418556). As it is now we cannot actually see the effect here, without the images. But please make it an [SSCCE](http://sscce.org/). AFAIU, this would require two images to demonstrate, one for the water block and one for the ship block. That code has **12 images**. – Andrew Thompson Dec 13 '13 at 20:55
  • I added a link at the bottom to a rar file of all the images. – Xanthian23 Dec 13 '13 at 21:05
  • *"I added a link at the bottom to a rar file of all the images."* No on *so many levels* I cannot count them. :-/ 1) I have no software on this PC to handle RAR, and no intention of installing it. 2) I don't have the motivation to be chasing off-site links, downloading, things expanding files... 3) I started that thread, with the images, specifically to avoid having to use 'off site resources' for images. 4) ... – Andrew Thompson Dec 13 '13 at 21:06
  • Sorry, still learning the ropes. So you want me to try and use some of the images you linked to? I think I can do that, gimme a few mins, and I'll update the code to reflect it... – Xanthian23 Dec 13 '13 at 21:16
  • *"So you want me to try and use some of the images you linked to?"* O_o If the answer to that is not clear yet, I simply don't know what words *will* make it clear.. BTW - don't forget to chop out 10 images.. – Andrew Thompson Dec 13 '13 at 21:18
  • I have updated the code so that it can be ran using online images via the url's you gave me, (First time I've ever done that) It runs a bit tweaky for the two ship method, so I have a two, three and four ship method included. Hopefully that helps. – Xanthian23 Dec 13 '13 at 22:12
  • 1
    `Ships ship = new Ships(); -> Battleship.java:31: error: cannot find symbol` Also the code is 296 LOC! Please put more effort into A) Making it shorter. B) Ensuring that what we are copy/pasting will *actually compile, run and **show the problem.*** The SSCCE document actually links to another tool I created to help check that an SSCCE will compile cleanly, in that it only compiles a single source code pasted in the text area. An SSCCE of a Java run-time problem should *compile cleanly in the SSCCE Text Based Compiler.* – Andrew Thompson Dec 13 '13 at 22:22
  • Ug, I forgot the I have my random number generator in another class. I'll fix it and test it before re pasting. Sorry – Xanthian23 Dec 13 '13 at 22:29
  • Xanthian, *start simple*. Don't try to get it working on 15x15 in one go, start by 5x5 and small ships to see what is wrong before moving on. Your ships can be parametrized; try to get `void addShip(int minX, int minY, int length, boolean vertical)` working. That will also lead to shorter, more readable code. – tucuxi Dec 13 '13 at 22:42
  • OK, I dropped one of the ships, inserted the random number generator into the code and verified that it compiles. – Xanthian23 Dec 13 '13 at 22:47
  • tucuxi - I tried to figure out how to do it with a method created the way you suggested, but I just couldn't wrap my head around how that would work. It seems like it should, but I don't understand how to do it, (at least not yet). I ended up creating four ship methods, one for each size, with no arguments. It took a bit of scanning through a couple nested loops, but in the end it worked. Since this is my final, I'm going to stick with a running program, but once the semester is over, I'm going to try and sort out how to do it your way, as it seems like it would be less work overall. Thanks. – Xanthian23 Dec 17 '13 at 11:02
  • Oh, and Andrew, thanks for the heads up on how to post a question correctly, so it can be used, (and be more helpful overall). I will make sure to run things through before posting next time. – Xanthian23 Dec 17 '13 at 11:06

1 Answers1

1

Read about "model-view-controller"; it makes development a lot easier. Your draw-the-board-and-play classes should be different from your manage-the-ships classes. This allows you to test them separately.

You would test the ship-management (which would do board initialization, hit management, and so on) by generating random boards and printing them via System.out.println to see if things worked.

You would test the drawing by hand-crafting a few interesting boards, and testing to see if they displayed as expected, and called the right ship-management code when the user clicks on things. Finally, you would plug both parts together.

With this separation, you would be able to ask this question with only the draw-the-board code, which would be much more readable than the mix of both that you are currently copy-pasting.

tucuxi
  • 17,561
  • 2
  • 43
  • 74
  • Well, I followed your advise, and I now have a working program. The ships & images are in one class, the board and game play stuff is in another, and it did make it easier to test separately. In the end, when I generate my board, I set the background of each grid to white. From there it was a simple check of the background color to see if there was a ship in that gird spot or not, (each ship piece has a color associated with it, 6 in all). Thanks for the advice! – Xanthian23 Dec 17 '13 at 10:57