This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-16
Channels
- # adventofcode (93)
- # beginners (104)
- # boot (1)
- # cider (4)
- # cljsjs (2)
- # clojure (174)
- # clojure-austin (1)
- # clojure-greece (5)
- # clojure-spec (13)
- # clojure-uk (32)
- # clojurescript (15)
- # core-logic (13)
- # cursive (13)
- # data-science (8)
- # datomic (11)
- # duct (1)
- # fulcro (22)
- # instaparse (23)
- # jobs (1)
- # lein-figwheel (5)
- # off-topic (13)
- # onyx (13)
- # parinfer (1)
- # pedestal (19)
- # re-frame (33)
- # specter (26)
- # unrepl (22)
begin unreasonably long context dump for which i am greatly sorry: again, i have a 2d grid of values that are integers or nil
i need to start at a particular x,y point on the grid and get the values seen as you walk four steps in each of the cardinal directions from that x,y point
(let [x 6
y 8]
{:right-run (select [(srange (inc x) (+ x MAX-RUN-LENGTH)) ALL
y]
(@app-state :grid))
:down-run (select [x
(srange (inc y) (+ y MAX-RUN-LENGTH)) ALL]
(@app-state :grid))
})
i could do it by hand in each of those cases - eg, if you’re going left, then in that situation you do (srange (- x MAX-RUN-LENGTH) x)
my preexisting clojure code involved a function (find-runs [x y xdir ydir]), with invocations like (find-runs x y 0 -1)
, and so inside that function you’ve got a single bit of run-finding code that can handle any of the four cardinal directions
but it looks like if i want to specterize this code, the sanest thing is to write four specter select
calls by hand, rather than reimplement (find-runs x y xdir ydir)
in a way that uses a single select
call whose path is heavily parameterized? apologies if any of this is foolish / obvious / doesn’t make any sense
(reduce (fn [[run-length run-sum] num-steps-in-direction]
; Find the position of the cell we're currently examining.
(let [run-x (+ x (* xdir num-steps-in-direction))
run-y (+ y (* ydir num-steps-in-direction))]
(if (or (not (cell-is-on-grid grid run-x run-y))
(nil? (get-in grid [run-x run-y])))
; If the cell's value is nil or this position is off the grid, the run is over.
(reduced [run-length run-sum])
; Otherwise, record this cell's value and continue following the run.
[(inc run-length)
(+ run-sum (get-in grid [run-x run-y]))])))
[0 0]
(map inc (range)))
the end goal here is to come up with a “run”, which is essentially [(count run-values) (apply + run-values)]
and again, the main problem i’m having trouble with is figuring out whether it’s sanely possible to write a single select
call that’s parameterized on x
, y
, xdir
, and ydir
, or if that select
call would be too grody and i should instead write four single-purpose select
calls by hand
i’ll do the by-hand approach for now, this is a toy project and doesn’t matter, but thanks in advance for your time and advice! 🙂
here’s what i ended up with:
(defn grid-range [start end direction]
(srange (max start 0)
(min end (if (= direction :horizontal)
GRID-WIDTH
GRID-HEIGHT))))
(let [x 6
y 4]
{:right-run (select [(grid-range (inc x) (+ x MAX-RUN-LENGTH) :horizontal) ALL
y]
(@app-state :grid))
:down-run (select [x
(grid-range (inc y) (+ y MAX-RUN-LENGTH) :vertical) ALL]
(@app-state :grid))
:left-run (select [(grid-range (- x (dec MAX-RUN-LENGTH)) x :horizontal) ALL
y]
(@app-state :grid))
:up-run (select [x
(grid-range (- y (dec MAX-RUN-LENGTH)) y :vertical) ALL]
(@app-state :grid))})
the sanest way i can think to clean it up is to make a function like eg (get-run-selector-path x y :left)
, which is implemented with just a cond
with four branches, and the :left
branch looks like [(grid-range (- x (dec MAX-RUN-LENGTH)) x :horizontal) ALL y]
, etc; maybe there’s a saner thing to do here though?all this is further complicated by the fact that i’d like all the runs’ values to be in order based on their direction from the origin cell because a “run” just means “a series of values in a row, in each cardinal direction, starting at this cell but excluding this cell’s value, with maximum length 4, stopping as soon as you see your first nil”.
so i’ll need to end up reversing left-run
and up-run
and then call (take-until nil the-run)
on each of my four runs. and at the end of the day i’m like, maybe the end result in specter will be insane and i should just stick with my preexisting implementation, which is looking way better now that i’m comparing the two? and if so, that’s totally cool. again, sorry for the wall of text!
in case it’s helpful, the game board looks like this: http://jrheard.com/quinto/ the “make a move” button has the AI detect the highest-scoring possible move and make it. there isn’t yet a way for the user to make a move, and there isn’t yet a visible score or any explanation of how the game works. the game’s basically scrabble but for numbers, the goal is to make “runs” of numbers that sum up to a multiple of five, it’s called quinto, my friend found it at goodwill and it seems like basically zero people in the world have ever played it
@jrheard your use case is probably best handled with matrix-specific navigators
here's an excerpt from some code I have:
(defnav matrix-elem [row col]
(select* [this structure next-fn]
(next-fn (-> structure :rows (nth row) (nth col)))
)
(transform* [this structure next-fn]
(let [rows (:rows structure)
r (nth rows row)
new-elem (next-fn (nth r col))]
(->Matrix (assoc rows row (assoc r col new-elem)))
)))
you could also make a "submat" navigator that navigates you to a submatrix
then you could do something like (select [(submat 4 2 8 2) ALL ALL] mat)
to get the "down run"
for a less flexible approach you could have a submat-elems
function like so:
(defnav matrix-elem [row col]
(select* [this structure next-fn]
(next-fn (-> structure(nth row) (nth col)))
)
(transform* [this structure next-fn]
(let [rows (:rows structure)
r (nth rows row)
new-elem (next-fn (nth r col))]
(assoc rows row (assoc r col new-elem))
)))
(defn ^:direct-nav submat-elems [row col row2 col2]
(reduce
multi-path
(for [r (range row (inc row2))
c (range col (inc col2))]
(matrix-elem r c)
)))
(def data
[[1 2 3 4]
[5 6 7 8]
[9 :a :b :c]
[:d :e :f :g]])
(select (submat-elems 1 1 3 1) data)
;; => [6 :a :e]
technically you don't really need matrix-elem
and can just use nthpath