Size Classes – Close, but Not Quite

By Kevin Packard                        twitter@kevinpackard

One Storyboard to Rule Them All

At WWDC 14, Apple introduced Unified Storyboards and Size Classes. With a single universal storyboard, it’s now possible to design layouts for iPhones and iPads, in landscape and portrait, from a single nib. I like the idea of a single resource for all devices, but Apple’s implementation falls short for practical use.

Specifically, I’m coding an app UI now. No, not Three Straight – a new app in partnership with taptaptap.  This app has quite different iPhone 4 and iPhone 5 layouts. With Size Classes, iPhone 4 & 5 are indistinguishable. If I have to hand-code the iPhone 4 constraints, I might as well hand-code everything. So, I’ve abandoned Size Classes for this project.

Before you say, “Change the iPhone 4 layout to match the iPhone 5, stupid!” let me point out that this design was created by a very well-respected designer, Wolfgang Bartleme, whose work includes Camera+, Convert, and Fantastical. I’m the developer. Wolfgang is the designer. It’s not my prerogative to change his design. Furthermore, if the tool constrains the design, then Size Classes is not the right tool.

But it’s close.

 

But but but … what about the iPhone 6 !?!

I get why Apple is pushing Unified Storyboards. We currently have six* screen variations to design for.  Rumors of a new supersized iPhone, the mythical iWatch, and split-screen multitasking iPads suggest an explosion of screen sizes in the near future. Also, the new Xcode emulator supports arbitrary sized devices – a sure sign of the geometric spacial chaos to come.

The current Size Class definition yields 9 generic layout variants: Any/Compact/Regular for both height and width. In either dimension, Regular & Compact layouts inherit properties from Any, but can override constraints for custom layouts.

Initial layout is done in Interface Builder, using [Any,Any]. Variant layouts for iPhone vs. iPad, portrait vs landscape, are done by changing constraints in variants such as [Any,Compact], or [Regular,Regular].

Great! But again, there’s no way to distinguish iPhone 4 from iPhone 5 with these generic variant definitions.

This leaves me in a position of guessing, without confidence, what my layout will look like on a new super sized iPhone 6.  Without hard info on the iPhone 6 resolution, this is the best I can do.  But given the unacceptable results for iPhones 4 & 5, I will undoubtably be modifying the iPhone 6 layout once it ships.

 

A Modest Proposal

This proposal extends Size Classes, making them more practical for real-world applications, while retaining the intended flexibility of dynamic layout.

I propose additional Size Class definitions for specific devices and orientations. Each inherits properties from a generic variant, but can change constraints for device-specific designs:

iPhone 4 Portrait: “~iphone-portrait”, inherits from [Compact w, Regular h]
iPhone 4 Landscape: “~iphone-landscape”, inherits from [Compact w, Compact h]**
iPhone 5 Portrait: “~568h-portrait”, inherits from [Compact w, Regular h]
iPhone 5 Landscape: “~568h-landscape”, inherits from [Compact w, Compact h]**
iPad Portrait: “~ipad-portrait”, inherits from [Regular w, Regular h]
iPad Landscape: “~ipad-landscape”, inherits from [Regular w, Regular h]

These new Size Class definitions provide device-specific control of layouts. Developers are encouraged to work with generic layouts, and only use device-specific Size Classes when necessary.

When a new device is introduced, like a new super-sized iPhone perhaps, then Apple creates new Size Class definitions:

“~iphone6-portrait”, inherits from [Compact w, Regular h]
“~iphone6-landscape”, inherits from [Compact w, Compact h]

An existing app will not know about the “iPhone6” layouts, and will lay out using the generic variants [Compact w, Regular h] or [Compact w, Compact h]. If this is good enough, then generic layout wins!  But if the new device calls for a new design, a developer can release an update with specific layouts for iPhone6-portrait and iPhone6-landscape.

Similarly, split-screen multitasking was recently unearthed in iOS 8. These odd layouts are handled with new Size Class definitions:

“~ipad-landscape-half”, inherits from [Regular w, Regular h]
“~ipad-landscape-third”, inherits from [Compact w, Regular h]
“~ipad-landscape-quarter”, inherits from [Compact w, Regular h]
“~ipad-portrait-half”, inherits from [Regular w, Regular h]

And even the mythical iWatch gets a definition, for the app extensions we all hope to write:

“~mythicalWatch”, inherits from [Compact w, Compact h]

 

Practical, but Unclean

My proposal dirties up Apple’s clean, generic Size Class definitions. But here’s the thing: Designers know best. When a designer calls for a device-specific layout, the tool has to handle it. Size Classes can’t handle my current design, and I see plenty of problems with future screen sizes. Therefore, my tool is Objective-C. Ugh. But until Apple fixes Size Classes, I don’t see a any other solution.

_________________________________

(*) the six existing screens are: (iPhone4/iPhone5/iPad x Portrait/Landscape). When presenting content on an external display via an Apple TV, there are two more: 1080p and 720p.

(**) this should probably be [Regular w, Compact h], but that’s a separate discussion. In fact, I suspect this odd definition is an artifact of attempting to fit specific device screens into generic Size Class definitions – something my proposal addresses.

 

Whither Three Straight?

By Kevin Packard                        twitter@kevinpackard

I can’t quit you baby.  So I’m gonna put you down for awhile.

I haven’t posted for some time, but not because I’ve abandoned Three Straight.  So what happened?  A couple things.  Like … I left a stable, well-paying Staff Engineering position at Shoretel to work from home and chase some dreams.

But I’m getting ahead of myself.  Here’s how things unfolded.

 

Opportunity Costs

First, I realized that if I was to keep my full-time job at Shoretel,  it was going to take a year or two to get Three Straight polished and released.  That would be fine, and fun!  But the opportunity costs were high.  Specifically, I wouldn’t be able to finish the other projects on my plate.

 

Project One

The first project is an iPhone app for TapTapTap that I’ve been developing on and off for some time now.  This project absolutely must come before Three Straight, for many reasons — first and foremost being that I promised John Casasanta that I would finish.  And now that I’ve left ShoreTel, I’ve been able to crank it up.  It’s oh so close to completion.  Very soon, I hope to make the announcement that it’s in the app store.

 

Project Two

The second project is an overhaul of the venerable OS X application MacRail, a machine-vision system originally called Autovision, with roots back to the late 1970’s.  In 2004, I undertook the arduous task of bringing MacRail to OS X.  Working with another engineer, it took 10 months to complete.  Since then, I’ve done occasional patches and extensions to MacRail, but it is sorely in need of a complete conversion to Cocoa.  I estimate this to take roughly 12 months of full-time work.  Now … it’s not possible to take on the 12 month MacRail project while working full-time at ShoreTel.  Conversely, 12 months of contract work is certainly not worth leaving ShoreTel.  So why did I leave?  Because of the third project … (!)

 

Project Three

The third project is another app with TapTapTap.  I can’t say anything about it yet (oh okay, it’s an iPad game, but that’s all I’m saying!).  It should be done around the end of this year.  I’m working for an equity stake this time, instead of an hourly contract.  It has a lot of potential, and I’m really excited about it.  Like MacRail, this project is way too big to take on while holding down a full-time job.  The true opportunity cost of staying at ShoreTel is not doing this project.

So, I resigned.  My last day at ShoreTel was January 4 of this year.  On January 7 I started working from home, spending half time on the MacRail contract, and half time working on my TapTapTap projects.  The MacRail contract pays the bills, and if all goes well, the TapTapTap game will hit the app store before the end of the year and start generating revenue.

That’s the plan, Stan.

 

Yes, but whither Three Straight?

Project Four is Three Straight.  It doesn’t have the same revenue potential as the TapTapTap game, so it must come last.  But believe me – it will happen.  I might even create a Kickstarter Campaign to sell physical game boards.

 

I couldn’t be happier … 🙂

Blocks, and a Responsive User Interface

By Kevin Packard                        twitter@kevinpackard

Just in time for Apple’s WWDC, I connected the Three Straight game engine to the proto-UI. The result is a very playable game, now available for preview on your iPad, iPhone, or iPod Touch.

I already had the brain: a working game engine running on my Mac, using the Mini-Max algorithm.  And I had a body: the board and marble prototype UI. All I had to do was connect them together.

 

All This, and Brains Too !

Here’s the code that connects brain to body:

 1.  - (void) dragDidEnd:(MarbleView*)marble {
 2.     Position position = [self boardPositionFor:marble.center]; 
 3.     BOOL legalMove = [_engine opponentTurn:marble to:position]; 
 4.     [self updateMarblePositions:FAST]; 
 5.     if( legalMove == YES && _engine.isGameOver == NO) { 
 6.        [_engine computerTurn]; 
 7.        if( _engine.isGameOver ) 
 8.           [self updateMarblePositions:FAST]; 
 9.        else
10.           [self updateMarblePositions:SLOW]; 
11.     } 
12.  }
13.  
14.  - (void) updateMarblePositions:(NSTimeInterval)duration {
15.     [UIView beginAnimations:@"UpdateMarbles" context:nil];
16.     [UIView setAnimationDuration:duration];
17.
18.     for( int i = 0; i < 4; ++i ) {
19.        _playerMarble[i].center = [self boardLocationFor: 
20.                         [_engine positionForPlayerMarble:i]];
21.        _computerMarble[i].center = [self boardLocationFor:
22.                         [_engine positionForComputerMarble:i]];
23.     }
24.  
25.     [UIView commitAnimations];
26.  }

 

The human player makes a move by dragging a marble to a new board position.  The method -dragDidEnd is called when the player lets go of his marble.  On line 2, a board position is calculated from the marble’s new location.  On line 3 the player’s move is passed to the game engine.  If the move is legal, the engine updates its internal model of marble positions, and returns YES.  If the move is illegal, the engine returns NO.

Line 4 serves two purposes.  If the move is illegal, the player’s marble is returned to its old position.  You can demonstrate this by dragging a marble to the other side of the board, and letting go.  If the move is legal, the player’s marble is near the new position, but not exactly on it: his marble needs to be moved to the exact location, so that it appears to settle into place.

The method -updateMarblePositions (Lines 14-26) starts a Core Animation sequence.  The duration of the animation sequence is passed in: FAST for 1/4 second, or SLOW for 1 full second.  This method is non-blocking.  That is, the animation sequence starts, but –updateMarblePositions returns immediately.  The animation continues for the supplied duration, and will complete some time in the future.

If the player’s move is legal and the game is not over, then Line 6 asks the engine to make a move for the computer.  The operation is synchronous, and can take several seconds while the game engine “thinks”.  It will not return until it’s finished.  When it does return, the internal model of marble positions is updated with the computer’s move.

Lines 7-10 animate the computer’s marble to its new position.  Note that it moves the marble slowly (SLOW), unless it wins, in which case it pounces rapidly (FAST).  I’m told that this little bit of personality adds to the addictiveness of the game … (!)

 

The Frankenstein Moment

I tried it.  It didn’t work.  At least not as intended.

When I made a move, my marble didn’t settle into place.  Instead, it stuck where I let go: near the board position, but not directly on it.  Once the computer was done thinking, my marble snapped into place, while the computer’s marble smoothly animated to its new position.

But wait – when I made an illegal move, my marble nicely flew back to its original position.  Hmmm…

Looking at the code, it’s pretty clear that Line 6 is the culprit.  Asking the engine to calculate the computer’s move causes the main thread to block while the engine “thinks”.  And apparently, blocking the main thread immediately after setting up a Core Animation sequence prevents the sequence from starting.  Who knew?

 

Back to the Drawing Board

I thought about adding a timer to delay the engine.  Or putting the engine on a separate thread.  Or using lower-level Core Animation methods.  Instead, I decided to experiment with Blocks.

 

Blocks. What are they?

A Block is a C-language extension that describes an anonymous chunk of work: an anonymous function, plus the data referenced by that function.  Blocks are used for callbacks, iteration, and concurrency.  They’re often declared inline, which improves readability.  There is a wealth of information online about blocks on Wikipedia, and on Apple’s Developer site, so I won’t cover the details.

 

Blocks for Concurrency

In this case, I’m using Dispatch Blocks to manage concurrent work.  That is, we want to schedule the game engine to execute in a Block asnychronously, so the main thread is free to animate marbles.

For concurrency, a Dispatch Block must be scheduled on a queue.  The iOS operating system maintains two important queues for an app:

– the main queue: blocks execute serially, on the main thread. Any block that manipulate the user interface must execute on the main thread.

– the global queue: blocks execute in parallel, using any available resources. Blocks on the global queue may not touch the user interface.

Here’s a new -dragDidEnd method, using blocks:

 1.  - (void) dragDidEnd:(MarbleView*)marble {
 2.     Position position = [self boardPositionFor:marble.center]; 
 3.     BOOL legalMove = [_engine opponentTurn:marble to:position] 
 4.     [self updateMarblePositions:FAST]; 
 5.     if( legalMove == YES && _engine.isGameOver == NO) { 
 6.        dispatch_async( dispatch_get_global_queue(0,0), ^{
 7.           [_engine computerTurn]; 
 8.           dispatch_async( dispatch_get_main_queue(), ^{
 9.              if( _engine.isGameOver ) 
10.                 [self updateMarblePositions:FAST]; 
11.              else
12.                 [self updateMarblePositions:SLOW]; 
13.           });    
14.       }); 
15.     }
16.  }

What’s changed? Line 6 does three things. First, the ^{ marks the beginning of a block that ends on Line 14. Second, dispatch_get_global_queue(…) returns the system’s global background queue.  Third, dispatch_async(…) schedules the block for asynchronous execution on that queue. All together, Line 6 reads “asynchronously execute this block on the system’s background thread.

Line 7, the expensive game engine “thinking” now takes place asynchronously in the background, along with the rest of the block. This means -dragDidEnd now completes without blocking, and the animation created on Line 4 is free to run.

After the engine has finished thinking, the new marble positions need to be updated. However, this block is not executing on the main thread. It’s not safe to touch the user interface. So Line 8 schedules a second block to execute on the main thread, by calling dispatch_get_main_queue() to get the main thread queue. This second block defines the animation that updates the board with the computer’s move.

The results are perfect: the game engine begins thinking at the same time the UI is animating the last marble move.  Try it yourself, by making moves as fast as possible.  If you’re quick, you can make your move while the computer’s marble is still animating, and start the game engine thinking before his last move is even in place.

The main takeaway is:

– blocks are declared inline, which improves readability

– blocks are lighter-weight than threads and work queues, and far less error-prone.

 

Now for Something You’ll Really Like

I hope. The game engine currently recurses serially in a depth-first search for the best possible move. I’ve squeezed about all I can out of the algorithm for detecting wins and losses. In order to gain further speed, I need to leverage all available cores. To leverage cores, I need to convert the engine to blocks. It’s a work in progress, involving the creation of private dispatch queues, and block synchronization. It will be interesting to see what happens when hundreds of thousands of blocks are scheduled to run concurrently.

If it works, it has the interesting effect of changing the search pattern from depth-first to breadth-first. The advantage is that when the computer is pinned down, and can only move one or two marbles, the search can run deeper in the same amount of time as a shallower search with four marbles to move.  Breadth-first searching also allows for better search tree pruning, which is another place to improve the engine speed.

Interested in Previewing Three Straight?

By Kevin Packard                        twitter@kevinpackard

At last, after many delays, I’ve finally set up a Three Straight preview.

I’m using TestFlight to manage the distribution.  To create a TestFlight account and register your iPhone, iPad, or iPod Touch, click here:

 

TestFlight will install some necessary software on your device. Register as many devices as you like.  Then, within a day or so, I will make a Three Straight build for you.

Once I create a build, TestFlight will send you a notification emali with a download link.
From your device, click the link. Three Straight will install on your device, and you can start playing.

What happens after that?
– as I add new features to Three Straight, I will make new builds available.
– you will receive a notification email for each new build. Just click he link to install.

 

The Rules

  1. You are Red.  The computer is Blue.
  2. You start.
  3. Take turns moving one space in any direction.  No jumps.
  4. The first player who gets three marbles in a straight line wins.
  5. Shake to reset.

 

Nothin’ Fancy

Three Straight has just barely come alive.  Only single-player mode is supported.  The computer plays at about a Medium ability.  There’s no way to change levels, or play online, or play with two people — yet!  These features and more are coming as soon as possible.

 

Thank You !

Thanks for participating. Please give feedback by leaving comments on this forum.  And please follow @threestraight on twitter!

Kevin Packard, http://blackfrog.com@kevinpackard

Game Theory – MiniMax

By Kevin Packard                        twitter@kevinpackard

Making the Three Straight Game Engine

Three Straight can not succeed without a single-player mode.  I need a Three Straight Game Engine, an artificial intelligence opponent that can play a mean game of Three Straight.

If Design is the skin, then the Game Engine is the brain.  Last month, nearly one year to the day after prototyping Three Straight, I started work on a Three Straight game engine.

 

First, some background

As a freshman in high school, I wrote a Tic-Tac-Toe game on the Apple ][, in BASIC.  The logic was a combination of special-cases and brute-force searching for a win.  It sometimes won, never lost, and definitely wasn’t pretty.

As a sophomore in college, I wrote Othello (aka Reversi) for the Macintosh.  An Othello game is over in 60 moves.  60 moves didn’t sound like much, and I naively thought the computer could brute-force search all possible moves and select the best one.  So I coded up the game engine, and ran it.  Nothing happened.  A bug?  No.  It was thinking.  And thinking.  And thinking…  Turns out that evaluating all possible Othello moves in a reasonable time was not going to happen on a circa 1986 Macintosh.  So with 19-year-old gusto and naiveté, I started hacking.  I tried to get the computer to play Othello like I did – grab the corners, build edges, avoid positions next to empty corners, grab positions two-away from corner spots…  But in truth, I’m not a very good Othello player.  And my program was even worse.  It played like a drunken Frankenstein version of myself, and was easily beaten by nearly everyone.  Disappointing.

 

No hacking this time

I approached the Three Straight game engine with more deliberation.  With science!  Okay, I starting by hitting The Google.

There’s an amazing amount of information available on game theory.  A Yale course on Game Theory looked promising, and it was educational and entertaining, but it failed to provide specifics.  I also found many Othello and tic-tac-toe game engine source code repositories.  But none of these helped.

 

MiniMax

I narrowed the search to Wikipedia, and finally found what I was looking for.  The MiniMax algorithm.

function integer minimax(node, depth)
    if node is a terminal node or depth <= 0:
        return the heuristic value of node
    α = -∞
    for child in node:      # evaluation is identical for both players 
        α = max(α, -minimax(child, depth-1))
    return α

(from Wikipedia)

MimiMax is ingeniously simple.  When it’s the computer’s turn to move, the Game Engine generates a score for each possible move, then selects the move with the highest score.  To score a particular move, the Engine first looks to see if that move is a win or a loss.  A win is scored as ∞ and a loss is scored as -∞.

A move that’s neither a win nor a loss is scored by evaluating the opponent’s possible moves in response to the computer’s move.  To do this, the Engine temporarily makes the evaluation move, then “switches sides” and selects the best move for the opponent.  Evaluating an opponent’s best move is identical to evaluating the computer’s best move: a win is ∞, a loss is -∞, and all other moves are scored by once again switching sides, back to the computer this time, and evaluating the next set of moves.

In other words, the Minimax algorithm works by looking ahead: “if I move here, my opponent will move there, and then I will move here, …”

Finally, there must be a limit to how far ahead the Engine will look.  Minimax limits the number of look-aheads by setting a maximum search depth.  That is, the Engine will only look ahead so many turns.  If it reaches the “look ahead” limit  and does not find a win or a loss, the Engine must generate a score for the board based solely on the state of the board.  This is the tricky part: how to assign a numeric score to a Three Straight board that is neither a win nor a loss?

The Minimax algorithm itself does not solve this problem.  Minimax is a generic algorithm, that can be applied to Tic-Tac-Toe, Othello, chess, or any turn-based game.  The key to making Minimax work for Three Straight is figuring out how to numerically score a move, given only a snapshot of the board and the sequence of moves that brought the game to this point.  No more looking ahead, because the Engine has already looked as far as it’s allowed.

Before getting into the solution, let’s take a look at generic Minimax in action.  It’s essential to truly understand MiniMax, before thinking about how to numerically score a Three Straight board.

 

MiniMax in Action

Here’s an example of an imaginary turn-based game.  In this imaginary game, each turn has only 1 or 2 possible moves.  The computer’s moves are represented by squares, and his opponent’s moves are circles.  We’ve limited Minimax to a 4 move “look-ahead”.

Here, the opponent has just moved (level 0), and it’s the computer’s turn.  The computer has two possible moves (level 1).  MiniMax will evaluate them both, and choose the move with the largest value.

The MiniMax algorithm at work (image from Wikipedia).

Starting at level 1, MiniMax evaluates the leftmost move first.  It’s neither a win nor a loss, so MiniMax must determine a score.  He has not reached the look-ahead limit of 4, so he’ll evaluate this move by switching sides, and score each of his opponent’s two possible moves (the left two nodes of level 2).  When they are both scored, he’ll select the smallest.

To score the two opponent moves at level 2, MiniMax evaluates the leftmost first.  It’s neither a win nor a loss.  The look-ahead limit of 4 has not been reached, so MiniMax evaluates his own possible moves (the left two nodes of level 3), leftmost first.

The leftmost node on level 3 is neither a win nor a loss, so MiniMax evaluates his opponent’s moves (left two nodes of level 4), leftmost first.  The leftmost node at level 4 is neither a win nor a loss, but the “look-ahead” limit has been reached.  The imaginary board must be evaluated in place.  In this example, MiniMax examines the board and assigns a score of 10.  Remember, that’s the hard part, but this is an imaginary example, so just play along.  MiniMax has now scored the leftmost move at level 4 with a value of 10.

MiniMax evaluates at the second level-4 move and detects a win, scoring this move with a value of ∞.  With both level-4 moves scored, MiniMax can choose the score for preceding the level-3 move.  Because he’s evaluating his opponent’s move, MiniMax selects smaller score, a 10.  He now has a score for the first level-3 move: a 10.

The second level-3 move has only one possible level-4 move, which MiniMax scores as 5.   The level-3 move inherits the smallest score of his children, or child in this case, so the level-3 move is scored as 5.

With both level-3 moves scored at 10 and 5, MiniMax scores the level-2 move with the larger of the two level-3 scores: 10.  He chooses the larger this time, not the smaller, because he’s evaluating one of his own future moves, not one of his opponent’s.

This process continues, scoring level-4 moves based on state of the game, and scoring higher level moves with the largest value for a computer turn, or the smallest value move for an opponent turn.  Scores percolate up from the bottom of the search tree, until finally, at the top of the tree, the computer’s move is apparent.

Then the computer makes his move for real, and it’s his opponent’s move.

Ingenious and simple.  Except for the one remaining problem: how to evaluate a Three Straight board that’s neither a win nor a loss?

 

Jumping the Shark

At this point I began to over-think the problem.  What is the numeric value if a Three Straight board?  I sketched out patterns that set up wins – the “wedge”, the “picnic bench”, the “lightning bolt”, the “rose” – names by brother and I came up with, playing as kids.  I iterated through opponent marble positions for these patterns, and began assigning a range of values, depending on blocking positions.  I generated yet another set of values for these patterns when they occurred on a board edge.  In short, I started designing a system that played like me.  And frankly, I’m not that good.  My father and brother regularly beat me.

Egads!  I was repeating my Othello game engine mistake!

But this time I recognized it.

 

Jumping Back

So, I postponed the problem.  I went with a very simplistic evaluation: a win is ∞, a loss is -∞, and everything else is 0.

This decision let me move ahead.  Time to write some code!  After three days, much to my delight, the very simplistic evaluation turned out to be enough for a very smart Three Straight game engine.  Because, you see, a simple evaluation executes quickly.  The quicker the evaluation, the more boards that can be evaluated.  The more boards that can be evaluated, the deeper the search.  The deeper the search, the smarter the Engine.

 

Cruel Engine, Taunting Engine

Actually, I had to make one minor tweak.  MiniMax doesn’t specify which move to choose when there are tying high scores.  What if there are two winning moves?  I decided to randomly choose one.

This led to an interesting effect.  Let’s say the Engine sees two moves that are guaranteed wins.  One win is one move away.  The other win is guaranteed, but it’s 3 moves away.  MiniMax evaluates both moves as ∞, because both are guaranteed wins.  A human would always choose the immediate win, but MiniMax sees them as equal, and chooses one at random.  And if MiniMax happens to choose the 3-move win over the obvious and immediate win, then suddenly the Engine seems cruel, as if it’s taunting its human opponent, because it’s prolonging the agony of defeat.

This was not the desired effect.  For a fix, I made a small tweak.  Instead of assigning ∞ and -∞ for wins and losses, I assigned +1,000,000,000 and -1,000,000,000.  Then I “aged” the scores as they moved up the tree, by multiplying them by 0.9 at each level.  This changed the evaluation so that an immediate win is scored as +1,000,000,000, but a guaranteed win that’s 3 moves in the future is scored as +1,000,000,000 x 0.9 x 0.9 = 810,000,000.  Now, the Engine selects the immediate win over the guaranteed future win.  It now kills rapidly, and with purpose. Which somehow makes it kinder.

 

Satisfaction

I have to say, it’s extremely satisfying to have a smart, functional Three Straight game engine.  It’s come alive, and it has personality.  Level 2 (look ahead 2 moves) is perfect for beginners.  At Level 4, it plays a pretty good game, but can still be tricked.  At Level 7, it only looses when its opponent starts first and makes no mistakes.  At Level 8, it does not loose.  Ever.  These translate directly to the four levels of game play: Beginner, Intermediate, Difficult, and Inhuman.

For those interested the code itself, I’ll cover that in the next post.

Prototype

By Kevin Packard                        twitter@kevinpackard

Three Straight was prototyped in three evenings, fourteen months ago.  The first two nights were spent creating artwork and organizing the project.  Everything came together the third night, with a small bit of code.  The resulting game has no turns, no rules, no bells & whistles when someone wins.  Marbles are dragged freeform around the board.  But two people can sit down and play a game, on an iPad or an iPhone or an iPod Touch.  It’s a decent prototype.

 

Evening One

Xcode project setup and board artwork.  I started by Googling up a hirez photo of a beautiful piece of mahogany.

 

I used OmniGraffle, my favorite overpriced graphing app, to bevel the edges and add the drill holes and burn marks.

 

I want Three Straight to run on as many iOS devices as possible.  There are three screen sizes to deal with: the iPad, the iPhone 4 & 4S (retina screen), and the iPod & older iPhones. I generated images of the board in three resolutions to match. Interestingly, the gamma of the iPad’s display is lighter than the iPhone’s gamma.  I lightened the iPhone image, so the iPhone and iPad match side-by-side.

Instead of targeting the iPad and iPhone separately, Three Straight is built as a single Universal app that runs on all iOS devices.  In Xcode, I made a Universal project containing layouts for each target device.  I need two layouts, one for the iPad and one for the iPhone & iPod.  Each layout has a single fullscreen  UIImageView  instance, containing the board image.  The resulting app shows the board image full screen, and runs on the iPad and iPhone.  No coding thus far.

I realize that the artwork is a bit rough.  It needs more polish before the game is finished.  In fact, I don’t know if I want wood grain, or some other look. Maybe metallic. Or Trontastic?!  Clearly, I’ll have to engage a graphic artist for the final artwork.  But for now, this board image has a classic look that’s guaranteed to invoke nostalgia in anyone who’s played the original wooden marble game.

 

Evening Two

Nearly a total waste.  I tried, and failed, to create some marble images.  Finally I gave up, and stole these solar system marble images online.  Later I’ll replace these with something better, something original.

I kind of like the solar system look, and found some other images that might work too.  But they’re owned by Dan Wiersema, and aren’t free for commercial work.

What I have is good enough for now.  I generated red & blue marble images in three resolutions.  For the eight marbles, I added eight UIImageView‘s to each of the two layouts.

 

By the end of that second evening, I had an application running on my iPad and iPhone, with a  board, and some marbles that didn’t move.  Still no coding.

 

 

Evening Three

Three Straight came alive with a few lines of code, to make the marbles draggable.  The board & marble images are displayed with  UIImageView  instances.  A  UIImageView  isn’t draggable by default, so I created a MarbleView class to handle dragging, by subclassing  UIImageView.

Touch event delegate methods are defined in UIResponder.h. Methods are defined for handling an object being touched, released, or dragged.  To handling marble dragging, I don’t care when the marble is touched or released, just when it’s dragged.  So I overloaded just the –touchesMoved:withEvent: method.  The code is simple:

#include MarbleView.h
@implementation MarbleView
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.superview];
    self.center = location;
}
@end

First, the drag code needs to extract a touch, any touch.  iOS handles multi-touch gestures, such as pinching to zoom a photo. All touch delegate methods handle multi-touch, so the delegate is passed not a single touch, but a set of touches. If the user has a single finger on the marble, the set of touches will contain just one UITouch instance. But if the user somehow manages to get two or three fingers onto the marble, the set will contain two or three UITouch‘s. Dragging is not multi-touch, so the drag code cares only about the first touch. The method -anyObject conveniently returns one UITouch instance from the set of potentially many touches.

Second, the location of the touch is extracted, in the coordinate system of the marble’s container.

Third, the marble’s position is changed to center on the touch location.

When a marble is dragged around the board, –touchesMoved:withEvent: is called repeatedly, and the marble smoothly follows the finger.  This code isn’t optimal: if you start dragging a marble from its edge, there’s an initial snap as the marble centers on the drag.  But it’s good enough for a prototype.

I was pleasantly surprised to discover that all eight marbles can be dragged at once, with eight fingers on the board.  Apple’s iPad and iPhone touch screens – at least their newer products – can handle at least eight simultaneous touches.  Impressive.

By the way, the header file for MarbleView doesn’t declare anything.  The dynamic binding of Objective-C resolves the –touchesMoved:withEvent: method at runtime.

#import <Foundation/Foundation.h>
@interface MarbleView : UIImageView {
}
@end

 

So now a prototype exists.  It’s been in my pocket for the past year.  There are no turns, no rules, no bells & whistles when someone wins. But two people can sit down and play a game.  Pretty cool.

Ask, and I’ll give you a demo.  Even better: if you have an iPad / iPhone / iPod, I’ll put it on your device and you can watch the game grow from a prototype to a finished product.

 

 

Beginning

Welcome to the Three Straight development blog, a fascinating perspective on iOS development.

Or not so fascinating … depends on wether or not you’ve interested in how iPhone apps are created.

I’m developing a game for the iPhone, iPad, and iPod.  I’ll cover the entire process here: design, programming topics, game theory, marketing, and the App Store.  If this sounds interesting, read on!

Here’s an RSS feed for new posts.  Or follow @threestraight on twitter.

Thanks for your time,
Kevin Packard, http://blackfrog.com@kevinpackard

——————–

About Me

I’ve been developing software professionally for more than two decades.  Until 2009, I consulted full-time under the corporate name blackfrog software, but now I have a regular day job, creating VoIP telephone software on embedded Linux for enterprise desk phones.  In the evenings, I dabble with iPhone apps.

I wrote the code for the iPhone app Convert, designed byTapTapTap.  Convert was released in 2009, and climbed to the #2 spot in the App Store, before falling back down.  This was both gratifying, and inspiring – inspiring enough to do an app on my own.

I’m wrapping up another project for TapTapTap now, after which I’ll start Three Straight in ernest.

 

About The Game

Three Straight was invented in the 1970’s by a high school math teacher in Alaska named Bob Packard.  Bob happens to be my father.  He used to make the game boards out of mahogany in his woodshop, then he’d take my brother & I to school where we’d thrash on his calculus students.

The rules are simple.  Eight marbles are placed on the board, four for each player.  Players alternate turns moving marbles, one space only, no hops.  A player wins by arranging three of his marbles in a row – Three Straight!  The game is deceptively simple, but the strategies are complex.

 

Getting Started

I expect to begin Three Straight in late-February.  In the mean time, I have some ideas about how the game will appear visually, and what features I’d like to support with the first version.  I’ve created a prototype game board, and I’ve also prototyped an artificial intelligence game engine.  I’ll post about these in the upcoming weeks.

Thanks for reading.

By Kevin Packard                        twitter@kevinpackard