Clojure re-order function

480 views Asked by At

This I must admit is still where I am a clojure newbie. I often find that if I search in the Clojure Docs, I find the function I am looking for. ;)

But I am nervous about this one, but maybe I might get lucky.

I have a card game. Each player has a hand of anywhere from 1-9 cards in hand.

The cards get put into their hands 1 card at a time, from the top of their decks by drawing.

What the players are requesting is the ability to take there UNORGANIZED hand or UNSORTED hand and re-oganize their hand.

I offered a solution of "How about a command like /re-order 31487652 in the command window, that could issue the function (no worries about the command, it's just the sorting func).

The goal of this would be to take each card in their hand 12345678 and change the order to a new order that they provide, the 31487652.

The data is in this format:

(:hand player)

[{:name : "Troll", :_id : 39485723},
{:name : "Ranger", :_id : 87463293},
{:name : "Archer", :_id : 78462721},
{:name : "Orc", :_id : 12346732},
{:name : "Orc", :_id : 13445130},
{:name : "Spell", :_id : 23429900},
{:name : "Dagger", :_id : 44573321}]

My only issue is, I could THINK about this using traditional programming languages, I mean easy, you just copy the data over to another array, haha but I mean don't we love clojure?...

But I'd like to keep things in the pure clojure ideology, AND LEARN the how to do something like this. I mean if it's just "Use this function" that's great I guess, but I don't want to create an atom, unless mandatory, but I don't think that is the case.

If someone could help get me started just THINKING of a way to approach this problem using clojure that would be awesome!

Thanks for ANY help/advice/answer...

ADDENDUM #1

(defn vec-order [n]
  (into [] (if (pos? n)
             (conj (vec-order (quot n 10)) (mod n 10) )
             [])))

(defn new-index [index new-order] (.indexOf new-order (inc index)))

(defn re-order [state side value]
  (println (get-in @state [side :hand]))
  (update @state [side :hand]
          (fn [hand]
            (->> hand
                 (map-indexed (fn [index card] [(new-index index (vec-order value)) card]))
                 (sort-by first)
                 (mapv second))))
  (println (get-in @state [side :hand])))

So here is my current code, with extraction of data. There is a massive @state, with the side the player is on. I use:

(println (get-in @state [side :hand]))

To look at the data before and after the execution of the defn, but I am not get ANY change. The vector is, for simplicity, 21436587 into [2 1 4 3 6 5 8 7].

But I am missing something because I even run the /re-order 12345678 to make sure things aren't moved and I am just not seeing things. But nothing...

Thank You, definitely for getting me this far.

3

There are 3 answers

3
rezwits On BEST ANSWER

With your help:

(defn vec-order [n]
  (into [] (if (pos? n)
             (conj (vec-order (quot n 10)) (mod n 10) )
             [])))

(defn new-index [new-order index] (.indexOf new-order (inc index)))

(defn re-order [state side value]
  (swap! state update-in [side :hand]
             (fn [hand]
               (->> hand
                    (map-indexed (fn [index card] [(new-index (vec-order value) index) card]))
                    (sort-by first)
                    (mapv second)))))

WORKS!!! 100%

1
Piotrek Bzdyl On

If you have your required order of the elements as a vector you can sort-by by a function returning an index of a card in that vector:

(let [cards [1 2 3 4 5 6 7 8]
      my-order [3 1 4 8 7 6 5 2]]
  (sort-by #(.indexOf my-order %) cards))
;; => (3 1 4 8 7 6 5 2)
3
dpassen On

So, the first function of note will be update which will allow us to return a new player with a function applied to hand if we invoke it as such.

(update player :hand (fn [hand] ... ))

Once, we have this basic structure, the next function that will help us is map-indexed which will allow us to pair the current hand with a new sort-ordered index.

From there, we will be able to sort-by the index, and finally mapv to retrieve the cards.

So, the final structure will look something like:

(defn sort-hand [player new-order]
  (update
    player
    :hand
    (fn [hand]
     (->> hand 
          (map-indexed (fn [index card] [(new-index index new-order) card]))
          (sort-by first)
          (mapv second)))))

For this to work, it's expected that new-order is a vector like [3 1 4 8 7 6 5 2]

As for a solution to new-index,

we can use .indexOf like this (defn new-index [index new-order] (.indexOf new-order (inc index)))