A simple number-tile slider game The following puzzle was inspired by Andy Hertzfeld's puzzle desk accessory for the original Macintosh.

Hertzfeld was able to write the original version in approximately 600 bytes of 68000 machine code, starting with a Pascal version that was over 6 kilobytes.

This version is in Java, and after compilation, the executable is somewhat over 2000 bytes (a fair bit of that is Java runtime information, not bytecode per se).

To play, click on the tile you want to move. Tiles can only move into the empty space. The game is over when the numbers are arrayed in ascending order, with the 1 in the upper left corner, 2 in the square immediately to the right, and so on. The empty square must be at the lower-right corner.

Press your browser's "refresh" button to get a new puzzle.

Since it is so short, I've included the original source code here:

/*
 * Copyright (C) 2007 Stan Chesnutt.  All rights reserved.  You are
 * welcome to reuse this code in any project, commercial or otherwise,
 * as long as you mention my name in the documentation for the program
 * (perhaps in an "about" box, or a credit page, etc).
 *
 * comments/questions to chesnutt at gmail dot com
 */

/*
 * A simple number-tile-slider applet.  A tribute to Andy Hertzfeld
 */

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;

public class SliderApplet extends Applet implements MouseListener
{
    int[] board;
    int new_x, new_y;
    int offsets_x[] = {0, 0, 1, -1};
    int offsets_y[] = {1, -1, 0, 0};

    /*
     * Applet:init()
     */
    public void init() {
        board = new int[16];
        Random random = new Random();
        int blank_x, blank_y;

        new_x = -1;
        new_y = -1;

        /*
         * preset the board
         */
        for (int i = 1; i < 16; i++)
            board[i - 1] = i;

        blank_x = 3;
        blank_y = 3;

        /*
         * simulate a bunch of tiles sliding around
         */
        for (int i = 0; i < 200; i++) {
            int r = random.nextInt(4);
            int new_x = blank_x + offsets_x[r];
            int new_y = blank_y + offsets_y[r];

            if ((new_x >= 0) && (new_y >= 0) && (new_x < 4) && (new_y < 4)) {
                int blank;
                /*
                 * slide piece from new_x,new_y to x,y
                 * and make the square new_x,new_y blank
                 */
                blank = new_y * 4 + new_x;
                board[blank_y * 4 + blank_x] = board[blank];
                board[blank] = 0;
                blank_x = new_x;
                blank_y = new_y;
            } else
                i--; // couldn't move, so retry
        }

        addMouseListener(this);
    }

    /*
     * Applet:paint()
     *
     * Redraw the applet panel
     */
    public void paint(Graphics g) {
        Dimension d = getSize();
        int cellWidth = d.width / 4;
        int cellHeight = d.height / 4;
        int cell_x = new_x / cellWidth;
        int cell_y = new_y / cellHeight;
        boolean win = true;

        /*
         * make the player's move
         */
        if (new_x != -1) {
            int here = cell_y * 4 + cell_x;
            int value_here = board[here];

            if (value_here != 0) {
                for (int i = 0; i < 4; i++) {
                    int try_x = cell_x + offsets_x[i];
                    int try_y = cell_y + offsets_y[i];

                    if ((try_y >= 0) && (try_x >= 0) && (try_x < 4) &&
                        (try_y < 4)) {
                        int there = try_y * 4 + try_x;

                        if (board[there] == 0) {
                            board[there] = value_here;
                            board[here] = 0;
                        }
                    }
                }
            }
        }

        /*
         * check for a win.  If so, draw colored background
         */
        for (int i = 0; i < 16; i++)
            win = win && (board[i] == (i + 1) % 16);

        if (win) {
            g.setColor(Color.RED);
            g.fillRect(0, 0, d.width, d.height);
        }

        g.setFont(new Font("TimesRoman", Font.BOLD, (cellWidth * 2) / 3));

        /*
         * draw tile numbers
         */
        for (int i = 0; i < 16; i++) {
            int x = i % 4;
            int y = i / 4;
            int v = board[i];

            if (v != 0) {
                String value = Integer.toString(v);

                /*
                 * draw a small tile-like outline
                 */
                g.setColor(Color.GRAY);
                g.fillRoundRect(x * cellWidth + 6, y * cellHeight + 6,
                                cellWidth - 12, cellHeight - 12, 10, 10);

                /*
                 * draw the text of the tile
                 */
                g.setColor(Color.BLACK);
                g.drawString(value,
                             x * cellWidth + (cellWidth / 2) -
                             (value.length() * (cellWidth / 4)),
                             (y + 1) * cellHeight - (cellHeight / 4));
            }
        }

        new_x = -1;
        new_y = -1;
    }

    /*
     * MouseListener.MouseEntered()
     */
    public void mouseEntered(MouseEvent event) {
    }

    /*
     * MouseListener.MouseExited()
     */
    public void mouseExited(MouseEvent event) {
    }

    /*
     * MouseListener.MousePressed()
     */
    public void mousePressed(MouseEvent event) {
    }

    public void mouseClicked(MouseEvent event) {
        new_x = event.getX();
        new_y = event.getY();
        repaint();
    }

    /*
     * MouseListener.MouseReleased()
     */
    public void mouseReleased(MouseEvent event) {
    }
}
$Header: /home/cvs/htdocs/stan/slider.html,v 1.2 2007/01/14 18:04:29 chesnutt Exp $