This is my code for a 15 puzzle.
The basic gist is to get a grid of equal width and height (size) and number them in ascending order, with the exception of the last square getting a 0 value ('0' integer not shown on the tile so it looks like a blank tile). The grid is scrambled and then the user has to click tiles either next to or above the 'blank tile' to swap their places. The grid goes green when it is reordered in ascending order with the 'blank tile' being at last spot on the grid.
The default size is 4, hence the 15 puzzle name, but my 15 puzzle accepts any size (within reason).
FifteenPuzzle
import java.util.Arrays;
public class FifteenPuzzle
{
private int[][] grid;
private int xspace; // xspace,yspace are the current coordinates of the space
private int yspace;
private final int size; // the number of tiles across and down
private final int[][] goal; // the tile positions in the goal state
private int movecount = 1000;
//sets up the grid using initialGrid
public FifteenPuzzle (int[][] initialGrid)
{
size = initialGrid.length;
goal = setGoal();
grid = initialGrid;
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
if (grid[x][y] == 0) {
xspace = x;
yspace = y;
}
}
}
}
// sets up the grid by copying goal and then making random moves.
public FifteenPuzzle (int size)
{
if (size < 2 || size > 10) {
throw new IllegalArgumentException("Invalid size");
}
this.size = size;
goal = setGoal();
grid = new int[size][size];
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
grid[x][y] = goal[x][y];
if(goal[x][y] == 0) {
xspace = x;
yspace = y;
}
}
}
scramble();
}
public void scramble() {
for(int moves = 1; moves <= movecount; moves++){
//do a set amount of random moves.
boolean done = false;
while (!done) {
double num = Math.random();
if (num <= 0.25) {
if(legalClick(xspace + 1, yspace)) {
moveTile(xspace + 1, yspace);
done = true;
}
}
if (num > 0.25 && num <= 0.5) {
if(legalClick(xspace - 1, yspace)) {
moveTile(xspace - 1, yspace);
done = true;
}
}
if (num > 0.5 && num <= 0.75) {
if(legalClick(xspace, yspace + 1)) {
moveTile(xspace, yspace + 1);
done = true;
}
}
if (num > 0.75) {
if(legalClick(xspace, yspace - 1)) {
moveTile(xspace, yspace - 1);
done = true;
}
}
}
}
}
public int[][] setGoal() {
int[][] goal = new int[size][size];
for (int a = 1; a <= size; a++) {
for (int b = 1; b <= size; b++) {
goal[a-1][b-1] = a + size*(b-1);
}
}
goal[size-1][size-1] = 0;
return goal;
}
public int[][] getGrid()
{
return grid;
}
public int getSize()
{
return size;
}
// Returns true if x,y is on the board and adjacent to the blank space.
public boolean legalClick(int x, int y)
{
if (x < 0 || y < 0 || x > size-1 || y > size-1) {
return false;
}
return ( (x == xspace && (y == yspace - 1 || y == yspace + 1)) || (y == yspace && (x == xspace - 1 || x == xspace + 1)) );
}
public boolean finished()
{
return Arrays.deepEquals(grid, goal);
}
// swaps the tile at x,y with the space;
public void moveTile (int x, int y)
{
if (!legalClick(x, y)) {
return;
}
grid[xspace][yspace] = grid[x][y];
xspace = x;
yspace = y;
grid[xspace][yspace] = 0;
}
}
SimpleCanvas
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleCanvas {
private JFrame frame;
private CanvasPane canvas;
private Graphics2D graphic;
private Image canvasImage;
private boolean autoRepaint;
public SimpleCanvas(String title, int width, int height, Color bgColour) {
frame = new JFrame();
canvas = new CanvasPane();
frame.setContentPane(canvas);
frame.setTitle(title);
canvas.setPreferredSize(new Dimension(width, height));
frame.pack();
Dimension size = canvas.getSize();
canvasImage = canvas.createImage(size.width, size.height);
graphic = (Graphics2D) canvasImage.getGraphics();
graphic.setColor(bgColour);
graphic.fillRect(0, 0, size.width, size.height);
graphic.setColor(Color.black);
frame.setVisible(true);
this.autoRepaint = true;
}
public void drawLine(int x1, int y1, int x2, int y2, Color c) {
setForegroundColour(c);
graphic.drawLine(x1, y1, x2, y2);
}
public void drawRectangle(int x1, int y1, int x2, int y2, Color c) {
setForegroundColour(c);
graphic.fillRect(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
}
public void drawString(String text, int x, int y, Color c) {
setForegroundColour(c);
graphic.drawString(text, x, y);
graphic.setFont(new Font("TimesRoman", Font.BOLD, 20));
if (autoRepaint)
canvas.repaint();
}
public void drawString(int n, int x, int y, Color c) {
drawString(n + "", x, y, c);
}
public void setForegroundColour(Color newColour) {
graphic.setColor(newColour);
}
public Color getForegroundColour() {
return graphic.getColor();
}
public void setAutoRepaint(boolean autoRepaint) {
this.autoRepaint = autoRepaint;
}
public void repaint() {
canvas.repaint();
}
public void addMouseListener(MouseListener ml) {
canvas.addMouseListener(ml);
}
public void addMouseMotionListener(MouseMotionListener mml) {
canvas.addMouseMotionListener(mml);
}
class CanvasPane extends JPanel {
public void paint(Graphics g) {
g.drawImage(canvasImage, 0, 0, null);
}
}
}
FifteenPuzzleViewer
import java.awt.event.*;
import java.awt.*;
public class FifteenPuzzleViewer implements MouseListener
{
private FifteenPuzzle puzzle;
private int size;
private SimpleCanvas sc;
public FifteenPuzzleViewer(FifteenPuzzle puzzle)
{
this.puzzle = puzzle;
size = puzzle.getSize();
sc = new SimpleCanvas(size*size-1 + " Puzzle", 100*size, 100*size + 100, Color.white);
sc.addMouseListener(this);
if(puzzle.finished()) {
drawGreenGrid();
} else {
drawGrid();
drawGrid();
}
}
private void drawGrid()
{
int[][] grid = puzzle.getGrid();
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
drawTile(x*100, y*100, Color.RED);
if(grid[x][y] != 0 && grid[x][y] < 10) {
sc.drawString(grid[x][y], x*100+45, y*100+55, Color.BLACK);
} else if (grid[x][y] != 0) {
sc.drawString(grid[x][y], x*100+40, y*100+55, Color.BLACK);
}
}
}
for (int y = 0; y < size + 1; y++) {
sc.drawLine(0,y*100,size*100,y*100, Color.BLACK);
}
for (int x = 0; x < size; x++) {
sc.drawLine(x*100,0,x*100,size*100, Color.BLACK);
}
}
private void drawGreenGrid()
{
int[][] grid = puzzle.getGrid();
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
drawTile(x*100, y*100, Color.GREEN);
if(grid[x][y] != 0 && grid[x][y] < 10) {
sc.drawString(grid[x][y], x*100+45, y*100+55, Color.BLACK);
} else if (grid[x][y] != 0) {
sc.drawString(grid[x][y], x*100+40, y*100+55, Color.BLACK);
}
}
}
for (int y = 0; y < size + 1; y++) {
sc.drawLine(0,y*100,size*100,y*100, Color.BLACK);
}
for (int x = 0; x < size + 1; x++) {
sc.drawLine(x*100,0,x*100,size*100, Color.BLACK);
}
}
private void drawTile(int x, int y, Color c)
{
sc.drawRectangle(x, y, x+100, y+100, c);
}
public void mousePressed(MouseEvent e)
{
puzzle.moveTile(e.getX()/100, e.getY()/100);
if(puzzle.finished()) {
drawGreenGrid();
} else {
drawGrid();
}
}
public void mouseReleased(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public static void main(String[] args) {
FifteenPuzzle p = new FifteenPuzzle(3);
FifteenPuzzleViewer fpv = new FifteenPuzzleViewer(p);
}
}
setForegroundColor
is missing in action for me. \$\endgroup\$