mirror of
https://github.com/richardharrington/robotwar.git
synced 2024-06-08 17:29:33 +00:00
9b46900f9b
them constants because technically, the robot ranges are the maximum ranges for the centers of the robots' bodies, so the actual extent of the arena when displayed must be slightly larger
85 lines
3.9 KiB
Clojure
85 lines
3.9 KiB
Clojure
(ns robotwar.terminal
|
|
(:use [clojure.string :only [join]]
|
|
[clojure.pprint :only [pprint]]
|
|
[robotwar.constants])
|
|
(:require [clj-time.core :as time]
|
|
[clj-time.periodic :as periodic]))
|
|
|
|
(defn worlds-for-terminal
|
|
"takes worlds and a fast-forward factor, and
|
|
adds two fields to each world: idx and timestamp.
|
|
useful for our hacky display-in-terminal situation."
|
|
[worlds fast-forward]
|
|
(let [tick-duration (/ *GAME-SECONDS-PER-TICK* fast-forward)]
|
|
(map-indexed (fn [idx world]
|
|
(into world
|
|
{:idx idx
|
|
:timestamp (int (* idx tick-duration 1000))}))
|
|
worlds)))
|
|
|
|
(defn near-point [[pos-x pos-y] [x y]]
|
|
(and (= (int pos-x) x)
|
|
(= (int pos-y) y)))
|
|
|
|
(defn arena-text-grid
|
|
"outputs the arena, with borders"
|
|
[{robots :robots} print-robot-range-x print-robot-range-y]
|
|
(let [horiz-border-char "-"
|
|
vert-border-char "+"
|
|
header-footer (apply str (repeat (+ (* print-robot-range-x 3) 2) horiz-border-char))
|
|
scale-x #(* % (/ print-robot-range-x ROBOT-RANGE-X))
|
|
scale-y #(* % (/ print-robot-range-y ROBOT-RANGE-Y))
|
|
field (for [y (range print-robot-range-y), x (range print-robot-range-x)]
|
|
(or (some (fn [{:keys [idx pos-x pos-y]}]
|
|
(when (near-point [(scale-x pos-x) (scale-y pos-y)] [x y])
|
|
(str "(" idx ")")))
|
|
robots)
|
|
" "))]
|
|
(str header-footer
|
|
"\n"
|
|
(join "\n" (map #(join (apply str %) (repeat 2 vert-border-char))
|
|
(partition print-robot-range-x field)))
|
|
"\n"
|
|
header-footer)))
|
|
|
|
(defn display-robots-info [{idx :idx :as world} time-since-start fps]
|
|
(doseq [robot-idx (range (count (:robots world)))]
|
|
(println (apply format
|
|
"%d: x %.1f, y %.1f, v-x %.1f, v-y %.1f, desired-v-x %.1f, desired-v-y %.1f"
|
|
(map #(get-in world [:robots robot-idx %])
|
|
[:idx :pos-x :pos-y :v-x :v-y :desired-v-x :desired-v-y]))))
|
|
(println (format "Animation frame rate: %.1f frames per second", fps))
|
|
(println "Round number:" idx)
|
|
(println (format "Seconds elapsed in the game world: %d", (int (* idx *GAME-SECONDS-PER-TICK*))))
|
|
(println (format "Seconds elapsed in the real world: %d", (time/in-secs time-since-start)))
|
|
(println))
|
|
|
|
(defn animate
|
|
"animates a sequence of worlds in the terminal"
|
|
[initial-worlds print-robot-range-x print-robot-range-y fps]
|
|
(let [frame-period (time/millis (* (/ 1 fps) 1000))
|
|
starting-instant (time/now)]
|
|
(loop [[world :as worlds] initial-worlds
|
|
frame-start starting-instant]
|
|
(println (arena-text-grid world print-robot-range-x print-robot-range-y))
|
|
(display-robots-info world (time/interval starting-instant frame-start) fps)
|
|
(let [desired-next-frame-calc-start (time/plus frame-start frame-period)
|
|
this-instant (time/now)
|
|
next-frame-calc-start (if (time/after? this-instant desired-next-frame-calc-start)
|
|
this-instant
|
|
(do
|
|
(-> (time/interval
|
|
this-instant
|
|
desired-next-frame-calc-start)
|
|
(time/in-msecs)
|
|
(Thread/sleep))
|
|
desired-next-frame-calc-start))
|
|
animation-timestamp (time/in-msecs (time/interval
|
|
starting-instant
|
|
next-frame-calc-start))]
|
|
(recur (drop-while #(< (:timestamp %) animation-timestamp)
|
|
worlds)
|
|
next-frame-calc-start)))))
|
|
|
|
|