Skip to content

Commit

Permalink
Migrate sample bots; add (bad) biggest-space bot; add more board fns
Browse files Browse the repository at this point in the history
  • Loading branch information
timmc committed May 30, 2012
1 parent e8db873 commit ab54bde
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 5 deletions.
21 changes: 20 additions & 1 deletion src/crosscram/game.clj
Expand Up @@ -92,13 +92,32 @@ perspective. Player ID will be used modulo 2."
domino
(vec (map (comp vec reverse) domino))))

(defn posint?
"Return logical true if given a positive integer."
[x]
(and (integer? x) (pos? x)))

;;;; Boards

(defn mk-board
"Given a dimensions vector of [rows, columns], generate an empty board."
"Given a dimensions vector of [rows, columns], generate an empty board.
Both dimensions must be positive integers."
[[rows columns]]
{:pre [(posint? rows) (posint? columns)]}
(vec (repeat rows (vec (repeat columns nil))))) ;; TODO dimension-agnostic

(defn board-size
"Get the board size as a vector of row, column."
[board]
[(count board) (count (first board))])

(defn on-board?
"Test if a row/column coordinate pair is a board coordinate."
[board [rv cv]]
(let [[rows cols] (board-size board)]
(and (<= 0 rv (dec rows))
(<= 0 cv (dec cols)))))

(defn rotate-board
"Rotate a board from player 0's perspective to the specified player's
perspective. Player ID will be used modulo 2."
Expand Down
48 changes: 48 additions & 0 deletions src/crosscram/samples/biggest_space.clj
@@ -0,0 +1,48 @@
(ns crosscram.samples.biggest-space
"Pick a space that has the widest open area around it. Doesn't do
particularly well against random.clj. Feel free to hack on this bot.
Some of these utility functions might be useful."
(:require [crosscram.game :as cc]))

(defn canonical
"Order a horizontal piece in left to right square order."
[piece]
(let [[[row c0] [_ c1]] piece
left (min c0 c1)]
[[row left] [row (inc left)]]))

(defn neighborhood
"Get board values for a neighborhood centered on a possible move with
`radius` squares in each direction. Returns a vector of row vectors
containing the values from the board."
[board radius move]
(let [[[row left] _] (canonical move)]
(vec (for [y (range (- row radius) (+ row radius 1))]
(vec (for [x (range (- left radius) (+ left radius 2))]
(cc/lookup-square board [y x])))))))

(defn count-nil
"Given a neighborhood, count the nils."
[hood]
(count (filter nil? (apply concat hood))))

(defn pick
"Pick a random move from the highest-score cohort."
[candidates]
(->> candidates
(sort-by :score >)
(partition-by :score)
(drop-while empty?)
first
rand-nth
:move))

(def radius 3)

(defn make-move
[game]
(let [board (:board game)
candidates (for [m (cc/available-moves board)]
{:move m
:score (count-nil (neighborhood board radius m))})]
(pick candidates)))
4 changes: 2 additions & 2 deletions src/crosscram/samples/eager_walk.clj
@@ -1,6 +1,6 @@
(ns crosscram.samples.eager-walk
"Pick the first available space."
(:require [crosscram.board :as board]))
(:require [crosscram.game :as cc]))

(defn make-move [game]
(first (board/available-moves game)))
(first (cc/available-moves (:board game))))
4 changes: 2 additions & 2 deletions src/crosscram/samples/random.clj
@@ -1,6 +1,6 @@
(ns crosscram.samples.random
"Pick a random available space."
(:require [crosscram.board :as board]))
(:require [crosscram.game :as cc]))

(defn make-move [game]
(rand-nth (board/available-moves game)))
(rand-nth (cc/available-moves (:board game))))
8 changes: 8 additions & 0 deletions test/crosscram/test/game.clj
Expand Up @@ -39,6 +39,14 @@
(is (= (place-domino empty [[0 0] [0 1]] 5)
[[5 5 nil] [nil nil nil]]))
;; api
(is (= (board-size empty) [2 3]))
(are [pos on?] (= (boolean (on-board? empty pos)) on?)
[0 0] true
[-1 0] false
[0 -2] false
[2 3] false
[1 2] true
[2 2] false)
(is (= (lookup-square empty [0 2]) nil))
(is (= (lookup-square move0 [0 2]) 0))
(is (= (lookup-square move0 [1 2]) 0))
Expand Down

0 comments on commit ab54bde

Please sign in to comment.