Clojure odds and ends

Here is a small test framework I made to runs the testcases inside Emacs on the Clojure Repl.

Just include clojure.pprint in the namespace and paste the rest of the code under your -main function.

For each new game you have to copy and paste the urls for the input and output files from the problem description. Then you can use (run-test <testcase>) to compare your output to the expected output.

This example is for the “Bender - Algorithmic Complexity” game.

(ns Solution (:gen-class) (:use [clojure.pprint :as pp :only (cl-format pprint)])

(def ^:dynamic *testcases*
  '(("O(1)"
     "https://code.codingame.com/servlet/fileservlet?id=549349084930"
     "https://code.codingame.com/servlet/fileservlet?id=549350237669")
    ("O(logn)"
     "https://code.codingame.com/servlet/fileservlet?id=549385510196"
     "https://code.codingame.com/servlet/fileservlet?id=549391419271")
    ("O(n)"
     "https://code.codingame.com/servlet/fileservlet?id=549425470904"
     "https://code.codingame.com/servlet/fileservlet?id=549439028979")
    ("O(nlogn)"
     "https://code.codingame.com/servlet/fileservlet?id=549466416338"
     "https://code.codingame.com/servlet/fileservlet?id=549476004749")
    ("O(n^2)"
     "https://code.codingame.com/servlet/fileservlet?id=549500048576"
     "https://code.codingame.com/servlet/fileservlet?id=549519236043")
    ("O(n^2logn)"
     "https://code.codingame.com/servlet/fileservlet?id=549544076693"
     "https://code.codingame.com/servlet/fileservlet?id=549559605882")
    ("O(n^3)"
     "https://code.codingame.com/servlet/fileservlet?id=549588533737"
     "https://code.codingame.com/servlet/fileservlet?id=549596182441")
    ("2^n"
     "https://code.codingame.com/servlet/fileservlet?id=549629594048"
     "https://code.codingame.com/servlet/fileservlet?id=549633349793")))

(defmacro with-testcase
  "Take a TESTCASE in the form '(NAME INPUT-URL OUTPUT-URL) and bind *IN* to a
  reader that reads from INPUT-URL."
  [testcase & body]
  `(let [[_ data-url# _] ~testcase]
     (with-open [rdr# (clojure.lang.LineNumberingPushbackReader.
                       (java.io.InputStreamReader.
                        (.openStream
                         (java.net.URL.
                          data-url#))))]
       (binding [*in* rdr#]
         (do ~@body)))))

;; (with-testcase (first *testcases*) (list (read) (read-line) (read-line)))
;; => (100 "" "10 200000")

(defn sample 
  "Take N elements from SEQ. If SEQ is longer insert a placeholder at the end."
  [n seq]
  (if (string? seq)
    (cl-format nil "~a~a" (subs seq 0 (min n (.length seq))) (if (> (.length seq) n) "[..]" ""))
    (if (coll? seq)
      (if (> (count seq) n)
        (concat (take n seq) (list (symbol (cl-format nil "..~a" (- (count seq) n)))))
        (take n seq))
      nil)))

;; (sample 3 (range 10))
;; => (0 1 2 ..7)
;; (sample 3 "asdfgh")
;; => "asd[..]"

(defn run-test
  "Take a TESTCASE in the form '(NAME INPUT-URL OUTPUT-URL) and run -MAIN while
  taking input from INPUT-URL and comparing the result to OUTPUT-URL. Print a
  summary to *OUT* and return either TRUE or FALSE."
  [testcase]
  (cl-format *out* "----- Testcase: ~a -----~%" (first testcase))
  (let [result-expected (slurp (nth testcase 2))]
    (with-testcase testcase
      (let [result-actual (time (with-out-str (-main)))
            passed (= result-actual result-expected)]
        (cl-format *out*
                   "  actual: ~s~%expected: ~s~%~a~%" 
                   (sample 80 result-actual)
                   (sample 80 result-expected)
                   (if  passed "ok" "error"))
        passed))))

;; (run-test (first *testcases*))
;; => ----- Testcase: O(1) -----
;; => "Elapsed time: 0.98633 msecs"
;; =>   actual: "O(1)\n"
;; => expected: "O(1)\n"
;; => ok
;; => true

(defn run-tests []
  (map run-test *testcases*))
1 Like

Maybe that wasn’t a good start for this topic. Here is something more basic. Reading input from standard-in (aka. *in*) with read and read-line.

If you havn’t installed the Clojure compiler yet I highly recommend to do that first. The codingame IDE is missing the REPL and that’s the perfect tool to try out the examples. They are self-contained and you can just copy/paste them into the REPL and evaluate them.

In the codingame puzzles standard-in is already filled with the input data for the puzzle. In our examples we have to put something there ourself. The with-in-str macro does that. To quote the documentation "with-in-str Evaluates body in a context in which *in* is bound to a fresh StringReader initialized with the string s.".

The most important thing to know is that read tries to read clojure datastructures, not just bytes or ASCII. It consumes characters from *in*, until it has finished reading a datastructure like a number, a string, an identifier, or something more complex.

Here is an example with numbers. If you paste the first two lines into the REPL and hit return, it will answer (1 2 3). That is a list with the numbers 1, 2 and 3. I added the ;; => just to show that it’s the result.

(with-in-str "1 2 3"
  (list (read) (read) (read)))
;; => (1 2 3)

Here are some funky numbers.

(with-in-str "4/4 46/4 3N 2r100 555555555.55555555 555555555.55555555M"
  (list (read) (read) (read) (read) (read) (read)))
;; => (1 23/2 3N 4 5.555555555555556E8 555555555.55555555M)

Whitespace is ignored. Like in Clojure source code the comma is also treated as “whitespace”.

(with-in-str "1 2 \t 3 \n 4,5 \r\n\t    6"
  (list (read) (read) (read) (read) (read) (read)))
;; => (1 2 3 4 5 6)

A common pitfall is the semicolon. In Clojure source code everything after a semicolon, on the same line, is a comment and gets ignored. It’s the same thing with read. Here are two examples to show what happens. The second example throws an error, because there is only one datastructure in *in*.

(with-in-str "1;2;3\n4;5;6"
  (list (read) (read)))
;; => (1 4)

(with-in-str "1;2;3"
  (list (read) (read)))
;; => java.lang.RuntimeException EOF while reading

Now we have to talk about strings. If there is a string, delimited with double quotation marks, in *in* it works as expected.

(with-in-str "\"just a string\""
  (read))
;; => "just a string"

But if there are only characters in *in*, without quotation marks, read will return an identifier, not a string.

(with-in-str "list"
  (read))
;; => list

Thats an identifier, if you evaluate it you get the same thing you get when you type “list” in the REPL, the list function.

(with-in-str "list"
  (eval (read)))
;; => #<clojure.lang.PersistentList$1@711a4169>

You can even use it, but we digress.

(with-in-str "list"
  ((eval (read)) 1 2 3))
;; => (1 2 3)

To convert an identifier to a string you can use the str function. But obviously that doesn’t allways suffice, because strings can contain a lot more characters than identifiers and whitespace.

(with-in-str "list"
  (str (read)))
;; => "list"

If the string is delimited with single quotation marks, you get something else entirely, because the single quote is a reader macro for the quote function.

(with-in-str "'not a string'"
  (list (read) (read) (read)))
;; => ((quote not) a string')

If the input is not in a format suitable for read you might have to use read-line and string manipulation. Unlike read, read-line doesn’t try to read datastructures. It just consumes characters from *in* until the next newline, or until *in* is empty, and returns these characters in a string.

(with-in-str "1 2 3\n4 5 6"
  (list (read-line) (read-line)))
;; => ("1 2 3" "4 5 6")

Mixing read and read-line can get confusing, because read might leave a newline/whitespace at the start of *in*.

(with-in-str "123\na line of text\n456\n"
  (list (read) (read-line) (read-line) (read) (read-line)))
;; => (123 "" "a line of text" 456 "")

I like to do it this way.

(with-in-str "123\na line of text\n456\n"
  (list (with-in-str (read-line)
          (read))
        (read-line)
        (with-in-str (read-line)
          (read))))
;; => (123 "a line of text" 456)

That’s enough for now, I think.

2 Likes

Here is a small macro I use when debugging. Suppose you have three variables and want to print them into the *err* stream, then you have to do something like this:

(let [a 234 b 567 c 5768]
  (cl-format *err* "a=~a b=~a c=~a~%" a b c))
;; => a=234 b=567 c=5768

With the macro you don’t have to write the format string, and can just call (dump a b c) to get the same output.

(let [a 234 b 567 c 5768]
  (dump a b c))
;; => a=234 b=567 c=5768

The important part here is not to save a small amount of typing, but to prevent any mistakes in the format string. Make a mistake there and you might end up trying to hunt down a bug that doesn’t exist. Those are hard to find!

(defmacro dump [& exprs]
  `(let [evaluated# (list ~@exprs)
         lst# (map #(if (or (= %1 \newline) (and (string? %1) (= (last %1) \newline)))
                      (str %1)
                      (if (= %1 %2)
                        (cl-format nil "~s " %1)
                        (cl-format nil "~a=~s " %1 %2)))
                   '~exprs
                   evaluated#)]
     (cl-format *err* "~{~a~}~%" lst#)))

The macro dump takes any number of expressions and for each expression prints expression=value into the *err* stream. If expression and value are the same (e.g. strings or keywords) the =value part is omitted. Each expression is only evaluated once.

(defmacro dump* [& exprs]
  `(dump ~@(interpose \newline exprs)))

The dump* macro does the same, but every expression=value pair is printed on a separate line.

(let [a 345 b 5678 c 23 d 6758]
  (dump :label1 c [b d] a (max a b c d)))
;; => :label1 c=23 [b d]=[5678 6758] a=345 (max a b c d)=6758
1 Like

Seeing as I keep coming back to codingame every now and then, I might as well keep posting in this thread. Here is a very basic way to get some graphical output. Usefull when you have a puzzle that requires geometry and you are sick of comparing floating point numbers in the repl.

It uses the proxy macro to create an instance of a class that inherits from JPanel and overrides the paintComponent function. The class is unnamed, hence “proxy”. Inside the paintComponent function you can use the graphics context to draw.

https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html

(import [javax.swing JFrame JPanel]
        [java.awt Color Font Dimension])

(let [panel (proxy [JPanel] []
              (paintComponent [graphics]
                (doto graphics
                  (.setColor Color/BLACK)
                  (.setFont (Font. Font/SANS_SERIF Font/BOLD 72))
                  (.drawString "hello world" 80 250)
                  (.drawLine 0 0 640 480)
                  )))]
  (.setMinimumSize panel (Dimension. 640 480))
  (doto (JFrame. "hello world")
    (.add panel)
    (.setSize 640 480)
    (.setVisible true)))

So, how does the other side look like?

I mean if you are programming a multiplayer bot and implement the functionality to make a gametree anyway, you have almost everything you need to run your bot locally. The only thing missing is a server that talks to your bots over the *in* and *out* streams. How does that look like?

Here is an example with the game Nim. The rules are as follows. There is a given number of marbles. On a players turn he can take one, two or three marbles. The goal is to take the last marble.

A bot for this game on Codingame would look like this.

(defn bot
  "Random bot for the game Nim. Read the current number from *in* and print
  your choice to *out*."
  [name]
  (let [n (read)
        x (min n (inc (rand-int 3)))]
    (cl-format *err* "n=~a ~a takes ~a~%" n name x)
    (prn x)
    (recur name)))

On the server side we want to use prn to print something onto the stream that the bot can then read with read. To achieve that we use the Java Classes PipedInputStream and PipedOutputStream. If we connect one to the other we have a pipe in one direction. With a second pipe in the opposite direction we have everything we need. We just need to make sure that everyone is reading/writing to the right end of the right pipe. Also we have to run the bot in another thread to prevent deadlocks.

(defmacro run-bot
  "Create two pipelines. One from the bot to the server and another one for
  the opposite direction. Run the bot (body) in a new thread to prevent
  deadlocks and let him read/write to the two pipelines. Return a map with
  the two pipeline ends for the server (keys are :in and :out)."
  [& body]
  `(let [bot-out# (java.io.PipedOutputStream.)
         server-in# (java.io.PipedInputStream. bot-out#)
         server-out# (java.io.PipedOutputStream.)
         bot-in# (java.io.PipedInputStream. server-out#)]
     (future
       (binding [*in* (clojure.lang.LineNumberingPushbackReader.
                       (java.io.InputStreamReader. bot-in#))
                 *out* (java.io.PrintWriter. bot-out#)]
         (do ~@body)))
     (hash-map :in (clojure.lang.LineNumberingPushbackReader.
                    (java.io.InputStreamReader. server-in#))
               :out (java.io.PrintWriter. server-out#))))

(defmacro with-pipes
  "Bind *in* and *out* respectively so ther server can talk to the bot on the
  other ends."
  [pipes & body]
  `(binding [*in* (:in ~pipes)
             *out* (:out ~pipes)]
     (do ~@body)))

And here is “the server” with a sample output. This could be extended to run local benchmarks for any number of bots.

;; the game Nim with a random start number
(loop [n (+ 15 (rand-int 15))
       bots (cycle (list (run-bot (bot "itchy"))
                         (run-bot (bot "scratchy"))))]
  (let [x (with-pipes (first bots)
            (prn n)
            (read))]
    (when (> (- n x) 0)
      (recur (- n x) (drop 1 bots)))))

;; n=24 itchy takes 1
;; n=23 scratchy takes 3
;; n=20 itchy takes 1
;; n=19 scratchy takes 2
;; n=17 itchy takes 2
;; n=15 scratchy takes 1
;; n=14 itchy takes 3
;; n=11 scratchy takes 2
;; n=9 itchy takes 2
;; n=7 scratchy takes 2
;; n=5 itchy takes 3
;; n=2 scratchy takes 2
;; => nil
1 Like