Today I provide an updated Xcode project for our Minesweeper clone. This version implements a lot (but not all) of the design we covered on Monday, including all the gameplay features aside from auto-clear. You can download the project here; I discuss it, in very general terms, below.
To Do List
With reference to the design laid out on Monday, the current version of the project is missing these features:
- There is no puzzle setup screen; The “+” button just adds a 30×16/99 puzzle to the list
- There’s no high-score tracking
- Auto-clear is not implemented
- Since we don’t have final puzzle generation done, we also don’t have in-app purchases working
Nothing has happened to affect the design of any of these elements; it’s just a matter of implementing them. We’ll see how that’s going in the next update.
Changes of Plans
As I implemented Monday’s design, I did hit a couple of snags that caused me to tweak that design. My first problem was that it just didn’t seem possible to stuff a reasonably-sized
UISegmentedControl into the nav. bar along with the back button and the status information. I didn’t want to steal more screen space from the playfield, but didn’t see any reasonable alternative. Since I was going to have to go vertical anyway, I decided to put the control in its own
UIToolbar, which would, at least, make the buttons nice and big and easy to hit.
My second problem had to do with changing the tint on the nav. bar. Since the nav. bar is really part of the
UINavigationController, and shared among all child views, it doesn’t seem natural to change its tint from screen to screen. Although the API provides a simple interface to change the bar’s tint, I couldn’t find a way to do this on a per-screen basis that didn’t produce nasty visual artifacts. Therefore, I abandoned this idea, and settled for tinting the toolbar and the title text.
Along the implementation trail, there were a few shiny things that caught my eye. Let me share them with you.
I chose to implement a lot of the game logic (e.g., what happens when you try to uncover/flag a cell, or when the game’s status changes to “won” or “lost”) in the
Puzzle object, which is essentially a “model” object. I don’t have a big theoretical defense of this decision; the code just seemed to fit there. The only major piece of game logic (that I can think of) outside of the
Puzzle object is the timer code, which must be pretty intimately bound up with the UI, since it essentially tracks how much time the puzzle has spent on screen.
As I mentioned, I couldn’t find a good way to change the tint of the nav. bar from screen to screen. In fact, even changing the color of the title text was a hassle. To do this, I had to assign a
UILabel as the
titleView of the relevant
UINavigationItem, and then set the properties of that
UILabel. Making matters more difficult was the fact that the API seemed to stick the custom title view to the immediate right of the back button; i.e., the custom view was not centered by default. To center my title I had to experimentally determine where the
UILabel's left side was being placed, and derive a width from that which placed its centerline in the middle of the screen. Nasty. Maybe there’s an easier way.
My project contains two separate puzzle models:
ManagedPuzzle. This isn’t really defensible, but it’s the way I’m going.
Puzzle was built up as an the appropriate in-memory representation of a puzzle. To fill this role, it was given certain attributes (e.g., lots of read-only properties, a raw byte array for internal storage) that aren’t particularly friendly to Core Data (CD). I’m sure that
Puzzle could be hammered into a shape that would work as a CD managed object, but that looks to be pain for no gain; I don’t need any of CD’s object-graph-management features, I just want to use it as an ORM interface to the DB. The easiest way to marry the
Puzzle class to CD was by using an intermediate model class –
The current code doesn’t produce particularly random puzzles. We’re using
rand() to generate our puzzles, and since we’re not seeding it, we’ll always generate the same series of puzzles every time we start the program. At this stage of development, that’s probably more of a feature than a bug, though it will have to be fixed at some point in the future.