3

Linked: Using MouseListener to select a range of cells in a grid

I am creating a Battleships game, with two grids. The user will place their ships on one grid and then bomb the other grid.

I have an abstract class called Grid which creates a 10x10 grid of cells (which extend JPanel class). It gives each cell a listener, like so:

public Grid() {
    setPreferredSize(new Dimension(300, 300));
    setLayout(new GridLayout(GRID_SIZE,GRID_SIZE));

    for (int x = 0; x < GRID_SIZE; x++)
        for (int y = 0; y < GRID_SIZE; y++) {
            final Cell cell = new Cell(x, y);
            cellArray[x][y] = cell;
            add(cell);
            cell.addMouseListener(new MouseListener() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    mouseClick(e, cellArray);
                }

                @Override
                public void mouseEntered(MouseEvent e) {
                    mouseEnter(e, cellArray);
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    mouseExit(e, cellArray);
                }

                @Override
                public void mousePressed(MouseEvent e) {
                    mousePress(e, cellArray);
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    mouseRelease(e, cellArray);
                }
            });
        }
}

So these listeners can be accessed from the two subclasses, the listeners call another method as can be seen in the code above. These methods are contained under the constructor and are all abstract:

public abstract void mouseClick(MouseEvent e, Cell[][] cellArray);
public abstract void mouseEnter(MouseEvent e, Cell[][] cellArray);
public abstract void mouseExit(MouseEvent e, Cell[][] cellArray);
public abstract void mousePress(MouseEvent e, Cell[][] cellArray);
public abstract void mouseRelease(MouseEvent e, Cell[][] cellArray);

I then have two concrete subclasses - one is for the "Player grid", the other for the "Computer grid". The main difference is that in the Player grid the listeners are used immediately to place the ships. In the Computer grid, the ships are placed automatically (done when an object of the class is constructed).

This means that the listeners in the Computer grid class are only there to respond to the user's attempt to bomb a square. The Player Grid, once the ships have been placed, should be automatically bombed by the computer.

This is where I've run into difficulties. I have written a method in the PlayerGrid class called computerAttack() - but I've no idea how to access it from outside the class. How can I make the two subclasses communicate? Additionally, is this even the proper way to do it, or am I trying to do too much in these two classes? I have tried to provide as much information as necessary here, but if the actual content of the two subclasses is needed, I can provide them.

Community
  • 1
  • 1
Andrew Martin
  • 5,619
  • 10
  • 54
  • 92
  • What is coordinating the overall the flow of the game? Do you have a separate class for this? If not, that might be something to consider. – Joe F Apr 27 '13 at 16:38
  • I have a main classed called "MainGUI". It initialises a JFrame and places the two grids on it in a Border Layout. That's as far as it goes though. Would you suggest building the functionality to actually play the game once the boards are set into this? – Andrew Martin Apr 27 '13 at 16:39
  • 1
    An interesting thought experiment with the MVC suggestions is *"Could I play an entire test game of Battleships without a single piece of GUI code?"*. If the answer is yes, you've probably achieved good separation of model and view. – Paul Grime Apr 27 '13 at 17:05

2 Answers2

5

This is a typical application, where patterns like MVC are applied. One would implement the game logic separately and for common usage, containing methods like computerAttack(). The game logic would therefore contain references to both grids/players and would also implement the event interfaces of both grids/players. This way, the grids don't need to know each other.

M - The internal representation of the grid state (ship/water, bombed/not bombed, ...)

V - The GUI components (grid, buttons, UI event listener interfaces, ...)

C - Game logic (implemented in a separate class or package)

Sam
  • 7,778
  • 1
  • 23
  • 49
  • 2
    +1 for MVC; a grid-based example is cited [here](http://stackoverflow.com/a/3072979/230513). – trashgod Apr 27 '13 at 16:40
  • Thanks for this. I have been trying to follow MVC, but got a little lost recently! – Andrew Martin Apr 27 '13 at 16:41
  • Also +1 for MVC. Wasn't sure if it would be a little abstract just to mention it, but that's certainly the best approach IMO. – Paul Grime Apr 27 '13 at 16:45
  • As a follow up to this, in my two grid classes I have mouse listeners. These are used to color in the cells when a ship has been placed, but I currently have code that alters the internal representation of the grid, so it knows where the ships have been placed. Does that belong in the listener, or should that be abstracted out and done via a method call? Or should it be in the game logic class? Thanks - getting a bit lost with the MVC! – Andrew Martin Apr 27 '13 at 17:27
  • I'd do it this way: Create an own listener interface like `GridListener` as suggested by Paul Grime. Transform the `MouseEvent`s to `GridEvent`s within the `GridPanel` (GUI/view class). Then, let the game controller implement the `GridListener` which modifies the internal `GridState` accordingly (a separate class), what in turn causes a corresponding update of `GridPanel` by calling methods like `GridPanel.setFieldState(int x,int y, int state)` – Sam Apr 27 '13 at 17:36
  • Thanks for this - also, where do I create my Controller? I tried doing it in the main UI, but because I am creating my subclasses like this: "Grid ownGrid = new PlayerGrid()", they are being passed as Grid references, but are then trying to be manipulated as PlayerGrids which is causing errors. – Andrew Martin Apr 27 '13 at 17:49
  • You're wlcome. The two grid types would be visualized differently, while each reflects a separate instance of the same state class `GridState`. You could define the base class `Grid` as an abstract class or interface, which enforces methods like `Grid.setFieldState(int x,int y, int state)` to be implemented by `PlayerGrid` and `OpponentGrid`, each individually of course. – Sam Apr 27 '13 at 18:05
  • Regarding the `GameController` and `MainUI` instance, I usually create another class, e.g. `BattleShipApp`, which contains these objects, as well as the static main method if required. – Sam Apr 27 '13 at 18:24
1

Perhaps don't think of PlayerGrid and ComputerGrid separately. Treat them both as just Grids. The Grid would maintain the Grid's game state. You would have one Grid for player and one for computer.

Then you could have a GridListener interface, that has callbacks for significant events on the Grid. Like attackSuccess(int x, int y) and attackFailed(int x, int y).

So if there is a method like Grid.attack(int x, int y), it would roughly be:

boolean shipRemoved = removeShipAt(x, y);
if (shipRemoved) {
    // return true means that a ship was present and was removed.
    for (GridListener l: gridListeners) {
        l.attackSuccess(x, y);
    }
} else {
    // return false means that a ship was not present.
    for (GridListener l: gridListeners) {
        l.attackFailed(x, y);
    }
}

This way, the Grids don't directly communicate with each other, but can listen to the other's events. Or a higher-level game controller can listen to both Grid's events, and notify the other Grid(s) appropriately.

Paul Grime
  • 14,970
  • 4
  • 36
  • 58
  • +1 : Following MVC, your `GridListener` could be implemented by view classes like a `GridPanel`. Except for the logic being partially implemented in `Grid` (assuming this is a model class), I agree with your answer. The events should just be state change events, IMO. I tend to handle view updates within a separate `ViewContoller`, `Renderer`, or even that single, common controller. – Sam Apr 27 '13 at 17:00