Shuffle by merging lists in Clojure - clojure

How does one write a function to split a list and then merge it back together such that the resulting list represents the shuffle of a deck?
The list (1 2 3 4 5 6 7 8 9 10) should end up as (1 6 2 7 3 8 4 9 5 10)
Is there a way to use split-at or reduce or some other function to achieve this?
So far I'm here:
(defn shuffle [cards]
(split-at (/ (count cards) 2) cards)
)

(apply interleave (split-at 5 (range 1 11)))
(1 6 2 7 3 8 4 9 5 10)

Clojure has an excellent selection of sequence functions.
user> (range 1 11)
(1 2 3 4 5 6 7 8 9 10)
user> (apply mapcat list (split-at 5 (range 1 11)))
(1 6 2 7 3 8 4 9 5 10)
You can get an overview at the clojure cheatsheet, it's a little out of date but mostly still relevant, and gives a good overview of the Clojure basics.

Split like you already have, then zip the two halves together and flatten:
(defn shuffle [cards]
(->> cards
(split-at (/ (count cards) 2))
(apply map list)
(flatten)))
(shuffle '(1 2 3 4 5 6 7 8 9 10)) ;=> (1 6 2 7 3 8 4 9 5 10)
Of course if you want a “truly” random shuffle, use clojure.core/shuffle.

A general solution is
(defn riffle [s]
(let [v (vec s), c (quot (count v) 2)]
(interleave (subvec v 0 c) (subvec v c))))
In this case
(riffle '(1 2 3 4 5 6 7 8 9 10))
; (1 6 2 7 3 8 4 9 5 10)

Related

How to check if current value is greater than the next value in Clojure?

I am looking for a way to check if a current value in a collection is greater than the next value, and if so, add that pair of items to a collection eg:
[9 2 3 7 11 8 3 7 1] => [9 2 11 8 8 3 7 1] ; Checking each item against the next
I initially thought I could do something like:
(filter (fn [[x y]] (> x y)) [9 2 3 7 11 8 3 7 1])
But something like this seemed to work only with associative types. So then I tried something like this:
(defn get-next [col index] ; Returns next item from a collection
(get col (inc (.indexOf col index))))
(filter (fn [[x]] (> x (get-next [9 2 3 7 11 8 3 7 1] x))) [9 2 3 7 11 8 3 7 1])
But still I got the same error. Any help would be appreciated
Use partition function to make pair of current and next item in a collection.
user=> (partition 2 1 [9 2 3 7 11 8 3 7 1])
((9 2) (2 3) (3 7) (7 11) (11 8) (8 3) (3 7) (7 1))
Now you have pair of current and next item in the collection. you can compare the items in each pair and concat the result with mapcat.
user=> (->> [9 2 3 7 11 8 3 7 1]
#_=> (partition 2 1)
#_=> (mapcat (fn [[a b]] (if (> a b) [a b]))))
(9 2 11 8 8 3 7 1)
Another way is to use reduce:
(defn pairs [data]
((reduce (fn [res item]
(if (and (:prev res) (< item (:prev res)))
(assoc res
:prev item
:res (conj (:res res) (:prev res) item))
(assoc res :prev item)))
{:prev nil :res []} data) :res))
(pairs [9 2 3 7 11 8 3 7 1])
;; [9 2 11 8 8 3 7 1]

How to use frequencies in clojure to combine same frequency and display them once?

I making a poker hands game in clojure. I have to define a function such that such that it returns the ranks in the descending order. For example: order ["2H" "3S" "6C" "5D" "4D"] should return (6 5 4 3 2). But if there is a two-pair like this: ["5H" "AD" "5C" "7D" "AS"] then it should return (14 5 7), but mine returns [14 14 7 5 5], how can I correct this? It should work in the same way for other cases of poker hands as well like for a full house it should give the rank of the three of a kind and the rank of the two of a kind. So, for this I have written:
(defn order
[hand]
"Return a list of the ranks, sorted highest first"
(let [ranks (reverse (sort (map #(.indexOf "--23456789TJQKA" %)
(map #(str (first %)) hand))))]
(if (= ranks [14 5 4 3 2])
[5 4 3 2 1]
(into [] ranks))))
I have also written all the other poker hand functions like flush?, straight? etc.
Also, I have to define another function such that it takes two orders like '(8 5 9) '(8 7 3) and returns true if the first has the larger value of the first difference, else false. Could someone please give me an idea how to do this?
Updated to show sorting by count, then rank:
(defn ->r [hand]
(let [ranks (zipmap "23456789TJKQA" (range 2 15)) ; creates a map like {\2 2, .... \A 14}
count-then-rank
(fn [x y] (compare
[(second y) (first y)]
[(second x) (first x)]))]
(->> hand
(map (comp ranks first)) ; returns the rank for each card eg: '(5 14 5 7 14)
frequencies ; returns a map of rank vs count eg: {5 2, 14 2, 7 1}
(sort count-then-rank) ; becomes a sorted list of tuples eg: '([14 2] [5 2] [7 1])
(map first)))) ; extract the first value each time eg: '(14 5 7)
For a more complete solution, you can use the frequencies to determine if you have 4 of a kind, 3 of a kind, full house etc.
Updated with more info on straight and straight flush:
For a straight, one approach is:
Extract the ranks so you would have a list like '(14 3 2 4 5)
Sort this list to get '(2 3 4 5 14)
Get the first element: 2, and the last element 14
Construct a range from 2 (inclusive) to 15 (exclusive) to get '(2 3 4 5 6 7 8 9 10 11 12 13 14)
Compare against the sorted sequence. In this case the result is false.
Retry, but first replace 14 with 1.
replace => '(1 3 2 4 5)
sort => '(1 2 3 4 5)
(range 1 6) => '(1 2 3 4 5)
This time, the range and the sorted list match, so this is a straight.
(defn straight? [cards] ; eg: ["AH" "3H" "2H" "4H" "5H"]
(let [ranks (zipmap "23456789TJKQA" (range 2 15))
ranks-only (map (comp ranks first) cards) ; eg: '(14 3 2 4 5)
ace-high (sort ranks-only) ; eg: '(2 3 4 5 14)
ace-low (sort (replace {14 1} ranks-only)) ; eg: '(1 2 3 4 5)
consecutive #(= (range (first %) (inc (last %))) %)] ; eg: (= (range 1 6) '(1 2 3 4 5))
(or (consecutive ace-high)
(consecutive ace-low))))
For a flush, simply extract all the suits, and then ensure they are all equal:
(defn flush? [cards]
(apply = (map second cards))) ; this is when suit is the second character
Now, simply combine these two boolean conditions to determine if this is a straight flush
(defn straight-flush? [cards]
(and (straight? cards) (flush? cards)))
See if you can solve 4clojure best hand puzzle, to open up a large number of different ways to tackle this. When I solved this, I used similar, but not identical functions.
Spoiler a more complete solution (using suit first "D7" instead of rank first "7D") is below
https://github.com/toolkit/4clojure-solutions/blob/master/src/puzzle_solutions/best_hand.clj
I think frequencies will get you closer to what you're looking for.
user=> (frequencies [14 14 7 5 5])
{14 2, 7 1, 5 2}
You could use this for sorting:
user=> (sort-by (frequencies [14 14 7 5 5]) [14 14 7 5 5])
(7 14 14 5 5)
And then you could use distinct:
user=> (distinct [14 14 7 5 5])
(14 7 5)
Putting all of these together should get you exactly what you want. I'll leave that as an exercise for the reader. When I'm stuck wondering if there's an easy way to do something, I often turn to Clojure's cheatsheet.

Clojure reverse-flattening lists. E.g. '(1 2 3 4 5 6) to '( (1 2) (3 4) (5 6)

Is there a core function or some idiomatic way to do a "reverse flattening" of a collection?
E.g. I would like the following:
(by-two '(1 2 3 4 5 6)) ; evals to '( (1 2) (3 4) (5 6) )
Of course the form in the above case would need an even number of elements or the function should do something sensible if presented with an odd-numbered collection. A generalized by-n function would be better of course. It's not clear to me whether there's any merit in trying to generalize the concept in depth as well or what's the best form to do so:
(by [2 2] '(1 2 3 4 5 6 7 8)) ; evals to '( ( (1 2) (3 4) ) ( (5 6) (7 8) ) )
(by [3 2 1 1 1] '(1 2 3 4 5 6)) ; evals to '(((((1 2 3) (4 5 6)))))
You can use reduce and partition :
(reduce #(partition %2 %1) '(1 2 3 4 5 6 7 8) [2 2])
There's partition:
(partition 2 [1 2 3 4 5])
> ((1 2) (3 4))
If you want to include the small tail, there's partition-all:
(partition-all 2 [1 2 3 4 5])
> ((1 2) (3 4) (5))
There is no such standard function I aware of. But partition is helpful:
(defn by [sizes coll]
(if sizes
(by (next sizes) (partition (first sizes) coll))
coll))

Passing list of variables individually to clojure function

I have been playing around with clojure, and decided to make a higher order function that combines mapcat and list to emulate this behavior:
Clojure> (mapcat list '(1 2 3 4) '(5 6 7 8))
(1 5 2 6 3 7 4 8)
my first attempt was defining mapcatList as follows:
Clojure> (defn mapcatList[& more](mapcat list more))
#'sandbox99/mapcatList
Clojure> (mapcatList '(1 2 3 4) '(5 6 7 8))
((1 2 3 4) (5 6 7 8))
Obviously the function does not behave how I would like it, and I think this is because the two lists are being put into one list and passed as a single argument, not two.
I was able to remedy the situation with the following,
Clojure> (defn mapcatList[x y & more](mapcat list x y))
#'sandbox99/mapcatList
Clojure> (mapcatList '(1 2 3 4) '(5 6 7 8))
(1 5 2 6 3 7 4 8)
This solution works well with two lists, but I would like the function to work with a variable number of arguments.
My question: How can I pass a variable number of argument to a function, then destructure them so they are passed as individual arguments together to 'mapcat list'?
You are looking for apply. This calls a function with the arguments supplied in a sequence.
But are you aware that there's a function interleave that does exactly what your mapcatList tries to do?
You're right, the arguments are wrapped in a list as a result of the vararg declaration. You need to apply the arguments in order to unwrap the list of arguments:
(defn mapcatList[& more]
(apply mapcat list more))
user=> (mapcatList '(1 2 3 4) '(5 6 7 8))
(1 5 2 6 3 7 4 8)
user=> (mapcatList '(1 2 3 4) '(5 6 7 8) '(\a \b \c \d))
(1 5 \a 2 6 \b 3 7 \c 4 8 \d)

best way to repeatedly map values from a sequence into an arg list?

Sorry for the noob question, but is there an good way to destructure values from a sequence like this..
(somefunc [[a b c] [1 2 3 4 5 6 7 8 9]] (prn a b c))
..with the a b c being assigned values until the sequence exhausted and letting me call a function on the args? doseq requires a partition of the right size..
(doseq [[a b c] (partition 3 [1 2 3 4 5 6 7 8 9])] (prn a b c))
Output:
1 2 3
4 5 6
7 8 9
That does what I want, but it seems like there should be a way to do this in s straightforward way without having to specify the partition. I found a solution with loop/recur but it's a lot more code and clearly non-idiomatic. What's a good way to do this? Thanks!
(defn apply-to-three [f [a b c & xs]]
(f a b c)
(when xs
(recur f xs)))
user=> (apply-to-three prn [1 2 3 4 5 6 7 8 9])
1 2 3
4 5 6
7 8 9
nil
(defn some-func
[[a b c & rest :as all]]
(prn a b c)
(prn rest)
(prn all)
user> (some-func [1 2 3 4 5 6 7])
1 2 3
(4 5 6 7)
[1 2 3 4 5 6 7]
Read more about destructing: http://java.ociweb.com/mark/clojure/article.html#Destructuring
An alternate, lower-level solution using loop destructuring:
user=> (def coll [1 2 3 4 5 6 7 8 9 10])
#'user/coll
user=> (loop [[a b c & more] coll]
user=* (when a
user=* (prn a b c)
user=* (recur more)))
1 2 3
4 5 6
7 8 9
10 nil nil
nil
or use when c to exit after the last complete triplet.
Using map and partition:
(map prn (partition 3 [1 2 3 4 5 6 7 8 9]))
partition + map is all you need. Read about dorun, doseq and doall: http://onclojure.com/2009/03/04/dorun-doseq-doall/
user> (dorun (map #(apply prn %) (partition 3 [1 2 3 4 5 6 7 8 9])))
1 2 3
4 5 6
7 8 9
nil