- Swift is not just for Apple
- Game-playing AI is awesome
- Swift is not just for Apple!
In this article, I’m going to explain how I set up Swift in a Jupyter Notebook, and then how I used Swift to create a Time-Bounded Monte Carlo Tree Search algorithm agent that can play 2048 in 17 seconds. Neat!
The reason why 2048 is such an interesting game to test an AI agent against is because it is not deterministic: it’s random. Therefore – unlike some other traditional games used to test AI such as CartPole and noughts & crosses – it’s not a problem that a neural network is suited to. Instead, we can use a stochastic algorithm that asks "which move will do the least damage?" over and over again, and can run through scenarios at pace.
Pace, you say? Yeah – using Swift allows us to create an agent that can run 55 moves per second which is pretty damn fast.
The Game of 2048
Designed by Italian web developer Gabriele Cirulli, 2048 is played on a 4×4 board, where at initialization you have two random tiles with either ‘2’ or ‘4’ on them. By swiping left, right, up, or down, you cause matching value tiles that come in to contact to add up (e.g. ‘2’ and ‘2’ becomes ‘4’), with a view to winning by adding up all the tiles to get the number ‘2048’. Each turn, new tiles are generated with either ‘2’ or ‘4’ on them, meaning that the board can easily fill up and become blocked before you reach the target!
2048 is complex because the more you play, the more profound the impact of each move on the final board state becomes. We wouldn’t want it to be too easy for our AI.
Time-Bounded Monte Carlo Tree Search (T-B MCTS)
MCTS is a search technique used with AI that is probabilistic and heuristic, marrying together the classic use of tree searches with the machine learning (ML) principles of reinforcement learning. As an algorithm, it was introduced in 2006 and has been notably deployed in many game-playing instances, including chess, poker, and even campaigns.
It makes uses of exploration and periodic evaluation, working on the possibility that the current best action may not actually be the most optimal action (this is expanding the breadth of the tree and uses randomization). This is balanced out by an exploitation approach, that avoids inefficiency by sticking to a path with the greatest estimated value (this is expanding the depth of the tree).
Why Time-Bounded? When using a detailed algorithm, it’s important to define a maximum amount of time to make a decision. That’s traded-off against the fact that the more time (resources) you can allocate to the process, the better! In this example, I’m allocating 0.015 seconds to each decision.
For a fully detailed explanation of MCTS, I recommend checking out this useful article on GeeksforGeeks.
To begin, let’s get everything set up.
Please note: I’m going to talk through the setup I used on the course, which included Swift for TensorFlow (S4TF) but it is certainly not the only setup possible to create the MCTS agent.
Setup
- Unless you are using Linux or Mac, you’ll need a Windows Subsystem for Linux (WSL). I use Ubuntu 18.04 (get it for free on the Microsoft Store)
- Install Swift for TensorFlow
Optional Setup of Jupyter Kernel for Swift
This bit took me longer than I expected but it’s worth doing if you want to display your Swift work in a clean and shareable format (and like using Jupyter!)
The most valuable guide I found was at the Swift-Jupyter repo. There are several options for getting set up – including conda and Docker options – but I found that using a Swift for TensorFlow toolchain and virtualenv worked well (eventually).
Tip: I spent so long trying to work out the Swift toolchain with LLDB Python3 support. I ended up using curl in Ubuntu:
$ curl -O https://storage.googleapis.com/swift-tensorflow-artifacts/nightlies/latest/swift-tensorflow-DEVELOPMENT-ubuntu18.04.tar.gz
If you don’t intend to deviate from the standard, then once you’ve got the Swift toolchain in Ubuntu all you need to do is run this:
$ git clone https://github.com/google/swift-jupyter.git
cd swift-jupyter
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
python register.py --sys-prefix --swift-toolchain ~/swift-jupyter
And finally, the Jupyter notebook can be launched with a Swift kernel.

Playing the game
Using Swift (either in a Jupyter notebook as above, or in another environment of your preference), you need to import the Foundation framework which provides the base layer functionality:
import Foundation
We use a big bad block of code to manage the 2048 game and ensure it works at speed. This is a bitboard implementation:
Mega. Now we need the agent to play automatically, which means a function that takes a game state, returns the direction to move, and performs random moves after that. It should also return the score of the board.
Importantly, this is where we define the maximum amount of time that the algorithm can take to make a decision (with ‘let maxTime = 0.015’).
It also creates a dictionary that holds the directions taken and relevant scores, returning the direction with the highest average score:
Finally, we are at a point where we have a working 2048 bot, woohoo! Now we need to initialize a new game instance, run a while loop, and repeat until the game is over. Within this loop, it will determine the best move to play, play it, and print the new board state:

As you can see, it took just 17 seconds for the AI to make 961 moves, at a rate of nearly 56 moves per second. This doesn’t even include optimizations, so you could certainly run it faster and better!
You can find the full notebook and all the code on my Github here and check out my previous work for Towards Data Science below!
This is based on the awesome "Game-playing AI with Swift for TensorFlow (S4TF)" course offered by the IBM Developer Skills Network on CognitiveClass.ai and I highly recommend you check out their learning paths.