Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
About
JAVA Q&A JavaWorld's in-house tutor takes questions on
By Jeff Friesen, JavaWorld core Java programming topics in this blog
DEC 13, 2015 12:48 PM PT dedicated to Java beginners everywhere.
TUTORIAL
Checkers, anyone?
Develop a Swing-based library that presents a checkers game user interface.
Several months ago, I was asked to create a small Java library that can be accessed by an
application to render a graphical user interface (GUI) for the game of Checkers. As well as
rendering a checkerboard and checkers, the GUI must allow a checker to be dragged from one
square to another. Also, a checker must be centered on a square and must not be assigned to a
square that's occupied by another checker. In this post, I present my library.
Board
Checker
CheckerType
Player
A Board object identies the checkerboard. It serves as a container for Checker objects that
occupy various squares. It can draw itself and request that each contained Checker object draw
itself.
A Checker object identies a checker. It has a color and an indication of whether it's a regular
Sign In | Register
checker or a king checker. It can draw itself and makes its size available to Board, whose size is
inuenced by the Checker size.
CheckerType is an enum that identies a checker color and type via its four constants:
BLACK_KING, BLACK_REGULAR, RED_KING, and RED_REGULAR.
A Player object is a controller for moving a checker with optional jumps. Because I've chosen
to implement this game in Swing, Player isn't necessary. Instead, I've turned Board into a
Swing component whose constructor registers mouse and mouse-motion listeners that handle
checker movement on behalf of the human player. In the future, I could implement a computer
player via another thread, a synchronizer, and another Board method (such as move()).
What public APIs do Board and Checker contribute? After some thought, I came up with the
following public Board API:
Board(): Construct a Board object. The constructor performs various initialization tasks
such as listener registration.
void add(Checker checker, int row, int column): Add checker to Board at the
position identied by row and column. The row and column are 1-based values as opposed
to being 0-based (see Figure 1). The add() throws
java.lang.IllegalArgumentException when its row or column argument is less than 1
or greater than 8. Also, it throws the unchecked AlreadyOccupiedException when you
try to add a Checker to an occupied square.
void draw(Graphics g, int cx, int cy): Draw a Checker using the specied
graphics context g with the center of the checker located at (cx, cy). This method is
intended to be called from Board only.
boolean contains(int x, int y, int cx, int cy): A static helper method called
from Board that determines if mouse coordinates (x, y) lie inside the checker whose center
coordinates are specied by (cx, cy) and whose dimension is specied elsewhere in the
Checker class.
int getDimension(): A static helper method called from Board that determines the
size of a checker so that the board can size its squares and overall size appropriately.
This pretty much covers all of the checkers GUI library in terms of its types and their public
APIs. We'll now focus on how I implemented this library.
Listing 2. Board.java
import java.awt.Color; Sign In | Register
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
// dragging flag -- set to true when user presses mouse button over checker
// and cleared to false when user releases mouse button
public Board()
{
posChecks = new ArrayList<>();
dimPrefSize = new Dimension(BOARDDIM, BOARDDIM);
addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent me)
{
// Obtain mouse coordinates at time of press.
int x = me.getX();
int y = me.getY();
@Override
public void mouseReleased(MouseEvent me)
{
Sign In | Register
// When mouse released, clear inDrag (to
// indicate no drag in progress) if inDrag is
// already set.
if (inDrag)
inDrag = false;
else
return;
int x = me.getX();
int y = me.getY();
posCheck.cx = (x - deltax) / SQUAREDIM * SQUAREDIM
SQUAREDIM / 2;
posCheck.cy = (y - deltay) / SQUAREDIM * SQUAREDIM
SQUAREDIM / 2;
addMouseMotionListener(new MouseMotionAdapter()
{
@Override
public void mouseDragged(MouseEvent me)
{
if (inDrag)
Sign In | Register
{
// Update location of checker center.
@Override
public Dimension getPreferredSize()
{
return dimPrefSize;
}
@Override
protected void paintComponent(Graphics g)
{
paintCheckerBoard(g);
for (PosCheck posCheck: posChecks)
if (posCheck != Board.this.posCheck)
posCheck.checker.draw(g, posCheck.cx, posCheck.cy);
Sign In | Register
// Draw dragged checker last so that it appears over any underlying
// checker.
if (posCheck != null)
posCheck.checker.draw(g, posCheck.cx, posCheck.cy);
}
// Paint checkerboard.
Because of the extensive comments, I haven't much more to say about Board. However, note
the nested PosCheck class, which describes a positioned checker by storing a Checker
reference and its center coordinates, which are relative to the upper-left corner of the Board
component. When you add a Checker object to the Board, it's stored in a new PosCheck object
along with the center position of the checker, which is calculated from the specied row and
column.
1 2 NEXT