Introduction
This tutorial will focus on how to make use of some basic
Java Development Kit (JDK) classes and image-manipulation techniques to create a simple image puzzle game. It is intended to give you a taste of how
fun programming can be and a starting point leading to more serious
applications, if you are really encouraged by these simple fundamental tips.
After seeing so many interesting interactive games on the
Web, you are probably as motivated as I am to create one
on your own. No matter how much fun you get from those
commercial games, the chances are they never let you
add your personal touch to them. What if you could create one yourself?
Even if it is just a simple game, it can be fun, too!
In fact, with all the programming tools and interfaces available in
the standard Java package, it is not too difficult to put this thought
into practice after all.
What elements are required for an image puzzle? The game needs
to be able to take an image and properly resize the game panel.
The next step will be slicing the image into puzzle pieces. Making
customizable the number of rows and columns for the puzzle will
be useful. Now these pieces need to be randomly scrambled for
the game.
Up to this point, the game seems ready for play, but
how do we move a puzzle piece? I use a simple mouse click in this
demonstration, but you can set it up for the keyboard as well. Finally, some
features (audio, messages, etc.) need to be added when the puzzle is completed.
Of course, making a game more appealing and professional is much
more than this. It generally requires a team’s effort.
In this article, all the above issues will be described in more
detail. The following are full working source code sets illustrating our implementation.
Please let me know when you find bugs or add useful features to them.
Importing the Image
In Java, we have a method, getImage()
, to input a GIF or JPG
image file. The result is contained in an Image object. To create
the puzzle pieces, we will need to save the image into an array buffer
and do some simple cropping work. There is a JDK method available for this purpose.
PixelGrabber()
will get the pixel information
from the photo
image object and store data
in the one-dimensional integer array.
// grab pixels into a 1D array private void grabPixels() { PixelGrabber imagegrabber; imagegrabber = new PixelGrabber(photo, 0, 0, width, height, data, 0, width); try { imagegrabber.grabPixels(); } catch(InterruptedException e) {} }
Dividing the Image into Puzzle Pieces
Now we have a one-dimensional array that holds the pixel information.
The next step will be dividing the image into smaller puzzle pieces.
Suppose we want the image divided into row
rows and col
columns. We thus have a total of row * col
pieces.
Also, each puzzle piece needs to be associated with an identifier.
That way, we can use it to check whether the same puzzle piece has
been restored to its original position. order
is used for
this reason. w
is the width of each puzzle piece and h
is the height. r
, g
, and b
are for the red, green, blue channels of a pixel. They are extracted from the lower 24 bits
of the integer representing a pixel. tt
is a temporary one-dimensional array for each puzzle piece. MemoryImageSource()
in JDK is used to make an image out of an array.
// restore all the puzzle positions public void resetPositions() { for (int i = 0; i < row * col; i++) order[i] = i; repaint(); } // make each puzzle piece public void makePieces(int row, int col) { int [] tt; int idx = 0, xl = 0, yl = 0; int r = 0, g = 0, b = 0; int ww = -1, hh = -1; this.row = row; this.col = col; w = width / col; h = height / row; pieces = null; pieces = new Image[row * col]; order = null; order = new int[row * col]; for (int i = 0; i < row * col; i++) { tt = new int[w * h]; xl = (i % col) * w; yl = (int)(i / col) * h; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { idx = (y + yl) * width + (x + xl); r = (data[idx] & 0x00FF0000) >> 16; g = (data[idx] & 0x0000FF00) >> 8; b = data[idx] & 0x000000FF; tt[y * w + x] = (new Color(r,g,b)).getRGB(); } pieces[i] = createImage(new MemoryImageSource(w, h, tt, 0, w)); ww = hh = -1; while (ww < 0 || hh < 0) { ww = pieces[i].getWidth(this); hh = pieces[i].getHeight(this); } order[i] = i; } }
Scrambling the Pieces
Since all puzzle pieces now have identifiers in the array, order
,
all we need to do is randomly switch their identifiers for shuffling.
In case two identical pieces are selected, I simply increase the
second identifier by one. Once the scrambling is done, the display
area needs to be refreshed.
// rescramble the puzzle public void rescramble() { int p = 0, q = 0, tem = 0; int total = row * col; for (int i = 0; i < total; i++) { p = (int)((Math.round(Math.random() * 10 * total * total)) % total); q = (int)((Math.round(Math.random() * 10 * total * total)) % total); if (p == q) q = (p + 1) % total; tem = order[p]; order[p] = order[q]; order[q] = tem; } repaint(); }
Managing the Moves
All the puzzle pieces are now well scrambled. Interacting with the
user is the next step. When a user clicks on the puzzle, we need
to know if he/she hits a valid puzzle piece. If it is valid,
can the piece be moved? The following code does exactly that.
w
and h
are the width and height of each puzzle
piece. xl
and yl
are the coordinates for the
upper-left corner of the puzzle piece. The identifier for the
selected puzzle piece is target
. All four neighboring pieces
have been checked to see if any of them is empty. If any of
the four neighbors is empty, the selected piece can be moved
to that location. Note, we use the last available identifier, total - 1
, for the empty piece.
At the end, all the identifiers have been examined. If all of them
match the original ones, the puzzle is completed.
// do when mouse is down public boolean mouseDown(java.awt.Event evt, int x, int y) { int xl = 0, yl = 0; int target = -1; boolean done = false; int total = row * col; // check if we hit a valid puzzle piece for (int i = 0; i < total; i++) { xl = (i % col) * w; yl = (int)(i / col) * h; if (x >= xl && x < xl + w && y >= yl && y < yl + h) { moveaudio.play(); target = i; break; } } // left neighbor if (target > -1 && (target % col) != 0) { if (order[target - 1] == total - 1) { order[target - 1] = order[target]; order[target] = total - 1; done = true; } } // right neighbor if (!done && target > -1 && (target % col) != col - 1) { if (order[target + 1] == total - 1) { order[target + 1] = order[target]; order[target] = total - 1; done = true; } } // top neighbor if (!done && target > -1 && target / col >= 1) { if (order[target - col] == total - 1) { order[target - col] = order[target]; order[target] = total - 1; done = true; } } // bottom neighbor if (!done && target > -1 && target / col < row - 1) { if (order[target + col] == total - 1) { order[target + col] = order[target]; order[target] = total - 1; done = true; } } // we have hit a movable puzzle liece if (done) moves++; repaint(); // do we have a winner? if (moves > 0) { for (int i = 0; i < total; i++) { if (order[i] != i) return true; } gameover = true; } return true; }
Adding Configurable Parameters
To make the puzzle more attractive and configurable, here are
some parameter options that have been added or can be added
to the program. The current parameters available are the
photo filename, number of rows and columns to divide the
puzzle, and an audio filename for the puzzle move.
Colors and fonts are probably the most common options users
need. Caption support can be added for different language
flavors. An on-screen timer would probably increase the
playing excitement. Some interactive hints can provide
extra help for those puzzles that are difficult to solve.
I am sure you can think of many other configurable features
to make better puzzles. These are just some pointers.
Summary
In this tutorial, I briefly describe how to make a Java image
puzzle game from scratch with the basic tools available in the standard
JDK distribution and provide my own implementation
details. Of course, there are still many improvements that can be made to make it look and work better. Here are some possibilites:
- Moving a piece is probably more intuitive by dragging
instead of mouse clicks. - When a user clicks on a puzzle piece, I simply position the
piece at its destination.
Since this is a sliding puzzle, moving the puzzle piece
a small step at a time by animation is more visually
appealing. - Rectangular puzzle pieces do not resemble real-world
puzzle games. An image mask can be created to extract
the desired puzzle shape. - Some artificial intelligence algorithms can be implemented
to suggest the best puzzle piece for the next move.
Okay, you can now relax and enjoy your own little puzzle game!
References
- Ken Arnold and James Gosling, "The Java Programming Language", Addison-Wesley, 1997.
- Elliotte Rusty Harold, "Java I / O", O'Reilly and Associates Inc, 1999.
- Jerry Jackson and Alan McClellan, "Java by Example", Sun Microsystems Press, 1999.
About the Author
Chunyen Liu is
a software engineer at a global positioning company, GARMIN
International. Some of his 100-plus Java programs have won major programming contests. Check out his personal page for
more details. He also owns a Java-intensive site called
The J Maker.