Clojure: Same vector values in a row checker - clojure

I'm creating an Clojure Tic Tac Toe game, where I have a board variable with 9 numbers (def moves [0 0 0 0 0 0 0 0 0]) . This variable gets filled with 1's en 2's (no "x" or "o" yet). At the end of the game the variable could look like [2 1 2 1 1 1 2 1 2] where 1 owns the second row. Now I need a function to check if there's three in a row. I wanted to start horizontally. Which means I need a function to check if there's after each 3 numbers 3 of the same number are in a row. any ideas how I can create a function like this?
So this is the function so far:
(def moves [0 0 0 0 0 0 0 0 0])
(defn wongame? [playedmoved]
(
(def counter 0)
;action here....
)
)
(won moves) ;gives true/false

Something like this should work:
(defn end-of-game? [moves]
(->> moves
(partition 3)
(remove #(= [0 0 0] %))
(map (partial apply =))
(some identity)))
With your example input of [2 1 2 1 1 1 2 1 2] (partition 3) gives:
((2 1 2) (1 1 1) (2 1 2))
We need to use = with each list. apply is required because = works with individual arguments rather than a list. partial is required because the parameters to apply = are pending. #(apply = %) could have equivalently been used.
(false true false)
some is all about '(at least) one or none'. Here if one of the list it is passed is truthy then that one will be returned.
If you really need the answer to return true/false then put a call to boolean as the last function to the thread last macro (->>). I've left that out because only rarely do you need to actually return true or false in Clojure - you can rely on nil punning.
This same function will work for vertically as well. Start off with a general transpose function that works for a matrix (e.g. [[0 0 0][0 0 0][0 0 0]]):
(defn transpose [matrix]
(apply mapv vector matrix))
Then fashion your input before, and its output after:
(defn transpose-flat [xs]
(->> xs
(partition 3)
transpose
(mapcat identity)))

(def moves [0 0 1 1 0 1 2 2 2]) #_"the game's state in the middle"
At first, if we encounter like this pattern, we need stop the game,
Otherwise we are possible to misjudge the winner of this game
if the moves reaches the state like [2 2 1 1 1 1 2 2 2]. Which is the winner? We need to call this function each turn of the game.
Second, the name "won?" isn't appropriate because this function doesn't tell the winner, but tells the end of game. "won?" function should be renamed like ["end-of-game?" "game-end?" "end?"].
Third, I try to implement "winner" that returns the winner of the game like this:
(defn winner [moves]
(let [pattern-of-end {[1 1 1]:1 [2 2 2]:2}]
(->> moves #_"I use threading macro to explain"
(partition 3) #_"group by each three symbols.
1 line of 9 elements -> 3 lines of 3 elements"
(keep pattern-of-end) #_"If there is some lines that matches
the pattern-of-end ,that will return [1] or [2]"
first #_"Because the previous expression returns lazy-seq
we can get the scalar by calling first"
)))
This tells us the winner of the game.

It would have been easier if you had proper numbers for x's and o's:
(defn the-winner-is [moves]
(let [x 5
o 7]
(->> moves
(partition 3)
(map (partial apply +))
(some #{15 21}))))
This way it even says who is the winner (15 or 21).
(the-winner-is [5 5 7 7 7 0 7 7 7]) ; 21
(the-winner-is [5 5 5 7 7 5 5 7 7]) ; 15
(the-winner-is [5 7 5 7 7 5 5 7 5]) ; nil

Related

How to replace occurrence of an element with multiple elements in a sequence?

So, I have a list of items. I want to replace each occurrence of an item based on a criteria with a set of elements in it's place.
Ideally, map can let you convert a list of N elements into another list of N elements but here the length will increase as we insert more elements at index where we had other individual elements replaced by a list of elements in place.
As proposed by #Lee you can do it using mapcat.
E.g:
(mapcat #(if (even? %) [% %] [%]) (range 10))
will result into:
=> (0 0 1 2 2 3 4 4 5 6 6 7 8 8 9)
Rather than map, you can use reduce, starting with an empty accumulator collection [].
(reduce #(conj %1 (dec %2) %2)
[]
[1 3 5 7])
So here, starting with a collection of odd numbers [1 3 5 7], we are adding extra even numbers into the sequence. Output is:
[0 1 2 3 4 5 6 7]

Can somebody explain this piece of clojure code?

I am learning clojure and I am having problem understanding this clojure code, so I have this partial function
(def add-five (partial + 5))
When I run,
(add-five 2)# I get 7
(add-five 2 5) # I get 12
For first, I give one argument; For second, I give two arguments.
(map add-five [1 2 3 4 5])
this gives me
(6 7 8 9 10)
Here, I am assuming, add-five is being applied element of the list. However when I run,
(reduce add-five [1 2 3 4 5])
, I have no idea what is happening?
(reduce add-five [0]) #gives me zero
(reduce add-five [0 0]) #gives me five
Can someone explain what happens what I run the reduce like above?
(reduce add-five [0]) #gives me zero
For this line this rule from the documentation is applied:
If coll has only 1 item, it is returned and f is not called
Otherwise it applies the add-five function to 0 and 0 arguments: (add-five 0 0)

how to remove a particular occurances from sequence clojure

If I have sequence
[1 1 1 1 3 2 4 1]
how can I remove a particular number from that sequence? For example
(remove [1 1 1 1 3 2 4 1] 1) -> [3 2 4]
You can use a set as a predicate to remove, because sets can be called as functions.
(remove #{1} [1 1 1 1 3 2 4 1])
=> (3 2 4)
wrap that in (vec ..) if you need the result to be a vector.
The bonus of that approach is that you could remove many arbitrary values by sticking them in the set. If it's just one, this of course works too:
(remove #(= 1 %) [1 1 1 1 3 2 4 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. Drop-every?

Does the Clojure library have a "drop-every" type function? Something that takes a lazy list and returns a list with every nth item dropped?
Can't quite work out how to make this.
cheers
Phil
(defn drop-every [n xs]
(lazy-seq
(if (seq xs)
(concat (take (dec n) xs)
(drop-every n (drop n xs))))))
Example:
(drop-every 2 [0 1 2 3 4 5])
;= (0 2 4)
(drop-every 3 [0 1 2 3 4 5 6 7 8])
;= (0 1 3 4 6 7)
As a side note, drop-nth would be a tempting name, as there is already a take-nth in clojure.core. However, take-nth always returns the first item and then every nth item after that, whereas the above version of drop-every drops every nth item beginning with the nth item of the original sequence. (A function dropping the first item and every nth item after the first would be straightforward to write in terms of the above.)
If the input list length is a multiple of n you can use the partition function:
(defn drop-every [n lst] (apply concat (map butlast (partition n lst))))
(take 5 (drop-every 3 (range)))
; (0 1 3 4 6)