Teaching Swift to Play: Building AdversarialKit

Why I built a game AI library

I just released AdversarialKit, an open-source Swift package that lets any turn-based game find its best move. You describe your game, pick a strategy, and get a smart AI opponent back.

Game AI is one of my favourite corners of computer science. Long before today’s large language models, algorithms like minimax were beating humans at chess by doing something very simple: thinking ahead, assuming the opponent plays perfectly, and choosing the move that holds up best.

Those ideas are decades old, yet they’re still the backbone of every board game, puzzle app and strategy game you play. I wanted a clean, modern Swift home for them.

What AdversarialKit does

The package ships three building blocks:

  • Minimax with alpha-beta pruning. It searches the game tree and skips branches that can’t change the outcome. For bigger games, you set a depth limit and supply your own scoring heuristic.
  • Monte Carlo tree search. It plays thousands of random games and learns which moves win most often. It needs no heuristic at all, which makes it ideal for complex games where nobody knows how to score a position.
  • An Arena. It pits two AIs against each other over many rounds, alternating who starts. You can measure whether a change actually made your AI stronger instead of guessing.

Using it takes a few lines of Swift:

let strategist = MinimaxStrategist<MyGame>()
if let move = await strategist.bestMove(for: game) {
    game = game.applying(move)
}

Tic-Tac-Toe and Nim come included as ready-made examples. And yes, two perfect players always draw at Tic-Tac-Toe.

Why not just use GameplayKit?

Apple’s GameplayKit offers similar strategists, but they date from 2015 and the Objective-C era. Game models are NSObject classes you must copy correctly, calls are synchronous, and it only runs on Apple platforms.

AdversarialKit is built for Swift as it is written today:

  • Value types. Your game is a plain struct, and every move returns a new state. The search never mutates shared data, so it’s easy to test and reason about.
  • Swift 6 concurrency. Everything is Sendable, and the search runs async without ever blocking your UI.
  • Runs everywhere. Apple platforms and Linux, with zero dependencies.

Think of it as GameplayKit’s strategists, reimagined for modern Swift.

What I learned along the way

Classic algorithms age well. Minimax and alpha-beta pruning are textbook material from my theoretical computer science days at ETH Zürich. Implementing them again, 30 years into my programming career, was a reminder that good fundamentals never go out of date.

Measure, don’t guess. The Arena started as a testing tool and became my favourite feature. Whenever I tweaked a strategy, I let it play hundreds of games against the old version. Numbers settle arguments quickly.

Modern Swift pays off. Designing around value types and Sendable from day one made concurrency almost free. The compiler catches data races before they ever reach a running game.

AI helps build AI. I used AI coding assistants throughout the project. They sped up the boilerplate and tests, while the design decisions stayed firmly human.

Try it and tell me what you think

AdversarialKit is free and open source under the Apache 2.0 license. If you’re building a board game, a puzzle app, or you’re simply curious how game AI works, give it a spin.

I’d love your feedback: which games would you model with it, and which strategy should come next? Iterative deepening, transposition tables and parallel Monte Carlo search are all on my list.

GitHub: github.com/tkausch/AdversarialKit

Swift #iOSDevelopment #GameDev #ArtificialIntelligence #OpenSource

tomkausch

Leave a Reply

Your email address will not be published. Required fields are marked *