Skip to content

Commit 76bf15d

Browse files
committed
Adding Clojure starter kit
Signed-off-by: Tom Marble <[email protected]>
1 parent d8813e4 commit 76bf15d

File tree

18 files changed

+859
-0
lines changed

18 files changed

+859
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
(ns halite3.mybot
2+
(:require [hlt.log :refer [log]]
3+
[hlt.random :refer [set-seed! rrand-int]]
4+
[hlt.input :refer [as-int]]
5+
[hlt.state :refer [constants]]
6+
[hlt.ship :as ship]
7+
[hlt.shipyard :as shipyard]
8+
[hlt.direction :as direction]
9+
[hlt.game-map :as game-map]
10+
[hlt.game :as game])
11+
(:gen-class))
12+
13+
;; This Clojure API uses kebab-case instead of the snake_case as documented
14+
;; in the API docs. Otherwise the names of methods are analogous.
15+
16+
(defn -main
17+
"MyBot in Clojure"
18+
[& args]
19+
;; Initialize repeatable random number generator
20+
(set-seed! (if (seq args) (as-int (first args)) (System/nanoTime)))
21+
22+
(game/new-game) ;; get initial map data
23+
24+
;; At this point the "game" atom is populated with the game state
25+
;; This is a good place to do computationally expensive start-up pre-processing.
26+
;; After calling ready below, the 2 second per turn timer will start.
27+
28+
(game/ready "my-clojure-bot")
29+
30+
;; Now that your bot is initialized, save a message to yourself in
31+
;; the log file with some important information. Here, you log here
32+
;; your id, which you can always fetch from the game using my-id
33+
34+
(log "Successfully created bot! My Player ID is " (game/my-id) ".")
35+
36+
(while true
37+
;; This loop handles each turn of the game. The game state
38+
;; changes every turn, and you refresh that state by running update-frame
39+
(game/update-frame)
40+
41+
;; below is a naive example of handling a turn
42+
43+
;; For each of your ships, move randomly if the ship is on a low halite
44+
;; location or the ship is full. Else, collect halite.
45+
(let [me (game/me) ;; option 1: get player, destructure by hand
46+
{:keys [shipyard halite ships dropoffs]} me]
47+
(doseq [[_ ship] ships]
48+
(let [{:keys [::ship/id position halite]} ship
49+
cell (game-map/at ship)]
50+
(log "Ship:" id "has" halite "halite.")
51+
(if (or (< (:halite cell) (/ (constants :MAX_HALITE) 10))
52+
(ship/is-full? ship))
53+
(game/command
54+
(ship/move ship
55+
(get (direction/get-all-cardinals) (rrand-int 4))))
56+
(game/command (ship/stay-still ship))))))
57+
58+
;; If the game is in the first 200 turns and you have enough
59+
;; halite, spawn a ship. Don't spawn a ship if you currently have
60+
;; a ship at port, though - the ships will collide.
61+
(when (and (< (game/turn-number) 200)
62+
(>= (game/me :halite) ;; option 2 destructure inline
63+
(constants :SHIP_COST))
64+
(not (game-map/is-occupied? (game/me :shipyard))))
65+
(log "Spawning a ship!")
66+
(game/command (shipyard/spawn)))
67+
68+
;; above is a naive example of handling a turn
69+
70+
;; Send your moves back to the game environment, ending this turn.
71+
(game/end-turn)))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(ns hlt.commands)
2+
3+
(def north "n")
4+
(def south "s")
5+
(def east "e")
6+
(def west "w")
7+
(def stay-still "o")
8+
(def generate "g")
9+
(def construct "c")
10+
(def move "m")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
(ns hlt.direction
2+
(:require [hlt.commands :as commands]))
3+
4+
(def north [0 -1])
5+
(def south [0 1])
6+
(def east [1 0])
7+
(def west [-1 0])
8+
9+
(def still [0 0])
10+
11+
(def all-cardinals [north south east west])
12+
13+
(defn get-all-cardinals
14+
[]
15+
all-cardinals)
16+
17+
(defn convert
18+
"Converts from this direction tuple notation to the engine's string notation"
19+
[direction]
20+
(cond
21+
(= direction north) commands/north
22+
(= direction south) commands/south
23+
(= direction east) commands/east
24+
(= direction west) commands/west
25+
:else
26+
commands/stay-still))
27+
28+
(defn invert
29+
"Returns the opposite cardinal direction given a direction"
30+
[direction]
31+
(cond
32+
(= direction north) south
33+
(= direction south) north
34+
(= direction east) west
35+
(= direction west) east
36+
:else
37+
still))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
(ns hlt.dropoff
2+
(:require [hlt.input :refer [read-line-ints]]
3+
[hlt.position :refer [new-position]]))
4+
5+
(defn id
6+
"Get id for a dropoff"
7+
[dropoff]
8+
(::id dropoff))
9+
10+
(defn owner
11+
"Get owner for a dropoff"
12+
[dropoff]
13+
(:hlt.player/id dropoff))
14+
15+
(defn position
16+
"Get position of a dropoff"
17+
[dropoff]
18+
(:position dropoff))
19+
20+
(defn new-dropoff
21+
"New dropoff"
22+
[player-id dropoff-id position]
23+
{::id dropoff-id
24+
:hlt.player/id player-id
25+
:position position})
26+
27+
(defn read-dropoff
28+
"Read dropoff"
29+
[player-id]
30+
(let [[dropoff-id x y] (read-line-ints)
31+
position (new-position x y)
32+
dropoff (new-dropoff player-id dropoff-id position)]
33+
[dropoff-id dropoff]))
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
(ns hlt.game
2+
(:require [clojure.string :as string]
3+
[clojure.data.json :as json]
4+
[hlt.state :refer [game]]
5+
[hlt.log :refer [log]]
6+
[hlt.input :refer [read-one-line read-line-ints exit]]
7+
[hlt.position :refer [position?]]
8+
[hlt.player :as player :refer [read-players update-players]]
9+
[hlt.map-cell :as map-cell]
10+
[hlt.game-map :refer [read-game-map update-game-map at]]))
11+
12+
(defn my-id
13+
"Return my player id"
14+
[]
15+
(::player/id @game))
16+
17+
(defn me
18+
"Return my player information"
19+
[& ks]
20+
(let [{:keys [::player/id players]} @game
21+
player (get players id)]
22+
(if (seq ks)
23+
(get-in player ks)
24+
player)))
25+
26+
(defn game-map
27+
"Return the game map"
28+
[]
29+
(:game-map @game))
30+
31+
(defn has-structure?
32+
"Is cell empty?"
33+
[position-or-entity]
34+
(let [cell (at position-or-entity)]
35+
(map-cell/has-structure? cell)))
36+
37+
(defn structure-type
38+
"Get structure-type for a map cell"
39+
[position-or-entity]
40+
(let [cell (at position-or-entity)]
41+
(map-cell/structure-type cell)))
42+
43+
(defn turn-number
44+
"Return the turn-number"
45+
[]
46+
(:turn-number @game))
47+
48+
(defn new-game
49+
"Initialize the game"
50+
[]
51+
(let [constants (json/read-json (read-one-line) true)
52+
[num-players id] (read-line-ints)
53+
log-file (str "bot-" id ".log")
54+
players (read-players num-players)
55+
game-map (read-game-map)]
56+
(swap! game assoc
57+
::player/id id
58+
:num-players num-players
59+
:log-file log-file
60+
:constants constants
61+
:players players
62+
:game-map game-map
63+
:turn-number 0
64+
:commands [])))
65+
66+
(defn ready
67+
"Game initialization"
68+
[bot-name]
69+
(println bot-name))
70+
71+
(defn update-frame
72+
"Update game state"
73+
[]
74+
(let [{:keys [num-players players game-map]} @game
75+
[turn-number] (read-line-ints)
76+
players (update-players num-players players)
77+
game-map (update-game-map game-map)]
78+
(log "=============== TURN" turn-number "================")
79+
(swap! game assoc
80+
:players players
81+
:game-map game-map
82+
:turn-number turn-number
83+
:commands [])
84+
(doseq [[_ player] (seq players)]
85+
(let [{:keys [::player/id shipyard ships dropoffs]} player]
86+
(swap! game update-in
87+
[:game-map :map-cells (:position shipyard)]
88+
assoc :structure shipyard)
89+
(doseq [[_ ship] (seq ships)]
90+
(swap! game update-in
91+
[:game-map :map-cells (:position ship)]
92+
(partial map-cell/mark-unsafe ship)))
93+
(doseq [[_ dropoff] (seq dropoffs)]
94+
(swap! game update-in
95+
[:game-map :map-cells (:position dropoff)]
96+
assoc :structure dropoff))))
97+
nil))
98+
99+
(defn command
100+
"queue an engine command"
101+
[cmd]
102+
(swap! game update-in [:commands] conj cmd))
103+
104+
(defn end-turn
105+
"End turn"
106+
[]
107+
(let [commands (:commands @game)]
108+
(if (seq commands)
109+
(println (string/join " " commands))
110+
(exit 1 "no commands queued.. exiting"))))
111+
112+
(defn log-game
113+
"Debugging function to log the game data (without the game-map)"
114+
[]
115+
(log (with-out-str (clojure.pprint/pprint (dissoc @game :game-map)))))

0 commit comments

Comments
 (0)