sábado, 27 de junho de 2020

A Battleship Game using JavaFX


Battleship Game during action

A few months ago I started a Battleship API which I could use to teach Java for beginners. Create games is always fun, in this blog we created a JavaFX Pong game with only 90 lines of code and also a Stacker Game. Both games required constar screen redrawing and the logic behind the games were very simple, Battleship is different.

Battleship is a board game with rules that can be translated into Java code. The board has a state according to players guesses and the game ends if a certain state is acquired which is: a player is able to sink all the ships from the other player. Once we have rules we can easily test it.

In this post we will share the API behind our game, show the JavaFX UI and show what could be improved.

Just show me the code!

Battleship Game API


The goal with the API design is make it immutable and fluent, but I didn't work to make it perfectly fluent or immutable, because at some time I wanted to start creating the JavaFX part, which is fun! We can divide the API in two main parts: Model and Logic. 

The Model part contains classes that holds information necessary for the game, these classes are:

Player: A class that holds a player information;
Ship: An enum with all ships available in the game (see battleship rules);
Location: Immutable class that holds board X, Y location; 
ShipPosition: An immutable class that holds a ship position that can be assigned to a board;
Board: Contains all the ships for a board and the board state in a 2d boolean array;
GameState: The current game state.

Also in the model you will find the classes that gives life to the game:

BoardGame: An board based game with two players. This class controls the current player turn and the score.
GameManager: The class that puts everything together. Provide access to the game information and manages the game to make it playable.

These classes are enough to create a command line battleship game with two players. For one player game we need to create bots.

API improvements
Notice that some of the classes I mentioned could be a Java 14 Record. This is a refactoring that I plan to do soon. 

Game Bots


If a solo player wants to play battleship it will have to play against bots, for this purpose we have the project battleship-bot.. The bot project contains an interface that is used for bots BattleshipBot and it has a single method newLocation which produces locations that can be used to produce guess against another player. Now, bots only produces location, the implementation can decide how the location will be produced, including repeated locations. The bot API contains two implementations for BattleshipBot:

BattleshipRandomBot: A dummy bot that generate random locations but make sure that repeated locations aren't generated by keeping track of all the locations.
CheaterBattleshipBot: A cheater bot. It knows the opponent board and will generate hit locations based on a configurable random rate. This way we can adjust the game difficult.

Now we have everything to single play battleship, the only missing part is to make it playable using an User Interface.


API improvements
More bots could be added, including one that uses Reinforcement Learning as described in Deep Reinforcement Learning–of how to win at 


JavaFX User Interface

Create user interfaces in Java is fun when you use JavaFX!  The JavaFX application should be modern and have navigation thought screens, hence the first need was to create a way to navigate thought screens, to achieve this we created a screen interface and a screen manager class.

Screens


Screen: Contract for any class that wants to become an screen. It must provide the content that will be displayed in the screen, the the id, name and a title. it is also possible to override onShow method to have some code executed before the screen is displayed. For this game the following screens were created:


ScreenManager: This class is responsible to make the application flow between screens. It adds a nice transition between the content that is displayed and allow navigation between registered screens.

The application has only 3 screens:

Home Screen: A minimalist screen with a big single "Start" button

Very simple start screen

Preparation Screen: Before starting the game user can prepare its board and setup the game. The board can have ships added one by one or the user randomly prepare the ships positions. The game difficulty can also be selected and user is able to preview his board in a canvas.


This screen in action can be checked in the GIF below:

The preparation screen in action. Notice how it can generate ships positions for the impatients.



Game Screen: This screen is the game itself. It is where the game happens. The enemy board is where user can "fire" and a small board below is the user board, he can see all his enemy guess.



The game consists in selecting positions in the board. You can select and hit fire, or double click in any position.


Board painting, animations and ending


Boards: You probably noticed that the battleship board is drawn 3 times: preparation screen and game screen (two times). One may think that a reusable component, but instead we decided that we would paint a JavaFX canvas instead. We get the game state from the BattleShip API and then paint the board accordingly. It happens on class BattleshipPainter. We call it every time we need to refresh the game:

We paint a canvas to update it. Another approach would have a "Board" component.

Animations: The text with animation is controlled by a JavaFX Timeline which is built once. Since the animation is the same, we built two for each label (one for the user board and another for the opponent board), the label style is set individually and the position is simply on a StackPane on top of the board.

This is the method that creates animation.We create a Timeline with frames that modifies the target node properties. 
Ending: When the game is finished we display a VBox with some labels. The box background and border was styled. Notice how it looks like a custom panel, but it is a simply VBox styled with CSS.

Final screen with the results box.

Styling


The application was styled using JavaFX CSS. My wife helped me to select colors, it was really horrible, she did what she could to improve the overall looking of this game!

The application was styled using CSS. We override some JavaFX classes and also created a few more.



Improvements: The application layout could be changed, better resolution icons, Port the game to mobile.

Conclusion

In this post we presented a battleship game made with JavaFX. It shows how JavaFX is powerful to build desktop apps and games. For more complex games I do suggest the FXGL library.

Notice this code, with a little tweaks, could be ported to mobile, which could be a good candidate for a new writing about this game! Source code.