Classic Puzzle

Classic game of eight movable tiles in a 3 × 3 square with one missing. The rows are numbered from –1 to +1 from top to bottom; the columns are numbered – to +1 from left to right. That means that tile (0, 0) is in the center. The nine .png files, however, have numbers that are zero-based.

The original photo from the Wikipedia article was chopped into nine smaller pieces (of which eight are displayed) by a Unix shellscript using the Netpbm toolkit. You probably would rather chop it up using a different application.

The white background is one big View. The init method of the View (called by the loadView method of the view controller) creates eight smaller TileViews. Each TileView is derived from class UIImageView and holds a 100 × 100 UIImage. It also holds a row number and a column number (each in the range –1 to 1 inclusive) which change as the TileView is moved around. We saw class UIImage here, and will see class UIImageView here.

subclass of UIView is specialized for holding a
UILabel single-line String
UITextView multi-line String
UIImageView UIIimage

The View contains the row number and column number of the currently empty location. The View calls hitTest(_:withEvent:) to find out which subview (if any) was touched. The subview contains its own row and column number. If the subview is not landlocked, it animates into the empty location.

Source code in

  1. Class PuzzleAppDelegate
  2. Class ViewController. The loadView method creates the big white View.
  3. Class View
  4. Class TileView: the property userInteractionEnabled makes the TileView touch-sensitive.
  5. Images.xcassets, an Xcode asset catalog file. See America. Each imageset consists of only one image. The lower right image (22.png) is not used by this app.
    00.png 01.png 02.png
    10.png 11.png 12.png
    20.png 21.png 22.png

Create the project

America tells you how to add the image files to the project’s Images.xcassetts file.

When creating class TileView, make it a subclass of class UIImageView:

Choose options for your new file:
Class: View
Subclass of: UIImageView

Things to try

  1. Rearrange the pieces randomly when the app is launched. Append the following code to the init(frame:) method of class View.
    		for var i = 0; i < 100; ++i {
    			//Pick a random number in the range 0 to 7 inclusive.
    			//Then convert the random number from UInt32 to plain old Int.
    			let r: Int = Int(arc4random_uniform(8));
    			//Pick a random TileView, i.e., one of the last 8 items in the
    			//subviews property of this View.
    			let tileView: TileView = subviews[subviews.count - 1 - r] as TileView;
    Would it be more dramatic if we increase the duration of the animation?

  2. Count (and display) how many moves it takes the user to solve the puzzle. Rank the user:
    		String rank;
    		if nmoves < 10 {
    			rank = "genius";
    		} else if nmoves < 20 {
    			rank = "brighter than average";
    		} else //etc.

  3. Punish the user with a beep if he or she touches a TileView that cannot move.