Writing a Halite Bot in Clojure

Halite: May the Best Bot Win

Halite is a new AI programming competition that was recently released by Two Sigma and Cornell Tech. It was designed and implemented by two interns at Two Sigma and was run as the annual internal summer programming competition.

While the rules are relatively simple, it proved to be a surprisingly deep challenge. It’s played on a 2D grid and a typical game looks like this:

Each turn, all players simultaneously issue movement commands to each of their pieces:

  1. Move to an adjacent location and capture it if you are stronger than what’s currently there.
  2. Stay put and build strength based on the production value of your current location.

When two players’ pieces are adjacent to each other, they automatically fight. A much more detailed description is available on the Halite Game Rules page.

Bots are run as subprocesses that communicate with the game environment through STDIN and STDOUT, so it’s very simple to create bots in the language of your choice. While Python, Java, and C++ bot kits were all provided by the game developers, the community quickly produced kits for C#, Rust, Scala, Ruby, Go, PHP, Node.js, OCaml, C, and Clojure. All the starter packages are available on the Halite Downloads page.

Clojure Bot Basics

The flow of all bots are the same:

  1. Read the initial state (your player ID and the starting game map conditions)
  2. Write your bot’s name
  3. Read the current gam emap
  4. Write your moves
  5. GOTO #3

The Clojure kit represents the game map as a 2D vector of Site records:

1
2
3
4
5
6
(defrecord Site
    [^int x
     ^int y
     ^int production
     ^int strength
     ^int owner])

And movement instructions are simple keywords:

1
(def directions [:still :north :east :south :west])

A simple bot that finds all the sites you control and issues random moves would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(ns MyBot
  (:require [game] [io])
  (:gen-class))

(defn random-moves
  "Takes your bot's ID and a 2D vector of Sites and returns a map from site to direction"
  [my-id game-map]
  (let [my-sites (->> game-map
                      flatten
                      (filter #(= (:owner %) my-id)))]
    (zipmap my-sites (repeatedly #(rand-nth game/directions)))))

(defn -main []
  (let [{:keys [my-id productions width height game-map]} (io/get-init!)]
    (println "MyFirstClojureBot")
    (doseq [turn (range)]
      (let [game-map (io/create-game-map width height productions (io/read-ints!))]
        (io/send-moves! (random-moves my-id game-map))))))

Next Steps

There are currently almost 900 bots competing on the site, but there are only a handful written in Clojure! I’m sure the Clojure community could do some interesting things here, so head over to halite.io, sign-up using your Github account, and download the Clojure starter kit.

Comments