Tic Tac Toe: Creating Multiple Views/Scenes

This game is coming along well as a sample project, but not as a real game. There are things I expect as a player when playing an actual game I bought on the App Store. One of those things is having more than one screen. You usually have a main menu screen, a settings screen, etc…

I don’t want to go overboard on this project, but I do want this to look like a real game. The point of this exercise was to take a simple set of requirements and execute them fully. For me a minimum viable product would have at least a main menu scene. I have other ways I would like to handle a game ending, but I am behind schedule on this project and I don’t want to get stuck polishing it forever when a simpler solution will suffice.

I want my game to have the following scenes:

  • Main Menu
  • Variant Menu
  • Main Game Scene (already created)
  • Game Over Scene

The main menu should be simple. You tap on it and it brings you to the menu where you choose what variation of tic tac toe you want to play. This leads to the main game scene where you play the game. After the game ends one way or another, this leads to a final game over scene announcing how the game ended. After a set period of time, this reloads the main menu.

SpriteKit Scene Editor

When all of this stuff was in an immutable single scene, placing objects programmatically made a modicum of sense. But right now I want to place objects as I would if I were doing a traditional iOS app. I want to create a better UI for menus and asking questions. I don’t generally do my UI in code because it’s difficult to debug. I was trying to figure out how to create buttons and layouts and was starting to freak out. At this point I am bringing in the SpriteKit scene editor.

The scene editor is SpriteKit’s version of storyboards. They provide a WYSIWYG interface for laying out game elements. This makes all kinds of sense. Games are even more visually intensive than normal UIVew based applications. I find storyboards invaluable for laying out my UI elements. Not having something like this would be idiotic.

Game Over

I actually created this one first. I have been working through the 2D games book published by Ray Wenderlich’s company. They created both the main menu and the game over menu programatically. Neither required any user interaction beyond tapping the screen or waiting. I have put myself on a personal deadline of wanting to ship this project before Sunday, May 6th. There are a lot of more custom interactions I would like to add, but I need to see if I have time or not. This is minimum viable product time.

I originally intended to custom create different end scenes based on the winner of the game, but I started to get discombobulated by trying to figure out how to adjust things for different screens. I also realized this was inherently limiting and that I could use animations if I did everything in code.

First I need my properties that are going to be used to determine how this scene looks:

import Foundation
import SpriteKit

class GameOverScene: SKScene {
    let endState:GameEndState 
    let background = SKSpriteNode(imageNamed: "bg_ipad_portrait")
    let label = SKLabelNode(fontNamed: "Noteworthy-Bold")
    var iconNode = SKSpriteNode()

I need the end state to know who won the game or if it was a draw. I need to set the background as I did for all the other scenes in the game. I need a label showing who one. Lastly, I need an icon representing the winner.

I need to initialize these properties:

init(size: CGSize, endState: GameEndState) {
    self.endState = endState
    super.init(size: size)
		
    isUserInteractionEnabled = false
		
    anchorPoint = CGPoint(x: 0.5, y: 0.5)
		
    background.zPosition = -1
    addChild(background)
		
    let labelBackground = SKSpriteNode(
        imageNamed: "headerBacking")
    labelBackground.zPosition = 2
    labelBackground.size = CGSize(
        width: self.size.width, 
        height: 60.0)
    labelBackground.position = CGPoint(
        x: 0.0, 
        y: (self.size.width / 2.0) + 100)
    labelBackground.anchorPoint = CGPoint(x: 0.5, 
                                          y: 0.5)
    addChild(labelBackground)
		
    label.fontColor = SKColor.black
    label.fontSize = 40
    label.zPosition = 10
    label.position = CGPoint(x: 0.0, y: -20.0)
    labelBackground.addChild(label)
}

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

I need to set the winner label and the icon based on what game state is passed in:

override func didMove(to view: SKView) {
		
    switch endState {
	case .playerAWin:
	    iconNode = SKSpriteNode(imageNamed: 
                "pastry_donut_320")
	    label.text = "Player A Wins!"
	case .playerBWin:
	    iconNode = SKSpriteNode(imageNamed: 
                "pastry_starcookie02_320")
	    label.text = "Player B Wins!"
	case .draw:
	    iconNode = SKSpriteNode(imageNamed: 
                "pastry_starcookie02_320")
	    label.text = "Draw"
    }

I already had actions for animating a sprite into the scene that I could reuse for the game ending scene:

    let spritePosition = CGPoint(x: 0.0, y: 0.0)
    let spriteSize = CGSize(width: 320, height: 320)
		
    // Actions
    let fadeAction = SKAction.fadeIn(withDuration: 0.75)
    let scaleUpAction = SKAction.scale(to: 
	CGSize(width: spriteSize.width * 
	1.5, height: spriteSize.height * 1.5), 
	duration: 0.5)
    let scaleDownAction = SKAction.scale(to: spriteSize, 
                                   duration: 0.25)
    let soundEffect = SKAction.playSoundFileNamed(
	"pop.wav", 
	waitForCompletion: false)
    let scaleSequence = SKAction.sequence(
	[scaleUpAction, soundEffect, scaleDownAction])
    let actionGroup = SKAction.group(
        [fadeAction, scaleSequence])
		
    iconNode.setScale(0)
    iconNode.position = spritePosition
    iconNode.zPosition = 10
    addChild(iconNode)
    iconNode.run(actionGroup)
		
    let wait = SKAction.wait(forDuration: 5.0)
    let block = SKAction.run {
	if let scene = SKScene(fileNamed: "MainMenuScene") {
	    let reveal = SKTransition.flipHorizontal(withDuration:0.5)
	    self.view?.presentScene(scene, transition: reveal)
	}
    }
    self.run(SKAction.sequence([wait, block]))
}

This scene basically hangs around long enough to display the current winner. Then, after a sufficient amount of time, it navigates back to the main menu to allow the user to begin a new game.

This was a bit more code that I particularly wanted to have to maintain. One goal on this learning project was to try and get things done faster, so for the future scenes I wanted to try and take advantage of the SceneKit Editor.

Main Menu

I had an idea about how I wanted the main menu laid out, but I didn’t want to have to do this in code. Since I only need the user to tap on the screen, I just wanted to get things generally laid out.

I created a MainMenuScene.sks file along with a MainMenuScene.swift file. All of the graphics and UI stuff will be laid out in the .sks file while all of the user interactions and behaviors live in the .swift file.

I laid out the main menu to look like this:

Scene editor, very much like Interface Builder

For a while I could not get this to load and I couldn’t figure out why. I had forgotten that in IB and the Scene editor you need to connect objects to their correlating class. So be sure to set your scenes/custom nodes/etc… to their correlating class in the Inspector.

The only think I need the main menu to do is listen for a touch. If the player touches the screen, I want it to launch a new game. Here is the code for this:

import Foundation
import SpriteKit

class MainMenuScene: SKScene {
	
    override func didMove(to view: SKView) {

    }
	
    func sceneTapped() {
	let myScene = GameScene(size: size)
	myScene.scaleMode = scaleMode
	let reveal = SKTransition.doorway(withDuration: 1.5)
	view?.presentScene(myScene, transition: reveal)
    }
	
    override func touchesBegan(_ touches: Set,
	with event: UIEvent?) {
	sceneTapped()
    }
	
}

Choose Variation

For my variations scene I want the same background as the rest of the application but I want three buttons. SpriteKit doesn’t have SKButton objects. So far my understanding of how SpriteKit works is that everything is a node. I need three nodes with textures that look like buttons indicating each variation. I need to name them so that I can use touch recognition to see which version the player chose.

The class needs to keep track of which version of tic tac toe the user wants to play. I created a new enum to hold these values:

enum GameVariation {
    case normal
    case reversed
    case swap
}

Right now these are not doing anything. The last feature I want to add to the application is the ability to actually have different rules and functionality for these variations. Right now, this is basically a stubbed out method:

import SpriteKit
import GameplayKit

class TypeChoiceMenu: SKScene {
    override func didMove(to view: SKView) {

    }
	
    // Check for node type and pass into the initializer
    func sceneTapped(_ type: GameVariation) {
	// Update GameScene init for game type
	let myScene = GameScene(size: size, variation: type)
	myScene.scaleMode = scaleMode
	let reveal = SKTransition.doorway(withDuration: 1.5)
	view?.presentScene(myScene, transition: reveal)
    }
	
    override func touchesBegan(_ touches: Set,
	with event: UIEvent?) {
		
        guard let touch = touches.first else {return}
	let location = touch.location(in: self)
	let rawNodes = nodes(at: location)
	let tappedNodes = rawNodes.filter{return $0.name != nil}
		
	guard let node = tappedNodes.first as? SKSpriteNode else {
            return
        }
		
	switch node.name {
	case "RegularButton":
	    sceneTapped(.normal)
	case "ReverseButton":
	    sceneTapped(.reversed)
	case "TileSwap":
	    sceneTapped(.swap)
	default:
	    break
	}
    }
}

As with the code to check which tile is tapped in the main game, this checks the node name of the buttons in the scene. If one is selected, then it triggers a load of the main game scene and passes that choice along to the scene.

Conclusions

This wound up being a bit harder than I expected. After having done a few scenes in the editor, I am wondering why I didn’t do this sooner. However, when I first started this feature I was incredibly frustrated by the editor and wanted to scrap my plans to use it.

Sometimes you need to work through something that seems difficult or counterintuitive in order to find a better way to do things. If I had to do this project over again I would set up the entire game in the scene editor.

The final feature I want to implement before I declare this project ready to ship is a few variations on tic tac toe to make this game slightly more interesting. Playing through this I keep seeing more and more refinements I would like to make to this project, but the point of this was to get something done and to ship it. I don’t want to get sidetracked endlessly refining this learning project rather than moving on to a more complex project.

So far there has been a lot more to this game that I anticipated when I started. But part of the point of this was to work on all the non-obvious stuff. Until next time!

Tic Tac Toe: Implementing Artificial Intelligence

There are a few ways to implement turn based games as apps. You can either set up networking to play against a live opponent or you can set up an artificial intelligence to play against.

I am working under the assumption that no one is ever going to be super eager to play against a live opponent in tic tac toe, so the best option for a minimum viable product is to implement an AI. Setting up a network for live gameplay is a goal I have for my next project, but it’s overkill for this prototype project.

I want to make the AI good enough as opposed to perfect. It’s no fun playing tic tac toe where you can’t ever win. But it is also important that the AI behave somewhat logically, as opposed to these people.

GamplayKit AI

In 2015, Apple introduced a framework called Gameplay Kit. This framework took a bunch of commonly used concepts in game development and abstracted them out into a framework that could be used universally across the SDK instead of marooning it in either SpriteKit or SceneKit. The randomization algorithms are incredibly popular for applications outside of games.

One of their modules within Gameplay Kit is a set of strategists for opponent AI. The two strategists allow for both deterministic and probabilistic opponent gameplay to be added easily to a game.

I am opting not to use Apple’s built in AI implementation. I have two specific reasons for doing so:

  • I would have to refactor my application: Gameplay Kit was specifically designed around strong object oriented programming principles. In order to utilize any of Apple’s strategists, you have to have an object representing the game model, the player, and the game update. Right now, I am handling all of these things with stand alone functions and enums. In order to take advantage of Apple’s framework I would need to add a bunch of classes and code to my project and it is my personal preference to not do this.
  • I want to understand the math: I am interested in having an understanding of the math behind various strategists. In the next game I want to implement, I would like to be able to roll custom AIs based on various criteria to give virtual players their own personality. Even if that goal is unrealistic or could be accomplished using one of Apple’s built in classes, I want to gain the understanding of how this works by building it on my own.

The remainder of this post will be explaining the strategist algorithm I want to implement and how I was able to integrate it into my existing gameplay.

MiniMax Algorithm

One of the foundational algorithms in AI is the Minimax algorithm. This isn’t a “true” AI as it’s simply a decision tree generated algorithmically. This algorithm was the original AI strategist available when GameplayKit was introduced. It’s still the basis for most AI for games with “perfect” knowledge. Perfect knowledge means that everyone involved in the game knows everything about it. Poker does not involve perfect knowledge because each player only knows the cards they have in their own hands and there is an element of randomness associated with how the cards are dealt. Chess, checkers, and tic tac toe have perfect knowledge because all the pieces and moves can be seen and known by each player.

I based my implementation of the Minimax algorithm from the chapter on adversarial search in Artificial Intelligence: A Modern Approach, Second Edition.

The normal MiniMax decision tree is exhaustive, generating every possible outcome of any given game. This becomes overly complex for even a simple game like tic tac toe. Apple gets around this by allowing you to set a depth for the tree based on number of moves it has to plan ahead. The more moves you plan ahead, the better your AI will be, but each level grows the time exponentially.

This shows MiniMax in a pared down state to give the general idea of how it is implemented.

In order to implement an AI for a board game, you need the following pieces of information:

  • The current state of the board
  • A dictionary of legal moves along with their resulting board state
  • A test to determine if the game has ended (reached terminal state)
  • A payoff function that assigns values to terminal states. In a zero sum game like checkers the states would be +1 for a win, -1 for a loss, and 0 for a draw.

I am already tracking the board state for the UI and game logic and I already have a function checking for a win or a loss. I can reuse these but I still need to add a dictionary of legal moves/state and how to calculate those values.

Modifying the Algorithm in Swift

One of the main features of the MinMax strategist is that it builds decision trees multiple moves ahead. This is great and useful for games like chess where there are a vast multitude of contingencies that the players must keep track of. However, tic tac toe has a maximum of nine moves total. If you’re playing the game properly then no one can ever win. I want to create an AI that plays properly most of the time but doesn’t always play perfectly strategically.

When I play tic tac toe, I go through three levels of processing:

  1. Can I win on this move?
  2. Could my opponent win on this move?
  3. Outside of those outcomes, what is the best strategic move I can make?

My optimal move is to win the game on this move. If that is not possible, then my next best move is preventing my opponent from winning on this turn. After these possibilities are exhausted, there are some moderately strategic moves you can make. If you’re going first, it makes the most sense to squat on the center square. If you’re not going first, then choosing a corner tends to give you more options and opportunities.

I don’t really want my AI to be as sophisticated as a human player because it would be nice to win every once and a while. While creating my decision tree, I wanted to preserve the first two questions as the game would be idiotic if I didn’t. If you can neither win nor lose on this turn, then I wanted to add an element of randomness where the AI simply chooses an empty square. This introduces enough of an opening for the AI to play badly enough that the human player may be able to win every once in a while.

Integrating the Algorithm Into the Project

First up, I want to figure out whether a particular move is a good move or not. If a move results in me winning, that’s awesome and I want that to have a positive value. If a move results in my opponent winning, that’s kind of a bummer and I want to assign that a negative value. If the move doesn’t really impact things one way or another, then I don’t really care and it has a neutral value.

func moveState(currentPlayer: GameState, 
                currentBoard: [TileState], 
			move: Int) -> Int {
    var moveValue = 0
	
    let newState = makeMove(tile: move, 
                   currentPlayer: currentPlayer, 
                           board: currentBoard)

    guard let isWin = checkForWin(currentPlayer: currentPlayer, 
                                          board: newState) else {
        return moveValue
    }
	
    switch isWin {
    case .playerAWin:
	moveValue = -1
    case .playerBWin:
	moveValue = 1
    case .draw:
	moveValue = 0
    }
	
    return moveValue
}

The return value of this function will act as the value for our state dictionary. We’re going to create that next.

We now need our key to go with its value. The key is going to be the index of the move we are making. This allows us to keep track of which tile we are actually changing. We also need to make sure we are only checking valid moves. Finally we need to return a dictionary of valid moves and their correlating values:

func possibleMoveValues(currentPlayer: GameState, 
                         currentBoard: [TileState]) -> [Int:Int] {
    var moveValueTable = [Int:Int]()
	
    for (index, tile) in currentBoard.enumerated() {
	if tile == .notSelected {
	    let moveValue = moveState(currentPlayer: currentPlayer, 
                                       currentBoard: currentBoard, 
                                               move: tile.hashValue)
	    moveValueTable[index] = moveValue
	}
    }
	
    return moveValueTable
}

Finally, we need a way to make an AI move. We can utilize the function we already created for the player making a move. But in this case, instead of checking for a touch from the UI, we are generating it using the previous methods:

func makeAIMove(currentPlayer: GameState, board: [TileState]) -> ([TileState], Int) {
    var newBoard = board
    var move:Int
	
    let possibleImmediateWinValues = possibleMoveValues(
        currentPlayer: .playerB,
        currentBoard: board)

    let possibleImmediateLossValues = possibleMoveValues(
        currentPlayer: .playerA,
        currentBoard: board)
	
    if let immediateWin = possibleImmediateWinValues.
        filter({$1 == 1}).first {
	move = immediateWin.key
    } else if let immediateLoss = possibleImmediateLossValues.
        filter({$1 == -1}).first {
	move = immediateLoss.key
    } else {
	let possibleMoves = Array(possibleImmediateWinValues.keys)
	move = possibleMoves[Int(
               arc4random_uniform(UInt32(possibleMoves.count)))]
    }
	
    newBoard = makeMove(tile: move, 
               currentPlayer: currentPlayer, 
                       board: board)

    return (newBoard, move)
}

First I am creating a new board and a move value for our return tuple. We will be populating those later in the function.

Next, I am running the possibleMoveValues method twice, once for each player. The way I structured the possibleMoveValues method was to check for a specific player’s move. Based on how I structured checkForWin, it only checks against the current player, which is passed in as a parameter. This means that by passing in Player A as a parameter, you will only check for wins for Player A. To get the wins for Player B, you need to run the method again to check for those results. I know, this is complicated and I probably could have written this differently, but that had its own complexity. Every method has tradeoffs.

Next we need to check for wins and losses. We have a preference for a win for Player B, so we check the dictionary for any values that equal 1. Once we find one, we don’t care if there are any more because a win is a win. If a win value is found, we set our move to that.

If there are no wins possible, we check for the next best thing, which is preventing our opponent from winning. We check for a move that our opponent could make to win and if one is found, we make that move to block them from winning. Again, we don’t care if there is more than one winning move for our opponent because if there is, we can’t prevent it anyway.

If nothing we do immediately results in a win or a loss, we simply choose a random square as our move. This allows just enough wiggle room that it’s possible that the AI may choose a really poor strategic value.

So we have a new board and the index of the tile that needs to be changed. This needs to be updated in the UI.

Updating and Modifying the UI

Right now, we are assuming that both players are humans and are swapping the phone back and forth. We update the UI as follows:

if board[tiles.index(of: node)!] == .notSelected {
    switch currentPlayer {
    case .playerA:
	let playerTile = SKSpriteNode(imageNamed: "pastry_donut_320")
	playerTile.setScale(0)
	playerTile.position = spritePosition
	node.addChild(playerTile)
	playerTile.run(actionGroup)
	board[tiles.index(of: node)!] = .playerA
    case .playerB:
	let playerTile = SKSpriteNode(imageNamed: "pastry_starcookie02_320")
	playerTile.setScale(0)
	playerTile.position = spritePosition
	node.addChild(playerTile)
	playerTile.run(actionGroup)
	board[tiles.index(of: node)!] = .playerB
    case .notPlaying:
	node.color = UIColor.white
	board[tiles.index(of: node)!] = .notSelected
    }
}

Basically we have a Player object that tracks the current player. This is used to determine what cookie goes on a tile and who is winning. We don’t want to keep this switch statement any more because we want a move to happen automatically after the player is able to successfully complete a valid move. This requires us to disable user interaction until after the AI has completed its move and to replicate the animation on the tile chosen by the AI.

I updated this section of the code to look like this:

if board[tiles.index(of: node)!] == .notSelected {
    isUserInteractionEnabled = false
			
    // Human player's turn
    let playerTile = SKSpriteNode(imageNamed: "pastry_donut_320")
    playerTile.setScale(0)
    playerTile.position = spritePosition
    node.addChild(playerTile)
    playerTile.run(actionGroup)
    board[tiles.index(of: node)!] = .playerA

This part concerning the human player’s turn is mostly untouched. I simply pulled it out of the switch statement and put it directly in the block. It is being run if the player touches a tile that isn’t already selected. Next, I need to check if the player has won:

// Check for Human Player Win
if let isWin = checkForWin(currentPlayer: currentPlayer, board: board) {
    label.text = isWin.rawValue
    return
} else {
    currentPlayer = switchPlayer(currentPlayer: currentPlayer)
    label.text = "\(currentPlayer.rawValue)'s Turn"
}

If the player has won, we don’t want the AI to make a move. If the player has not won, we still want the UI to update to reflect the current player. Next, I had to modify the code used for the player’s move to work for how the AI is functioning:

    // AI's turn
    let aimove = makeAIMove(currentPlayer: currentPlayer, 
                                    board: board)
    board = aimove.board
    let aiTile = SKSpriteNode(imageNamed: "pastry_starcookie02_320")
    aiTile.setScale(0)
    aiTile.position = spritePosition
    tiles[aimove.move].addChild(aiTile)
    aiTile.run(aiActionGroup)
			
    if let isWin = checkForWin(currentPlayer: currentPlayer, 
                                       board: board) {
	label.text = isWin.rawValue
    } else {
	currentPlayer = switchPlayer(currentPlayer: currentPlayer)
	label.text = "\(currentPlayer.rawValue)'s Turn"
	isUserInteractionEnabled = true
    }
}

Instead of getting the chosen node through touch and then updating the game board, we are running a method that selects a move and returns a new board state. We need to set that board state to be the new board state. We also have to use the index of the move to tell the UI which tile to update. I added a new SKAction to wait a second before implementing the animations to make it look like the AI is thinking about the move.

Again, we need to check for an AI win. If the AI wins, we again show in the UI that the game has finished. If the game hasn’t finished, we need to switch the current player back to the human and reenable touch so that they can make their next move.

Conclusions

This blog post took a while to get done. I had a lot of travel and I had a rough couple of weeks recovering from travel. I find getting off of my routine makes it difficult for me to motivate myself to get things done. So that isn’t really part of the code, but it is part of my life. I have to do a lot of things to curate my mental health and when I don’t I am not able to get things done. I am trying to do better at self care so that I can get things done and feel good about it.

I think if I were doing a more complicated game than tic tac toe the Minimax algorithm looking more than a move ahead would work well. I have feelings about the game theory and economics that gave us this theorem, but it works effectively in this game. I have been deeply interested in game AI for a while and I was really excited to get a chance to implement one for this project. I am looking forward to getting more in depth with these algorithms in future projects.

The hardest part for me in dealing with this was changing how I think about how the UI gets updated. It was difficult wrapping my head around how the UI would respond to touch and know what the specific tile was. When I got to the part updating the AI’s tile, I had a similar cognitive issue of figuring out how we are tracking things. It was actually less complicated, but there are so many moving parts that are contained within one another that it can be difficult keeping everything straight in one’s mental map.

With this section completed, I have two more features I want to add to the game before I ship it. I want multiple scene for the app, so you have a main menu screen that you see before you begin playing. One reason I want this main menu scene is for the other feature, which is variations on tic tac toe. I want to add two variations. One will be simple and the other will be slightly more complicated.

When I have completed these features, I intend to submit this as a free app to the App Store. I want to be able to catalog what I needed to do on this blog. I think it’s likely that Apple will not allow this game on the store as it’s not particularly original. They have been cracking down on student projects and highly derivative apps on the store to try and keep the clutter down.

I understand their perspective, but it’s unfortunate because many employers require you to have an app on the store before they will consider hiring you. There is a limit to the complexity of an app you can create on your own in a limited amount of time without a paying job. By removing this as a platform where students can put portfolio projects, they’re hurting people who are just starting out and are not actually trying to gain a massive audience. There is no other distribution network for iOS applications and it’s onerous to expect employers to have Xcode and to build source code to device. I will see if I am able to get through this barrier or not. Until next time!

I bought this to be the featured image at the top, but WordPress cropped it so all you saw were boobs. Gall and brimstone!

Tic Tac Toe: Graphics, Labels, and Animations

I have a roughed out version of my tic tac toe game running. If my point was to just get something working, I could call it a day at this point. However, the goal I have with this project is to put together a somewhat professional game. This requires a lot of polish and a lot more work.

The focus I have in this blog post is to do the biggest immediate thing I can do to make this look like a real game. This includes real graphics and not just colors that I used as placeholders. I also want to give the user feedback about whose turn it is and what the state of the game is. Additionally I want to add some interest to the game with animations so it feels more responsive than it does now.

Graphics

This section will focus on incorporating graphics. I got a graphic design degree ten years ago and I haven’t used it much in the last decade. One of my longer term goals is to work more with graphics programs so I can get more comfortable with them. But for this project I am opting to use assets created by other people.

One resource I like a lot is Game Art Guppy. If you have worked through any tutorials from Ray Wenderlich, then the art on Game Art Guppy should look familiar. Ray’s wife Vicki did all of the art assets for the site for a number of years. Game Art Guppy is her market for low cost art assets for developers. Many assets on the site are free and none are over $25.

Since my other major hobby is cooking, I decided to use the pastry icon set from the site for my squares. I know Xs and Os are traditional, but I want this to be whimsical. There is a correlating background that goes with this tile set. These assets were used in an epic Candy Crush tutorial on Ray’s site. I highly recommend it because I learned a lot from working through it.

First I placed the background image behind the board. When I initially added the background it covered the other parts of my board, so I made its z-position -1 so that it would appear behind the board:

let background = SKSpriteNode(imageNamed: "bg_ipad_portrait")
background.zPosition = -1
addChild(background)

I chose the donut and one of the sugar cookies for the Xs and Os because they kind of looked a little like Xs and Os. I want to keep the white background but just apply the texture on top of it. I also want to animate the tiles, but that will happen later. Here is the code I used to get this working:

switch currentPlayer {
    case .playerA:
	let playerTile = SKSpriteNode(imageNamed: "pastry_donut_320")
	playerTile.scale(to: node.size)
	playerTile.position = CGPoint(x: node.size.width / 2, 
				      y: node.size.height / 2)
	node.addChild(playerTile)
	board[tiles.index(of: node)!] = .playerA
    case .playerB:
	let playerTile = SKSpriteNode(imageNamed: "pastry_starcookie02_320")
	playerTile.scale(to: node.size)
	playerTile.position = CGPoint(x: node.size.width / 2, 
				      y: node.size.height / 2)
	node.addChild(playerTile)
	board[tiles.index(of: node)!] = .playerB
    case .notPlaying:
	node.color = UIColor.white
	board[tiles.index(of: node)!] = .notSelected
}

Each player has a pastry associated with them. If I just change the texture of the containing node to the pastry icon then they will appear to float in space, so I am making the pastry a new sprite and making it a child of the containing node. The pastry icon is way too large to fit inside the containing node, so it needs to be scaled to fit within the tile. Additionally, the anchor point of the tile is in the bottom left, so I am positioning the new sprite to sit in the middle of the sprite node.

Finally, I want to have a real app icon. I always forget to make a generic one for sample projects and tech talks and it constantly bothers me. When I see other people went to the trouble it makes me feel crappy and inadequate, so really wanted to make sure I did that on this project.

I chose the sugar cookie as my main focus on the app icon because I thought it was the more visually interesting sprite in the set. I used Illustrator to place the sprite in the view and exported it as PNG. I then used Photoshop to change the size and apply the correct labels to each size. I believe Illustrator has an option to do all of this in one step, or a least fewer than I did, but I didn’t figure out how to make that work. I have been told Sketch does this as well, but I don’t own it and I’m unfamiliar with it, so I used the thing I am comfortable with.

Labels

I want to use a label at the top of the screen to give information to the user. I want to be able to tell the user which player’s turn it is and how the game has ended. Right now I am just printing it out to the console, but that can only be seen by me when I run this in the simulator.

SpriteKit has a specific node for labels. I created one inside the class declaration but outside of any methods:

let label = SKLabelNode(fontNamed: "Noteworthy-Bold")

I created a backing image for the label, which I need to add as a sprite node to the main node hierarchy:

let labelBackground = SKSpriteNode(imageNamed: "headerBacking")
labelBackground.zPosition = 2
labelBackground.size = CGSize(width: boardSize, height: 60.0)
labelBackground.position = CGPoint(x: 0.0, y: (boardSize / 2.0) + 100)
labelBackground.anchorPoint = CGPoint(x: 0.5, y: 0.5)
addChild(labelBackground)

I need to add the label node as a child of the label background node. I set the anchor point for the label background to the middle of the node so that the text can be placed in the center of the node.

label.text = "Tic Tac Toe"
label.fontColor = SKColor.black
label.fontSize = 40
label.zPosition = 10
label.position = CGPoint(x: 0.0, y: -20.0)
labelBackground.addChild(label)

I want raw string values associated with both my player state and my win state so that I can post them in the label for the user. Here are my updates to my state enums:

enum GameState: String {
    case notPlaying
    case playerA = "Player A"
    case playerB = "Player B"
}

enum GameEndState: String {
    case playerAWin = "Player A Wins!"
    case playerBWin = "Player B Wins!"
    case draw = "Draw!"
}

I tried attaching an observer to the current user to update the label whenever the current user changed, but that not only resulted in never telling the user that the game was over, it also didn’t prevent the game from continuing. Instead, I explicitly update the label in the line of code after I update the user. I do this in the initializer but I also have to do that on the touch event:

if let isWin = checkForWin(currentPlayer: currentPlayer, board: board) {
    label.text = isWin.rawValue
    isUserInteractionEnabled = false
} else {
    currentPlayer = switchPlayer(currentPlayer: currentPlayer)
    label.text = "\(currentPlayer.rawValue)'s Turn"
}

Previously I was checking for each specific win condition, but since that is being tracked in the raw value I can just post the end state in the label. I don’t want the player to continue to play if the game is over, so I need to disable user interaction. If the game isn’t over, then the current player switches and the label updates.

Animations

Next I want to animate the tiles as they are selected. This works, but it’s a little boring. It’s also a little abrupt to just have an image pop up when you touch a tile. Adding an animation to the action really gives a bit of life to the application.

One thing that confused me greatly about SpriteKit was how animations were done. I knew that animations had to be a huge component of SpriteKit, but there was no SKAnimation class. I thought maybe this descends from UIKit and you would need to use Core Animation to get this to animate. After some digging I found that there is an animation class in SpriteKit, but it’s called SKAction. Why they changed it, I can’t say. People just liked it better that way.

At some point I would like to do a dedicated blog post to SKAction, but for this project I am trying to simply focus on doing what I need to for the effect that I want. I want to create some kind of transition in with the sprites so they don’t just pop out of nowhere on the tiles. I want the sprite to fade in. I also want my sprite to scale up and kind of pop out a little before scaling back to the proper size.

First, I pulled a few properties out of the code blocks that I need to calculate my actions:

let nodeSize = node.size
let largeNodeSize = CGSize(width: nodeSize.width + 30, 
			   height: nodeSize.height + 30)
let spritePosition = CGPoint(x: node.size.width / 2, 
			     y: node.size.height / 2)

Next, I created my action outside of my switch statement. I want to use the same actions for both Player A and Player B, so there isn’t any point in creating those in-line. I wanted to place them outside of the touches method, but there are properties on objects that I need to check inside that method, so the actions are declared there.

SKAction has two ways of grouping actions together: Groups and Sequences. Groups are actions that need to run together at the same time. Sequences are actions that run one after another. I need to utilize both groups and sequences for my sprite animations. The scale up and scale down need to be part of a sequence because one follows the other. That sequence has to be grouped with the fade in so that it sprite both fades in and scales simultaneously.

Here is my action code:

// Actions
let fadeAction = SKAction.fadeIn(withDuration: 0.75)
let scaleUpAction = SKAction.scale(to: largeNodeSize, duration: 0.5)
let scaleDownAction = SKAction.scale(to: nodeSize, duration: 0.25)
let scaleSequence = SKAction.sequence([scaleUpAction, scaleDownAction])
let actionGroup = SKAction.group([fadeAction, scaleSequence])

You may notice that there is no code here to specify what the starting scale is for the sprite. That needs to be set when the sprite is created. My refactored tile sprite code now looks like this:

case .playerA:
    let playerTile = SKSpriteNode(imageNamed: "pastry_donut_320")
    playerTile.setScale(0)
    playerTile.position = spritePosition
    node.addChild(playerTile)
    playerTile.run(actionGroup)
    board[tiles.index(of: node)!] = .playerA

Music and Sounds

The last bit of polish I want to add to the project is music and sound effects. One interest that I have in game development is doing music and sound effects. Before I went into programming, I went to school for audio engineering and video production. My specific area of interest was sound design. I loved to sit down with an animated scene and remove all the sound and add it back as a project. One of my sound design projects can be seen here.

I need more practice to get back up to speed with sound design, along with graphics and illustration. Those are concerns for another time. But needless to say, I understand the value of music and sounds to making something feel like a finished product. So I wanted to make sure I had nice sounds for the game.

I want background music and I want a nice little “popping” sound effect for when the sprite pops into the tile. I don’t have time right now to make my own sounds or music, so I needed to find decent free sounds and music to include in the project. I don’t assume that if you find something online that it can be used in any way. I make sure that the creator has given permission for the use I have. I am willing to buy music and sound effects, if anyone reading this has any suggestions about good places to find resources.

I found the tile popping sound at Sound Effects Plus. The sounds are free to download. Their licensing and restrictions are available here.

I found my music at Incompetech. I discovered this site through Paul Hudson. He mentions the site, along with a few other good resources, in his SpriteKit book, which I reviewed here.

For the tile popping sound, I want to make sure the pop happens at the right time. I tried simply calling it at the same time the animation was happening, but because the sound was so short, the “pop” was happening while the sprite was still growing. I resolved this by adding it to my scale sequence:

let soundEffect = SKAction.playSoundFileNamed("pop.wav", waitForCompletion: false)
let scaleSequence = SKAction.sequence([scaleUpAction, soundEffect, scaleDownAction])

Finally, I added my background music to the game. Not a lot of people have written much about audio mixing in SpriteKit, so that might be a thing I do in the future. I want to be able to lower the volume of the background music so that it doesn’t drown out the sound effects, but I am not certain how to make that work. I tried a few things but none of them worked, so for now I am simply adding the sound when the application begins:

run(SKAction.playSoundFileNamed("Carpe Diem.mp3", waitForCompletion: false))

I feel comfortable with my control of SKAction for animations, but I am not happy with my control of it for sounds and sound effects. Want to research this further.

Conclusions

You can see the current state of the project here.

In total I spent under ten dollars on assets for this game. The sound effect and background music were free. The tile set was nine bucks. I am honestly kind of shocked at how much better this looks with just a minimal amount of work using assets I found on the internet.

For now this isn’t completely done. I still don’t handle what happens when a game ends in a way that I find satisfactory, but that is a chunk of functionality I intend to tackle in a future blog post. However, this did come along pretty well and I am happy with the results so far.

Tic Tac Toe: Touches and Responses

Last time I set up the tic tac toe board with a set of nine tiles that will represent the board. This week, my goal is to make each tile responsive to touch so that we can connect our game logic to it.

I need to do the following steps:

  • Set up a touch observer to look for touches
  • Determine if the touch is located on a sprite representation of a tile
  • If so, which tile?
  • Figure out how that tile correlates to my array of game logic tiles
  • Change the state on the correlating game logic tile
  • Update the UI
  • Check for a win or a draw
  • Either end or continue the game

I have found that, for me personally, breaking tasks down into smaller chunks makes it easier to stay focused and get something done. Instead of worrying or freaking out about how I am going to set up multiple game scenes, which I intend to do later, I am focused on this one small chunk of the program. I am further dissecting this chunk into even smaller chunks so I don’t get confused about how I am going to get all of these things to work together.

Connecting the Model Game Tiles to the View Game Tiles

The first problem I would like to solve before I go any further is to figure out how to connect my model tiles to my view tiles. The view tiles are the objects that the player sees and can respond and detect touches, but they only know about what they look like and what is touching them. They don’t know that a certain color or image or state means anything outside of their own context. That is the responsibility of my model tiles. Those tiles know what state they are in and they provide data to my functions that determine if they are part of a win or not. I need some way to allow the model tiles to talk to the view tiles so they can both do their jobs.

There are a few ways to do this. I could have created a bunch of methods and structures to check the row and column of my board, but I only have nine objects to track. SpriteKit has a method of checking sprites by name, so I chose instead to create a single array of sprites and to give each a unique name that includes their index so that they can be found and tracked based on an identifier.

I created an array of model tiles that represent the state of the board. I am using their indices and positions in my game logic. I created a similar array to hold all of my sprite tiles. When I generated the sprite tiles, I generated them in the same order as I did for the model tiles and named them accordingly. The first tile in the sprite array should be the uppermost left tile, which correlates to the index and first tile in the model array. So the indices of each should line up. Keep that in mind as we walk through the rest of the steps.

I realized after I wrote this section when I was trying to get the next section to work that I don’t actually populate either of my arrays at this point in my explanation. I add the sprites to the board layer but I forget to add them to the array I created to hold them.

I added the following code within the nested loop in the initializer:

boardLayer.addChild(sprite)
tiles.append(sprite)

I had also added a method to populate the model array that I commented out because I wasn’t using it yet. It is this one:


board = resetBoard()

This is a good example of ways things can go wrong when you’re working with computers. You may intend to do something but unless you explicitly set it, then it won’t happen and you become frustrated. This is actually kind of true with people too…

Detecting Touches

UIKit has built in gesture recognition. One such gesture is touching. I generally haven’t used this because I use other elements, like buttons, that implement it automatically. But it’s useful for SpriteKit games, especially ones of a platform nature where you don’t necessarily need to hit a specific object in order to get a character to move on the screen. In fact, that would kind of defeat the purpose of having a touch screen over a controller of some kind.

One thing you have to remember (that I forgot and had to correct) is that you have to enable user interaction explicitly when you initialize the scene. You do this by putting this line of code somewhere after the call to super:

isUserInteractionEnabled = true

There are several methods for touch, but the default one is touchesBegan. This method kind of lurks around observing the screen waiting for a touch to happen. Once one does, it implements whatever game logic needs to be executed to respond to the touch.

The method doesn’t just bring in a single touch. It brings in an array of UITouch objects. So keep that in mind when you are using touchesBegan that it could potentially have many touches and not just one.

Here is my initial boilerplate touchesBegan method:

override func touchesBegan(_ touches: Set, with event: UIEvent?) {
	if let touch = touches.first {
		let location = touch.location(in: self)
		let tappedNodes = nodes(at: location)
	}
}

At this point I started adding a bunch of loops and conditional logic to get my code to work. It didn’t. I got confused and frustrated. I went to The Boyfriend to ask for help debugging my code. No, I am not sexist Computer Engineer Barbie. The Boyfriend has 30 years of programming experience to my five.

We debugged the array issue I mentioned above and came up with this much nicer method for checking for touches in our tiles:

override func touchesBegan(_ touches: Set, with event: UIEvent?) {
	guard let touch = touches.first else {return}
	let location = touch.location(in: self)
	let rawNodes = nodes(at: location)
	let tappedNodes = rawNodes.filter{return $0.name != nil}
		
	guard let node = tappedNodes.first as? SKSpriteNode else {
            return
        }
	node.color = UIColor.black
	board[tiles.index(of: node)!] = .playerA
}

First we are ensuring that we actually have a touch. If there isn’t an eligible touch, we want to break out of the method. Next, we need to store the location of the touch. This location is used to get an array of all nodes that exist at that location. One thing I had forgotten when putting the board together was that the background and the game board are also SKSpriteNode objects. This meant that when I touched the screen three nodes were added to the raw nodes. Since we only care about the one we named, we need to remove those other sprites from the array. We do that by checking for a name. Since the board and the background are not named, they are removed from the array. Lastly, we’re checking to make sure that any node that is left in the array can be cast to SKSpriteNode. If not, we break out of the method early.

Now that we have ensured that the only node we can possibly have at this point are SKSpriteNodes, we can now set properties on the node such as the color. We can also use the node to find its index within the array of sprite kit tiles. This directly correlates to the index within the model of the tile state that needs to be updated. Voila!

Can touch a tile and switch it from red to black

Updating the UI

Right now this isn’t particularly interesting. I can tap on a tile once and it changes from red to black. I can’t switch back and forth between players. Yet.

I modified the bottom of the touchesBegan() method to the following:

if board[tiles.index(of: node)!] == .notSelected {		
	switch currentPlayer {
	case .playerA:
		node.color = UIColor.black
		board[tiles.index(of: node)!] = .playerA
	case .playerB:
		node.color = UIColor.red
		board[tiles.index(of: node)!] = .playerB
	case .notPlaying:
		node.color = UIColor.white
		board[tiles.index(of: node)!] = .notSelected
	}
}

I changed the default unselected color of the tiles to white to make the different players a little more dynamic. I want to make sure that if a tile has already been selected that it can’t be selected again. I check the index of the correlating tile in the model to see if it’s been selected or not. If the current player is Player A, the tile switches to black. If the current player is Player B, it switches to red. To ensure an exhaustive switch statement I simply leave the node color white, but this should never be called.

I noticed that the first time I tested this that the first tile I touched didn’t do anything. I realized that the first time I touched the board I was still in the .NotPlaying state. I had commented out the switchPlayer call at the end of the initializer, so I just had to delete the comment and we are nearly ready to go.

Can switch between players now

Checking for Win and Ending the Game

Finally, after the move has been completed, we need to check for whether or not someone won the game. I set this logic up before, so all we have to do is call our game over method:

if let isWin = checkForWin(currentPlayer: currentPlayer, board: board) {
	switch isWin {
	case .playerAWin:
		print("Player A wins")
	case .playerBWin:
		print("Player B wins")
	case .draw:
		print("No one wins. Everyone gets a participation trophy")
	}		
}

currentPlayer = switchPlayer(currentPlayer: currentPlayer)

I thought about trying to get an alert view going for these win cases, but then I would have to remove them later when we have better UI, so for the time being I am not concerned with how to tell the player that the game is over. I am simply printing the result to the console and making it so no one can play anymore because I am mean.

Have fun reading the tiny print on the console!! :p

If isWin falls through because the game is not yet over, we need to switch players, which is the last bit that we do at the end of this method.

Conclusions

My project so far can be seen here.

This was far easier to set up that I originally anticipated. All of the work I did over the last few weeks to separate the model from the view really paid off here. I could focus on simply checking what state the game is in and update the UI accordingly.

Most of the SpriteKit games I have done tutorials for are ones that are running continuously and thus have all of their logic in a game loop. Turn based applications are similar except you only update the game once someone has made a move. As someone who is primarily interested in turn based games, this is something for me to keep in mind.

I have broken this game out into three more chunks that need to be done before I feel like I have completed this game adequately:

  • Graphics, Labels, and Sounds: I have an art tile set that I want to apply to the game so it looks like a real game and not a minimalist painting. I want to update labels that say what the current state of the game is. Lastly, at a minimum, I want some kind of background music.
  • Implementing an AI: This game isn’t very interesting without someone or something to play against. I want to look into how to implement some kind of AI for this game, be it using GKStrategist or me just rolling my own AI.
  • Multiple Scenes and Variations: I want an actual main menu screen to explicitly start the game. I would like a game over screen to take you back to the main menu. I also have some ideas for variations on tic tac toe for the player to be able to choose because the base game is pretty boring. I want those options to be selectable in the main menu.

So just three more blog posts left on this project! Then it’s on to actually attempting something that people might actually want to play. Stay tuned!

Tic Tac Toe: Setting up the Visual Game Board

My goal in 2018 was to post to this blog at least once a week, but life happens. I got sick and I had some obligations for others that prevented me from reaching my goal. Sometimes you have a bad week, but you do your best and keep trying. So in the spirit of that, here is my post for the week.

Now that I have most of my game logic somewhat hammered out, I am going to put it on the back burner and focus on my user interface elements. I am choosing to think of each element as an independent piece rather than focusing on how everything is going to work together. I am hoping if I get each piece working properly, by the time I need to bring them together as an actual game I won’t have to worry too much about their implementation details.

One big question I have with SpriteKit over UIKit is placing elements on a screen. In UIKit we use auto layout to apply rules to the elements to tell them what size they are and how they exist within relation to one another. I know SpriteKit has an Interface Builder-esque scene builder, but I am a masochist and want to do my layout in code.

Game Board Layout

My intention for the game is to only be able to play it in portrait mode and not landscape. I want to think of this layout algebraically to try and get around the lack of auto layout. (Note: It may be possible that auto layout coexists with SpriteKit, but I am stubborn and want to pretend it doesn’t as a thought exercise and to prove a point. Don’t judge me.)

Since the phone is always going to be taller than it is wide, the width is my limiting factor here. I want my board to take up a square amount of space that spans from one side of the screen to the other. I also want the board to be centered in the screen with some space at the top or bottom which will include labels and such at a later state of the game.

Game Layers

So looking at my mock up, I have a board layer that is square and varies based on the width of the screen. Within that board layer, I have nine subviews representing the tic tac toe tiles. Those tiles exist relative to one another within the board layer. So I want to create two separate layers. I also want the tiles in my tile layer to exist relative to that layer and basically know nothing about the larger screen they exist within.

The board layer should be relatively straightforward. We know the length and width of the layer will be the same and that they are both constrained by the width of the screen.

First I need to create the sprite node that will represent the board:

// View Properties
let boardLayer = SKSpriteNode()

To create the view, we simply need to figure out the width of the screen. Next, we want to make the anchor point of the board layer the middle rather than one of the edges. Finally, we need to set the position of the anchor to the middle of the screen.

override init(size: CGSize) {
	super.init(size: size)
		
	anchorPoint = CGPoint(x: 0.5, y: 0.5)
		
	let background = SKSpriteNode(color: UIColor.white, size: size)
	addChild(background)

	let boardSize = size.width
	boardLayer.anchorPoint = CGPoint.zero
	boardLayer.position = CGPoint(x: -(boardSize / 2.0), y: -(boardSize / 2.0))
	boardLayer.size = CGSize(width: boardSize, height: boardSize)
	boardLayer.color = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.2)
	addChild(boardLayer)
}
	
required init?(coder aDecoder: NSCoder) {
	super.init(coder: aDecoder)
}

If you try to build and run this… nothing happens.

I got super confused and did a bunch of color debugging and changing the anchor points and sizes, but nothing was happening. After some digging and breakpointing, I realized that the game scene was never actually loading and that I needed to make some changes to the GameViewController class.

Game View Controller

I assumed that since this was an Apple template that it would just work, but either it doesn’t or I accidentally changed something to prevent it from doing so. I have been referring to a few SpriteKit tutorials on Ray Wenderlich’s site and in the ones I have seen, you need to declare a game scene property and explicitly let it, so that is what I did.

var scene: GameScene!

override func viewDidLoad() {
	super.viewDidLoad()
		
	let skView = view as! SKView
	skView.isMultipleTouchEnabled = false
		
	scene = GameScene(size: skView.bounds.size)
	scene.scaleMode = .aspectFill
		
	skView.presentScene(scene)
			
	skView.ignoresSiblingOrder = true
	skView.showsFPS = true
	skView.showsNodeCount = true
}

This now loads the game scene and you get a nice boring white background with a grey square. Later in this process I will be adding nice graphics, but this works for now. Next, we need to populate the game board with some tiles.

Game Tiles

Since I don’t know how wide my screen is going to be at any point in time, I don’t want to set hard values for the size of my tic tac toe tiles. I want to set their size proportionally as a percentage of a whole rather than hoping that 150 pixels will work well on all screens and adjusting the spaces between them.

The board is a grid of nine tiles. The tiles are all the same size and they form a square. The tiles are closer to one another than they are to the border of the game board. I need to determine how large they need to be and also how to place them within this space.

To determine their width/height, I am going to create an algebraic equation. I know that I need to fit three tiles into the board’s width, so I will be dividing the width by three. However, I also need to account for the spaces between each tile and padding at the edge. So, to determine how large the tiles are I need to do the following calculation:

tileWidth = (boardWidth - (outerPadding * 2) - (innerPadding * 2)) / 3

We now know how to calculate the size of each tile, but we need to actually create each one and make sure we give each one an identifier so that when the player taps the middle square the program knows how to identify it.

Tile Sprites

Earlier in this project I created an array of tile states. This was the game model. We are now working on the game view, so we need analogous view objects that correlate to the model objects.

We will need nine sprite objects that represent a tile in our array of tile state. Each sprite needs to be given a size (which we calculated above), a position, and an identifier. The position is determined relative to the other tiles and relative to the board view. The identifier is the way we will be able to track which sprite has been tapped while the game is being played.

SpriteKit Coordinate System

In order to place objects in space, it’s important to understand SpriteKit’s anchor points and coordinate system. We know how to calculate the size and shape of our tiles, but we also need to know how to explain to Xcode where we want each tile to be placed.

Back when I learned algebra, I became familiar with Cartesian coordinates:

Generic Cartesian coordinate space

It’s the idea that you have an X and a Y axis that intersect at a center point, or origin. Every point in the coordinate plane can be expressed in relation to that center point. Any point that is above the X axis is thought of as positive and anything below the X axis is thought of as negative. Anything to the right of the Y axis is also positive and anything to the left is negative.

In SpriteKit, the center point/origin of the screen is oriented at the bottom left corner:

SpriteKit’s default coordinate orientation

What that means is that every point you look at is effectively going be positive. In this scenario, if you wanted to place an element at the bottom of the screen, offset by twenty pixels, you would set the y-position to 20. If you wanted to place it at the top, offset by twenty pixels, you would set the y-position to screen.size.height - 20.

Alternately, you could place your anchor point at the top right corner of the screen:

Inverted SpriteKit anchor point. Everything on the screen is negative in relation to the anchor point.

This would work the opposite of the default. Instead of thinking of everything positively, you think of things negatively. If you wanted to place an element at the bottom of the screen, offset by twenty pixels, you would set the y-position to -screen.size.height + 20. If you wanted to place it at the top, offset by twenty pixels, you would set the y-position to -20.

For my purposes, I care about building my application from the center out. I have no idea how tall the screen is that my app will appear on, so I need to build everything from the middle out. This means my anchor point will need to split the difference between SpriteKit’s default anchor point and the inverted one:

Center anchor point. Half the coordinates will be positive in relation to the anchor and half will be negative.

This makes placement slightly tricky. Some of my coordinates exist in positive coordinate space and some exist in negative space. Anything to the left of the center of the screen is negative while anything to the right is positive.

Embedded Nodes and Relative Positioning

I have no idea how tall the iPhone displaying my game is going to be. For the last few years Apple has tried to get people to think of screens proportionally rather than absolutely. I can’t position my tiles in relation to the height of the iPhone because that is variable. I went to a bunch of trouble to create a square area at the center of the screen that adjusts its size based on the height of the iPhone. I know this will always be square and I am basing the size and position of my tiles relative to that object.

SpriteKit utilizes a node graph. What this means is that you can add a sprite node as a child of either the scene or of another sprite. The tic tac toe board will have nine sprites that I want to control as one object and place within their own relative context.

Anchor points greatly impact positioning of embedded objects.

Tile Code

First I need variables for the padding around my tiles. I use this in a few places and if I don’t like the way it looks I only want to change the value in one place.

let outerBorderPadding:CGFloat = 30.0
let betweenTilesPadding:CGFloat = 15.0

I am going to use these to calculate the size of my tiles:

let padding = (outerBorderPadding * 2) + (betweenTilesPadding * 2)
let tileSize = (boardSize - CGFloat(padding)) / 3

Next I need to place and name my tiles. I had a creepy convoluted rats nest of code because I was trying to avoid rows and columns, but then I talked through this with The Boyfriend and we came up with something less busted:

var tileCounter = 0

for yIndex in 0..<3 {
	for xIndex in 0..<3 {
		let sprite = SKSpriteNode()
		sprite.color = UIColor.red
		sprite.size = CGSize(width: tileSize, height: tileSize)
		sprite.name = "Tile\(tileCounter)"
				
		let xPosition = CGFloat(outerBorderPadding) + CGFloat(xIndex) * (tileSize + betweenTilesPadding)
		let yPosition = CGFloat(outerBorderPadding) + CGFloat(2 - yIndex) * (tileSize + betweenTilesPadding)

		sprite.anchorPoint = CGPoint.zero
		sprite.position = CGPoint(x: xPosition, y: yPosition)
		boardLayer.addChild(sprite)

		tileCounter += 1
	}
}

There is a tile counter outside of the nested loops so that I can ensure that my tile name correlates to the index it represents in the data model.

We have nested loops for the rows and columns. I was concerned I would need to create persistent objects to keep track of the row and column of each square. That is a valid design pattern that would utilize if I was doing something more complex, but with only nine objects I had hoped to just keep things simple.

For each tile, I need to label the tile so I know which one we are dealing with and I also need to place it properly in relation to the ones around it. Later I will add logic for the squares to have graphics associated with selection and deselection, but for now I simply want to establish that my layout works properly.

In the nested loops, I am creating a tile for a row and column. There are a few ways this could have been placed. In this implementation, I kept the default SpriteKit coordinate system for these sprites, which means the origin is in the lower left corner. We need to account for that in our positioning of the sprites. I am counting the indices of the tiles starting in the top left while the origin is in the bottom left. This doesn’t affect the x-position, but it does affect the y-position. This is why I’m subtracting two from the y-index. The sprite is added to the board and the tile counter is incremented.

Final output of the board.

Conclusion

The current state of the project is available here.

For me, the challenge of figuring out how to place elements programmatically is an incredibly important problem. My aim with a lot of my game development is to do work in board/card games. Since the screens I am working with are variable and the number of elements are variable, it is tremendously important to me to figure out how to emulate auto layout in SpriteKit.

The game logic is relatively simple compared to the amount of programming that goes into the UI. I feel like when people think about programming a game, the thing they focus on is the mechanics and not the layout. Graphics are the most intensive part of game development and there are a lot of problems to solve that you don’t think about because you just kind of expect things to work.

Tic Tac Toe: Unit Tests

One of the biggest investments in time/money you have as a game designer goes into doing graphics and user interfaces. It’s important to check out the game logic before putting a bunch of UI into place. The UI is more difficult to debug than the game logic is, so it’s a good thing to make sure your game logic is solid and functional before trying to plug it into the UI. That eliminates a possible class of bugs and allows you to debug faster. The game logic should be independently testable without any user input. This is an ideal place to create unit tests.

Unit tests can be set up to check every single possible state of a function. Apple has a built in testing framework called XCTest. This framework allows boolean comparisons, value comparisons, and whether a function properly throws an error. There is a guide to writing good unit tests in Xcode here.

The reason I made all of these stand alone functions was because it would make them easier to unit test. Instead of having to create a View object and call these methods on those objects, I can just call functions and pass them state to test each condition within them.

I don’t want to repeat all of the code I already wrote that I am testing, so if you want to refer back to what I am testing, check out my previous post here.

Testing Reset Board

The smallest and easiest function to test is the function that creates the array that holds all of the tile state. This function has no parameters and returns one object that will consistently be the same no matter what.

Here is the test function for resetBoard():

func testResetBoard() {
	let accurateBoard:[TileState] = [TileState.notSelected,
					 TileState.notSelected,
					 TileState.notSelected,
					 TileState.notSelected,
					 TileState.notSelected,
				         TileState.notSelected,
					 TileState.notSelected,
					 TileState.notSelected,
					 TileState.notSelected]
		
	let testBoard = resetBoard()
		
	XCTAssertEqual(accurateBoard, testBoard)
}

The first variable of this test is to create an instance of what I expect the board to look like. I didn’t use a for loop to create the board because that is the code I am testing within my function. If I use the same code to create my test cases, then the tests will never fail, but the code still may not work the way I expect.

The next variable is generating a board using the resetBoard() function.

Finally, I am running an XCTest function to verify that the two variables are the same. XCTest has functions to assert equality and inequality. I am checking that they have the same capacity and hold the same data types. I ran the test and the test passed, so I know that my resetBoard() function will give me an array of nine TileState.notSelected instances.

Testing Switch Player

The switchPlayer function is slightly more involved than the resetBoard() function. The GameState enum contains three possible cases that need to be tested:

  • The current player is Player A
  • The current player is Player B
  • The game hasn’t started yet and no one is the current player.

Each of these cases will need to be tested to ensure that this function works properly. First, I want to create instances of Player A and Player B to plug into my tests. I could just create them within the function call, but I like to have labels for things:

func testSwitchPlayer() {
	// Player Objects
	let playerA = GameState.playerA
	let playerB = GameState.playerB

The first case I will test is what happens when Player A is the current player:

// Player A Case
let firstPlayerTest = switchPlayer(currentPlayer: playerA)
XCTAssertEqual(firstPlayerTest, playerB)

According to our function logic, if Player A is the current player, then switching the player should return an instance of Player B. Likewise, starting with Player B should return an instance of Player A:

// Player B Case
let secondPlayerTest = switchPlayer(currentPlayer: playerB)
XCTAssertEqual(secondPlayerTest, playerA)

The final test case is what happens where there is no current player. I determined in my logic that Player A would always start, so if we are switching the current player at the beginning of the game, Player A should always be the returned value:

	// Not Currently Playing Case
	let thirdPlayerTest = switchPlayer(
            currentPlayer: GameState.notPlaying)
	XCTAssertEqual(thirdPlayerTest, playerA)
}

Again, this test passes and I have verified each possible test case.

Testing Make Move

One reason I am testing these in order of complexity is so that I can use some of these tested methods within my future tests. For the makeMove() function, I need a lot of instances of a game board. I don’t want to have to type out all of these boards like a monkey, so being able to just generate a board using my resetBoard() function is very useful. I feel comfortable using that for this because I have tested it and verified it works.

First I want to create some reusable objects for setting up my assertions:

// Objects
let playerA = GameState.playerA
let playerB = GameState.playerB
let notPlaying = GameState.notPlaying

let selectedA = TileState.playerA
let selectedB = TileState.playerB
		
let blankBoard = resetBoard()

I have to create a .notPlaying test case even though I do not believe there will be a point in my code where it actually gets used. It is for the sake of safety and completeness that I include it and I need to test it to verify that it’s benign if it does ever wind up getting used.

First I want to check what happens if Player A makes a move:


// Player A move
let firstNewBoard = makeMove(tile: 0, currentPlayer: playerA, board: blankBoard)
XCTAssertNotEqual(blankBoard, firstNewBoard)
XCTAssertEqual(firstNewBoard[0], TileState.playerA)
XCTAssertEqual(firstNewBoard[5], blankBoard[5])

Since makeMove() returns a new array of TileState, our blankBoard is never mutated and can be used as a reference point against any board that is generated by makeMove(). Since I have generated a new board called firstNewBoard that should be different from blankBoard, I want to verify that they are in fact different. I also want to verify that the item I mutated in the function call actually changed to what I expect. Finally, I want to verify that nothing else changed. I didn’t write a test case for every element, so I chose one to test. I feel this is good enough and I don’t really want to write a billion tests around this when I feel reasonably sure this works as it should with a random sample. Next, I want to do the same using Player B:

// Player B move
let secondNewBoard = makeMove(tile: 2, currentPlayer: playerB, board: blankBoard)
XCTAssertNotEqual(blankBoard, secondNewBoard)
XCTAssertEqual(secondNewBoard[2], TileState.playerB)
XCTAssertEqual(secondNewBoard[8], blankBoard[8])

This code looks substantially similar to the Player A code, but I did want to change the tiles with the moves and the random check just so I wouldn’t accidentally have an edge case that happened to pass my tests. I have done this before and it makes you feel rather silly.

So far I have tested both Player A and Player B with a blank board. I would like to now write a few tests that simulate an actual game to verify that this works out of isolation:

// Moves by both Player A and Player B
let firstMove = makeMove(tile: 4, currentPlayer: playerA, board: blankBoard)
let secondMove = makeMove(tile: 0, currentPlayer: playerB, board: firstMove)
let thirdMove = makeMove(tile: 2, currentPlayer: playerA, board: secondMove)
		
XCTAssertNotEqual(firstMove, blankBoard)
XCTAssertNotEqual(secondMove, firstMove)
XCTAssertNotEqual(thirdMove, secondMove)
		
XCTAssertEqual(thirdMove[4], selectedA)
XCTAssertEqual(thirdMove[0], selectedB)
XCTAssertEqual(thirdMove[2], selectedA)

I simulated a game through the first three moves. I used the previous move’s result as the parameter for the next move’s function. The tests I wrote were to verify that the board does in fact change after each method call. I am also running tests on the final board to verify that a move made in the first board continues to be passed down through each successive move.

Finally I am adding my sanity check that if I am passing in a not playing state that the board does not change:

	// Not Playing move
	let notSelectedBoard = makeMove(tile: 5, currentPlayer: notPlaying, board: blankBoard)
	XCTAssertEqual(notSelectedBoard, blankBoard)
}

I am not testing this case as rigorously because I don’t anticipate it ever happening. Also, if it does, it shouldn’t really change much of anything.

This test also passed without issue. (Note: I am writing this blog post and my tests in parallel. Had something failed, I would have left the previous code as is and would include the test failure and explain why. These tests have been passing because the functions were designed very simply, making it easier to write good test cases, which was kind of the point going in.)

Test Win Game

I’m finally testing my monster function that includes all the various ways a turn can end. I am sure everyone is thrilled about reading two thousand words on doing repetitive unit testing logic, so I broke my tests up on this method to allow me to explain them without repeating myself a billion times.

First I will check for the diagonal win conditions, mainly because there are only two of them and that gives you the general idea about how the rest of the tests would be set up for vertical and horizontal wins. Again, I start with my game objects:

// Objects
let playerA = GameState.playerA
let playerB = GameState.playerB
		
let playerAWin = GameEndState.playerAWin
let playerBWin = GameEndState.playerBWin
		
let blankBoard = resetBoard()

The checkForWin() function returns an optional game end state. It’s optional because it’s possible that there are still moves to be make and no one has won yet. Since I am checking for wins here, I made objects for win conditions. When I test for other conditions I will create different objects.

Here are my tests for an upper left diagonal win:

// Check upper left win
let upperLeft1 = makeMove(tile: 0, currentPlayer: playerA, board: blankBoard)
let upperLeft2 = makeMove(tile: 4, currentPlayer: playerA, board: upperLeft1)
let upperLeft3 = makeMove(tile: 8, currentPlayer: playerA, board: upperLeft2)
		
let checkForUpperLeftWin = checkForWin(currentPlayer: playerA, board: upperLeft3)
let notAWinYetUpperLeft = checkForWin(currentPlayer: playerA, board: upperLeft2)
let wrongPlayerUpperLeft = checkForWin(currentPlayer: playerB, board: upperLeft3)
		
XCTAssertEqual(checkForUpperLeftWin, playerAWin)
XCTAssertNil(notAWinYetUpperLeft)
XCTAssertNil(wrongPlayerUpperLeft)

I generated a not quite accurate game board by simply giving Player A a win without letting Player B have any turns because I am a bad person. I look at this data and see a few places we can test to verify that this code works. We test the actual win condition to ensure that a win is returned. I also tested the move before the win to verify that the GameEndState returns nil. Finally, we are checking whether the current player has won or not, not if a win exists on the board. Even though there should be a win on the board, it is not for Player B, so this should come back nil as well.

I did the same code pretty much for the upper right win case and ran the test. It passed, so now I am moving on to testing for a draw.

A draw uses some different cases from our state objects than a win, so I need to create some new object instances:

// Objects
let playerA = GameState.playerA
let draw = GameEndState.draw
		
let selectedA = TileState.playerA
let selectedB = TileState.playerB
let notSelected = TileState.notSelected

Since I don’t really care about checking for wins, I just created one player instance for game state, but more importantly I included the draw state that I didn’t include in the last batch of tests. I created instances of tile state so that I could easily construct boards to test for various conditions:

// Boards
let drawBoard:[TileState] = [selectedA, selectedA, selectedB,
			     selectedB, selectedA, selectedB,
			     selectedA, selectedB, selectedB]
let blankBoard = resetBoard()
let nonBlankBoard:[TileState] = [notSelected, notSelected, notSelected,
				 notSelected, selectedA, notSelected,
				 notSelected, notSelected, notSelected]

(Trying to purposely construct a non-winning draw board is kind of a pain in the butt!)

I want to test three different scenarios:

  • There is a draw
  • The game has just begun
  • There has only been one move

One cardinal rule of unit testing is to test for failure conditions. In this context, anything where the game should continue is a failure condition. I am not checking with a win here because I am already checking for that in my other unit tests.

Here are my final tests to verify my draw condition works:

// Test variables and assertions
let drawCondition = checkForWin(currentPlayer: playerA, board: drawBoard)
let startGame = checkForWin(currentPlayer: playerA, board: blankBoard)
let firstMove = checkForWin(currentPlayer: playerA, board: nonBlankBoard)
		
XCTAssertEqual(drawCondition, draw)
XCTAssertNil(startGame)
XCTAssertNil(firstMove)

The draw tests also passed. I originally was going to test to ensure that the game continues if there isn’t a draw or a win, but that functionality is already being covered in both of those unit tests, so I don’t feel I need to create an entire function around it.

Ensuring Code Coverage

I’ve written what seems like an excessive number of unit tests, but how do I know I actually covered every failure case?

Starting in Xcode 7, Apple introduced a code coverage feature that is able to generate a report telling you how much of your code base is being touched by unit tests.

To enable this feature, you need to edit your target scheme:

Next, choose the option in Debug to enable code coverage data to be gathered:

Be sure to show the test bundle and check the code covered by the test bundle rather than the one that shows up by default. In this screenshot you see that the top bundle shows 0% test coverage of the State Utils class when that is basically the only thing being tested so far in the application.

Conclusions

The sample code so far for this project can be found here.

I know this wasn’t the most exciting post in the world, but there are a few take aways from this. One reason these tests were so easy to write was because I designed the code to be easy to test. How many of us have gone to work on a legacy code project that is impossible to update because there are vast multitudes of side effects and something completely unrelated to your feature breaks because of unnecessary dependencies?

I had some concerns about my very large function checking for win conditions and how I feel comfortable that my conditional logic will work as I intend. When I begin plugging this into the UI, if something isn’t working properly I will know that it’s an issue with the UI code and not with my game logic.

Speaking of UI, my next few posts will revolve around setting up the user interface and the interactive components of my game. I will be treating these separate from my game logic and as self contained units of functionality. Stay tuned!

Tic Tac Toe: Rules and Conditional Logic

The next bit that I am doing in my Tic Tac Toe game is putting in place the game rules and conditional logic around the actual game mechanics. I am saving the UI code for another time because that will be far more intensive than laying out the rules.

One of my goals with my code is to make it completely testable. One way of doing that is to create stand alone functions that can be tested outside of the game scene. Rather than creating a large nest of mutating side effects, I would like to take in parameters and return parameters, thus keeping all of my changes safely within a testable function. This will likely go through some refactoring in the future as I think about the best way of doing things. I know when I tried this before I had a lot of people yelling at me about the way I chose to write my code. It’s my code and my codebase, so I will write it however I please.

Board Properties

Tic Tac Toe is a fairly simple game. It is a board of nine squares. It has two players. On each player’s turn, they choose a square that has not been chosen yet. If there are three squares in a row, that player wins. If there are not, then the next player goes. This continues until either someone wins or all of the squares have been chosen.

This means that we need to have some kind of structure to represent the board state and we need to keep track of the current player:

var board:[TileState] = []
var currentPlayer:GameState = .notPlaying

I’m opting to use an array of tiles to represent the tile state. If this were a more complex board I would opt to do a different data structure and lay out actual rows and columns. However, since there are only nine tiles to keep track of and I am planning to number them when I set up the UI, I am opting for the more simple route and hard coding my logic. I know this is making people pull their hair out and scream, but I want to try and keep things simple. The rules haven’t changed in however many years this has existed, so the likelihood I will need to update requirements is minimal. I may change my mind, but that is for refactoring, not prototyping.

I also need to have a mutating variable to track the state of who is the current player. Since the game begins without a current player, the state is initialized as .notPlaying.

Setting Up the Board

The board is represented by a set of nine tiles. These tiles will be selected by the two different players, but not initially. When the board is initially laid out, it will be completely unselected. This can be represented by a function:

func resetBoard() -> [TileState] {
	var board = [TileState]()
	
	for _ in 0...8 {
		let tile = TileState.notSelected
		board.append(tile)
	}
	
	return board
}

This function returns an array of nine tiles that are all in the unselected state. These can be accessed by their index, which is how I will make moves and how I will check for wins.

Switch Players

When I was going to school for programming, my teacher told us that if you are describing what a function does and you have to use the word “and,” then you need another function. Each function should do one and only one thing. These functions can call other functions that take care of that functionality for you, but a function should be an encapsulated piece of code that does one thing.

In that spirit, I am adding a function to switch the current player:

func switchPlayer(currentPlayer: GameState) -> GameState {
	switch currentPlayer {
	case .playerA:
		return GameState.playerB
	case .playerB:
		return GameState.playerA
	case .notPlaying:
		return GameState.playerA
	}
}

I am making the determination in this game that Player A always goes first. You can do Rock Paper Scissors Lizard Spock to determine which player is Player A, but within my game logic Player A always goes first. In my exhaustive switch statement, I am checking to see what the current game state is. If the current player is Player A, then it is now Player B’s turn. If the current player is Player B, then it’s Player A’s turn. If this is the beginning of the game and the current state is Not Playing, then by default it will be Player A’s turn because Player A always goes first.

Making a Move

Beyond the UI, what goes into making a move? Making a move means selecting one tile within the array of tiles and changing its state to reflect which player you are. To keep this functional, you need to take in a board state, a current player state, and the index of the tile whose state needs to change. You then return a brand new board state based on the changes taken in by the function:

func makeMove(tile: Int, currentPlayer: GameState, board: [TileState]) -> [TileState] {
	var newBoard = board
	
	switch currentPlayer {
	case .playerA:
		newBoard[tile] = .playerA
	case .playerB:
		newBoard[tile] = .playerB
	case .notPlaying:
		newBoard[tile] = .notSelected
	}
	
	return newBoard
}

I do not believe that the final .notPlaying state will ever be called, but to ensure an exhaustive switch statement and to make sure the application doesn’t crash, it seemed logical to just include that as a case. It would not impact the game in any way to just keep the tile unselected in case something goes off the rails.

Checking for a Win

The final stand alone function I want to create is the function that checks for a win. As anyone over the age of six can tell you, most Tic Tac Toe games end in a draw. So when checking for a win, you must also consider the possibility there are simply no more moves to be made and the game ends unceremoniously.

To this end, we need another state enum to check for ending the game:

enum GameEndState {
	case playerAWin
	case playerBWin
	case draw
}

The game can end in one of three states:

  • Player A wins
  • Player B wins
  • Stalemate

To check for a win, you need to know the current player and the current state of the board. Since it’s possible that the game hasn’t entered one of these states yet, you need to make the return type optional:

func checkForWin(currentPlayer: GameState, board: [TileState]) -> GameEndState? {
	var requiredState:TileState
	var gameEnd:GameEndState
	
	if currentPlayer == .playerA {
		requiredState = .playerA
		gameEnd = .playerAWin
	} else if currentPlayer == .playerB {
		requiredState = .playerB
		gameEnd = .playerBWin
	} else {
		return nil
	}

In order to satisfy the compiler’s type safety, you need to create a conversion between the current player being passed in and the states you are checking. You can’t just compare the board’s TileState directly against the current player because they are of different types even though their members have the same name.

There are eight possible win states in a game of Tic Tac Toe:

  • Three across the top
  • Three across the middle
  • Three across the bottom
  • Three down the left
  • Three down the middle
  • Three down the right
  • Three diagonally from the upper left
  • Three diagonally from the upper right

I am checking each of these based on the index of the position in the array of tiles. If you count the tiles starting from the upper left and continuing to the right and down, they have a predictable index. The upper left tile is represented as index 0 and the lower right tile is represented by index 8. We can use the indices to check for the various win conditions. Let’s start with the across win checks.

	// Three across the top
	if board[0] == requiredState && board[1] == requiredState && board[2] == requiredState {
		return gameEnd
	}
	
	// Three across the middle
	if board[3] == requiredState && board[4] == requiredState && board[5] == requiredState {
		return gameEnd
	}
	
	// Three across the bottom
	if board[6] == requiredState && board[7] == requiredState && board[8] == requiredState {
		return gameEnd
	}

These checks are checking for contiguous tiles that all match the current player state. If any of these conditions are satisfied, the current player wins the game and the rest of the logic is never executed as it’s unnecessary. Next, we do something similar with the vertical win conditions.

	// Three down the left
	if board[0] == requiredState && board[3] == requiredState && board[6] == requiredState {
		return gameEnd
	}
	
	// Three down the middle
	if board[1] == requiredState && board[4] == requiredState && board[7] == requiredState {
		return gameEnd
	}
	
	// Three down the right
	if board[2] == requiredState && board[5] == requiredState && board[8] == requiredState {
		return gameEnd
	}

These are slightly more complicated as the indices are not contiguous. I made a diagram of the correlating tiles to ensure that my hard coded values were correct. Yes, I know hard coded values are evil. Bite me.

Finally, we check for the two diagonal win conditions.

	// Three diagonally from the upper left
	if board[0] == requiredState && board[4] == requiredState && board[8] == requiredState {
		return gameEnd
	}
	
	// Three diagonally from the upper right
	if board[2] == requiredState && board[4] == requiredState && board[6] == requiredState {
		return gameEnd
	}

If none of these win conditions have been satisfied, that means that the board is in one of two states. Either we are still playing and the game should continue, or there are no more moves to be made an no one can win. The easiest way to check for a continuing game is to check if there are any unselected tiles still on the board:

	// Draw
	for tile in board {
		if tile == .notSelected {
			return nil
		}
	}
	
	gameEnd = .draw
	return gameEnd
}

If there is even one tile that isn’t selected, the game continues. By making the game end state optional, we are allowing a scenario where you return nil if the game is not in fact at its end state.

If you have exhausted all of these options, where there isn’t a win state and there are no more tiles to be selected, then the game has concluded in a draw. You set the game end state to a draw and return that at the very end if there has not been any scenario where the method can end differently.

By far the largest and most involved function I have in this application is the checking for win function. I am not super happy with how long this turned out, but for the time being I can’t really think of a better way of doing this. There is a lot of conditional logic in here that is specific to the game, so there is going to be a lot of code, I just don’t know if there should be this much. I will ponder in the future if there are ways of cutting down on this.

Conclusion

You can access the current version of the code here.

This isn’t necessarily the way someone else would set up their application, but there isn’t a right or a wrong way to do anything. There are matters of personal preference. There is probably a way to make this code less complex, which I hope to figure out later. One thing that frustrates me about talking to other people about code is this idea that code is written in stone and can never be changed. This isn’t the case at all.

It’s better to just get something done and to refactor it later. Otherwise, you stare at a screen and nothing gets done. When I worked for Brad Larson on our rewrite of his robotics software, he regularly refactored his code. He would notice he was doing something over and over again and that it could be pulled out as a stand alone function. The process of writing the code base informed his refactoring decisions.

You can’t just go into a code base knowing all the challenges you will encounter. You learn that by working with it. My code will probably look significantly different than this when it’s done because I will figure out better ways of doing things and I will refactor the code base. This is how you should be approaching your code.

So this is all well and good to have these disparate stand alone functions, but how can we tell if they actually work or not? In my next blog post, I will set up unit tests to prove that these work as designed.

Tic Tac Toe: Game State Setup

One concept that I find interesting about game development is the concept of state. When I first got interested in OpenGL, someone told me that OpenGL is simply a “state machine.” I didn’t really grasp what he meant by that, so it’s something I wanted to make sure I had a good handle on by working through these projects.

It’s a concept that I am sure is fairly straightforward but I have never heard it explained super well. Basically, in games there are aspects of the game that change. A simple example of state is if you’re playing a multiplayer game. It can only be one player’s turn at a time. That means that if you are programming a game where people take turns, you need a way to keep track of whose turn it is at any given point in time. That is your state. Additionally, it’s possible that it’s no one’s turn if the game is over. You don’t want someone to take a turn when the game has concluded. So your game has a few different states it can be at at any given point in time.

Since tic tac toe is a two-player game, I need a way to track which player is the current active player. I also need a way to indicate is the game is in a state where no one is the active player. Back in the dark ages (pre-Swift), there wasn’t a good way of tracking state, so Apple introduced GKState in their GameplayKit framework. This allowed you to build state machines to track state in your game. However, with the advent of more powerful Swift enums, it’s far simpler to just create an enum to contain and switch your state.

Creating state machines in Swift is easy and powerful. Enums are an “or” type, which means that a Swift enum can only have one and only one value at a time. Which means if you need to keep track of state, they work very well. Let’s look at the example of keeping track of the current state of the game. We’ve already determined that the game can have three different states:

  • No Game Active
  • Player A’s Turn
  • Player B’s Turn

These conditions can be directly mapped to an enum:

enum GameState {
    case notPlaying
    case playerA
    case playerB
}

This enum is named GameState, making it easy to remember what it is tracking. You can initialize this game state as a variable in your main program:


var currentPlayer:GameState = .NotPlaying

You initialize currentPlayer as not playing because this is the default state you will find yourself in until you initialize a game. Once a game begins, the currentPlayer state will change to either currentPlayer.PlayerA or currentPlayer.PlayerB. There will never be a situation where this state is blank or where there will be more than one valid case. It is exclusive.

The only other state I have to track in this simple game involves the state that any individual tile is in. The tiles can have three possible states:

  • Not Selected
  • Player A’s Tile
  • Player B’s Tile

This again maps very easily to an enum:

enum TileState {
    case notSelected
    case playerA
    case playerB
}

This takes care of all of the state I need in the game. It isn’t much, but it’s a solid start to the foundation of our game. Most of the non-UI code I need will revolve around checking and transitioning this state. You can check the code so far here. Next, I need to set up the conditional logic and rules of the game.

Game Dev Journal: Project 1 Planning Session

Begin at the beginning and go on till you come to the end; then stop.
– Lewis Carroll

My friend Brian Hogan wrote a book a few years ago called Exercises for Programmers. The gist of the book is that it gives you a list of simple exercises that get progressively harder that can be done in about an hour. One doesn’t get better at anything without targeted practice and one way to practice is to work on solving a simple problem, but doing that consistently every single day.

Many of his challenges can be done simply in a few minutes, but you can add complexity to them, especially with iOS. If you write unit tests around your solution and create a UI for them that requires AutoLayout, you add significant complexity to the challenge. This also treats the challenge as you would a project you would create in the real world. Instead of just getting practice with simple conditional logic in something like Swift, you are getting practice working with Apple’s tools and aspects of the UI that you will only create once in a real project that may take a year to ship.

I am taking this approach with my first foray into games. The simplest game I can think of to create is tic-tac-toe. I worked through a C Plus Plus book that implements tic-tac-toe as one of the first projects and does so in about a hundred lines of code. If you’re just trying to get something done as fast as possible, then this isn’t really the best learning project.

However, I want to treat this like it’s an actual game I would ship. I want decent graphics. I want an interactive UI. I want different game scenes. There is a lot to learn to make this look like a real game. All of these nice peripheral things are features I want to include in actual apps I want to ship. By learning them on this throw away project, I should be able to develop this faster for future projects.

When learning something new, it’s important to simplify things as much as you possibly can. Constraining the number of new things you have to learn is vital if you want to make any progress. If you can narrow 50 things down to five and build on from there, by the time you get to the last ten things, you should have a solid foundation built upon repetition.

In that spirit, I have broken down creating this as a serious project into a series of achievable tasks that I intend to blog about over the next few weeks:

  • The Game Logic: The backbone of this project is setting up all the conditional game logic. This includes checking for a win and switching players. I want to write this functionally so that every aspect of the game logic can be tested.
  • The UI: In tic-tac-toe, you have nine squares that can be selected. I need to find a way to set these squares up and have them respond to user interaction. I also have to determine how to disable interaction once a square has been chosen. I also want to create animations to make the game feel more natural and responsive.
  • Game Scenes: One aspect of modern games is the idea that you have more than one game scene. You start on a main menu. You can look at a leader board. There is another scene once the game is over and you win or lose. I would like to set this up as a “real” game with all of these transitions.
  • Game AI: I want you to be able to play by yourself, so it’s necessary to program an AI to play against. This takes advantage of Apple’s Gamplay AI framework.
  • Game Actions: One thing that confounded me about SpriteKit was trying to figure out if you could use Core Animation with it. I recently discovered that much of Core Animation has been adopted as the SKActions framework. I want to animate the sprites that represent the squares in some way and this promises to make that an interesting challenge.
  • Graphics and Sounds: Even though this isn’t going to be a particularly complex game, I would like to use nice graphics and sound effects for it. Since there are a limited number of elements, this gives a good intro to thinking about what one needs to make a game look polished and not like developer art. For better or for worse, graphics are the thing people notice and it’s important to find ways to differentiate yourself from everyone else. I also studied audio engineering and understanding how to put together a unique soundtrack quickly can be a valuable skill.

I’m going to use SpriteKit for this project. My goal by the end of the year is to ship a game to the App Store in SpriteKit, so this is practice for the game I want to ship. I don’t know how many prototype games I should do in SpriteKit, but I do want to work through at least one. I know SpriteKit isn’t cross platform, but I don’t really want to deal with figuring out how to ship to another platform. I want to get more comfortable with shipping through iTunes Connect and with Swift, so it makes more sense for me personally to do this is SpriteKit instead of something like Unity. I want to get to Unity/Unreal later this year (or earlier if I am bored and want to procrastinate).

My goal over the next few weeks is to make steady progress on this project. I plan to post at least one blog post a week for this entire year. I am doing this as a nights and weekend project, so it could take a while to finish. The goal isn’t speed so much as consistency. I know that it’s important to have deadlines or else you don’t ship anything, but deadlines cause me severe amounts of anxiety. I have goals about how long I want this to take, but life happens and I don’t want to assume this can be done over a weekend if it will actually take me a month because I don’t know what I am doing.

Again, the point of this project isn’t to build a tic-tac-toe game. The purpose is to use a simple problem as a foundation for figuring out design patterns in the most straightforward way possible. This may feel like overkill for something that isn’t particularly interesting, but I think practicing on this will make the next game easier. I also believe there are probably things in this process that I don’t know I need to do until I do it, so I want to uncover all the bodies before I start on a project I care about.