• Build Tic Tac Toe with AI Using Swift
  • The Nuggets translation Project
  • Translator: Nicolas Li
  • Proofreader: Cyseria, Jamweak

withSwiftCreate one with AITic Tac ToeThe game

I have a great passion for and obsession with learning. Recently, I proposed a hypothesis that takes the theory of making games and applies it to application development to improve user experience. A lot of people have come up with buzzwords like “gamification”, which addresses the pain points of applications by engaging users in ways that delight them through interaction and active engagement. No matter what your application actually offers. We’re not going to talk about that today (I’m not even going to bring up the question of whether or not advocates of play-feel behavior play games). Instead, we build a game using SpriteKit, GameplayKit, and Swift.

Limit your expectations

Before you get too ambitious to build a chart-topping app, let me tell you that’s not our goal today. We’re going to look at just the tip of the iceberg and create a simple tic-tac Toe game. After we get to work, I will add a computer-controlled AI opponent that will allow you to play.

Part ONE – Principles

Apple announced SpriteKit during WWDC2013, which gives developers an option to build game applications faster than by playing with their own frameworks. Since the gaming app category accounts for the majority of downloads in the Apple ecosystem, it’s not surprising that Apple is committed to the gaming community and has benefited greatly from making it easier for developers to create new iOS, macOS, watchOS, and tvOS games.

SpriteKit, also commonly referred to as Sprites, is a framework for handling rendering, graphic animation, and images. As an application developer, you decide what to change, and SpriteKit handles the job of showing those changes. You can read more about SpriteKit here. I also highly recommend reading the SpriteKit programming guide for more of the other features the framework offers, such as handling sound playback and Sprite physics models.

SpriteKit handles the running loop of your game and provides multiple places for developers to update game content every frame. The image below shows what happens to each frame from the start of the update to the final render. Essentially, you have plenty of opportunities to tweak your game in every frame.

GameplayKit is another framework that can be used. GameplayKit was introduced at WWDC last year and provides implementations of a number of common methods used to make games, such as creating random numbers, creating artificial intelligence opponents, or pathfinding systems for obstacles. They are very useful tools that do a lot of the heavy lifting and allow game application developers to focus on making more interesting games. I highly recommend reading the GameplayKit programming guide to learn how to use the techniques in these frameworks. Going back to our simple game, we will include only a small part of the framework to make our computer opponents “smarter.”

Start the Xcode

Launch XCode and create a game project from a template designed for iOS. Name the game TicTacToe and make sure the programming language is set to Swift. During the creation of the project, XCode creates the SKScene file, which shows the initial view of the game, along with a view controller file that initializes your game scene and handles the display on the screen when the application starts. If you launch the application now, you’ll see the Hello World tag, which makes all your stuff immediately available. Also, if you click on the view, a spaceship will be added at the location you clicked. We don’t need the tag or the spaceship anymore, so let’s remove that part of the code. Switch to the gamesCene. swift file and remove the code in the didMoveToView and touchesBegan methods.

Let’s take a moment and highlight some features of the scene editor. In the center of the view is the scene, and the yellow outline around our Tic Tac TOE game board shows our viewport, which makes our game visible. We can change the viewport or add cameras that allow us to see more of the game in real time. On Platformer we can also create a large background image with many enemy points hashed at the end of the scene. We can also use a camera node across the scene to show more new background sections over time. For this game, however, our view will remain stationary near the board.

At the bottom of the scene is the node editor. We can use this editor to add functionality to nodes or to select them more easily in the scene. We add a node to represent the game board, tags, and placeholder nodes for each game board. Finally, each node in the scene has a name that can be referenced back in the code.

I’ve already submitted the entire game project to Github for the sake of writing this post, so you can follow along and explore the area I’ve skipped.

Back to the code

Let’s switch back to GameViewController.swift to see how to set up our scene and get our game to do something. In the viewDidLoad method we configure and load our scene. We also added debug statements so we can track code and how many frames per second. In an action game, we are interested in monitoring how many nodes appear on screen simultaneously per second and whether we can maintain the ideal 60fps frame rate.

    override func viewDidLoad() {
      super.viewDidLoad()

      if let scene = GameScene(fileNamed:"GameScene") {
        // Configure the view.
        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true

        /* Sprite Kit applies additional optimizations to improve rendering performance */
        skView.ignoresSiblingOrder = true

        /* Set the scale mode to scale to fit the window */
        scene.scaleMode = .AspectFill

        skView.presentScene(scene)
      }
    }Copy the code

Looking at the gamesCene. swift file again, we need to examine the following three methods: didMoveToView, tochesBegan, and Update. The didMoveToView method is called when the scene is to be displayed in the view of our view controller. The cool thing about our GameScene view is that we have several options for accessing nodes in the view. In our approach, we initialize the scene by removing the color of the game board cell background. We did a few other things as well, but we’ll talk about those in a later article.

    override func didMoveToView(view: SKView) {
      /* Setup your scene here */
      self.enumerateChildNodesWithName("//grid*") { (node, stop) in
        if let node = node as? SKSpriteNode{
          node.color = UIColor.clearColor()
        }
      }

      let top_left: BoardCell  = BoardCell(value: .None, node: "//*top_left")
      let top_middle: BoardCell = BoardCell(value: .None, node: "//*top_middle")
      let top_right: BoardCell = BoardCell(value: .None, node: "//*top_right")
      let middle_left: BoardCell = BoardCell(value: .None, node: "//*middle_left")
      let center: BoardCell = BoardCell(value: .None, node: "//*center")
      let middle_right: BoardCell = BoardCell(value: .None, node: "//*middle_right")
      let bottom_left: BoardCell = BoardCell(value: .None, node: "//*bottom_left")
      let bottom_middle: BoardCell = BoardCell(value: .None, node: "//*bottom_middle")
      let bottom_right: BoardCell = BoardCell(value: .None, node: "//*bottom_right")

      let board = [top_left, top_middle, top_right, middle_left, center, middle_right, bottom_left, bottom_middle, bottom_right]

      gameBoard = Board(gameboard: board)
  }Copy the code

The next method we talked about is’ touchesBegan. ‘ This method handles touch events when the user chooses to move and reset the game. For each touch event on the scene, we determine their location on the scene and which node is selected. In our case, we either place the player’s pieces in a cell or reset the game. We also update the internal game board state.

‘Override func touchesBegan(Touches: Set, withEvent Event: UIEvent?) { for touch in touches { let location = touch.locationInNode(self) let selectedNode = self.nodeAtPoint(location) var node: SKSpriteNode

  if let name = selectedNode.name {
    if name == "Reset" || name == "reset_label"{
      self.stateMachine.enterState(StartGameState.self)
      return
    }
  }

  if gameBoard.isPlayerOne(){
    let cross = SKSpriteNode(imageNamed: "X_symbol")
    cross.size = CGSize(width: 75, height: 75
Copy the code