Fork me on GitHub
#beginners
<
2022-08-04
>
shravan a09:08:57

I am learning clojure from couple of weeks and doing some exercises, one of them is developing 2048 like dynamic game where board can be 4*4 or 3*3 or 8*8 and win-num is 2048, 4096 etc, i need below help How to convert [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] to ([ 1 5 9 13 ] [ 2 6 10 14 ] [ 3 7 11 15 ] [ 4 8 12 16 ]) values can be anything Currently is doing this (list (vec (take-nth 4 input-vec)) (vec (take-nth 4 (drop 1 input-vec))) (vec (take-nth 4 (drop 2 input-vec))) (vec (take-nth 4 (drop 3 input-vec)))) I want something genetic where i can convert [64 values] to (8 * [8 Values]) etc

Martin Půda09:08:12

(->> [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  16]
     (partition 4)
     (apply map vector))

=> ([1 5 9 13] [2 6 10 14] [3 7 11 15] [4 8 12 16])

shravan a09:08:15

Thanks (defn get-columns [game-board] (let [width (Math/sqrt (count game-board))] (println width) (->> (vec game-board) (partition width) (apply map vector)) )) (get-columns [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]) => #function[clojure.core/map/fn--5880] what am doing wrong here, how to get values instead of map-fn

Martin Půda09:08:22

You can't partition by double, the width is 4.0. (The result will be () and (apply map vector ()) returns transducer.)

shravan a09:08:05

got it thanks a ton

shravan a10:08:48

I have created my first program in clojure -> 2048 game, Any feedback will be helpful https://github.com/shravankgl/clojure-practice/tree/main/clojure-2408 Any other example i can try to improve, kindly let me know

Martin Půda10:08:22

Your code:

(defn get-zero-indicies
  "get list of all indicies containing value 0"
  [game-board]
  (map first
       (filter #(= (second %) 0)
               (map-indexed vector game-board))))
Reviewed:
(defn get-zero-indicies
  "get list of all indicies containing value 0"
  [game-board]
  (->> (map-indexed vector game-board)
       (filter (fn [[_ v]] (zero? v)))
       (map first)))

tomd11:08:21

or even better:

(defn get-zero-indicies
  "get list of all indicies containing value 0"
  [game-board]
  (keep-indexed (fn [i x] (when (zero? x) i)) game-board))

🙌 3
Martin Půda11:08:39

Your code:

(defn get-columns [game-board]
   (let [width (int (Math/sqrt (count game-board)))] 
      (vec (apply map vector (partition width game-board))))
     )
Reviewed:
(defn get-columns [game-board]
  (let [width (int (Math/sqrt (count game-board)))]
    (->> (partition width game-board)
         (apply map vector)
         vec)))

Martin Půda11:08:08

draw-board- put (println row) on the next line

Martin Půda11:08:11

move-cells- put each let variable on the new line and use only one let

Martin Půda11:08:10

execute- use condp, play-2048- (* total-cells) is strange, I think you wanted (* width width)here

Martin Půda11:08:11

Is this really the expected result?

(->> (range 16)
     (move-left))
=> (1 2 3 0 4 5 6 7 8 9 10 11 12 13 14 15)

Chase17:08:46

@U03T0J7DF88 , I'm curious what you are using to keep your parens/brackets balanced? I only ask because the formatting of some of your functions/expressions might be considered a bit unidiomatic as we usually use the shape of things to help us see the flow. For example, when seeing a function like this:

(defn pick-random-empty-cell
  "get a random 0 value cell"
  [game-board]
  (if (= (apply min game-board) 0)
  (rand-nth (get-zero-indicies game-board))
    -1))
I thought you had an error because of the way it's formatted. I thought your if expression would just be discarded but it turns out your parens are balanced correctly. But normally I would expect to see the function look like:
(defn pick-random-empty-cell
  "get a random 0 value cell"
  [game-board]
  (if (= (apply min game-board) 0)
    (rand-nth (get-zero-indicies game-board))
    -1))
It's a subtle difference but if you aren't using a structural editing tool (like paredit, parinfer, etc) I fear that manually balancing your parens while also formatting like this might lead to some unexpected errors and behaviors. I hope I'm making sense here and not just confusing you. There are also autoformatting tools like cljfmt , which you can also use through clojure-lsp , but I don't want to overwhelm you as setting up all the (great) tools we have and getting them working together can be a bit daunting.

shravan a20:08:29

@U9J50BY4C thanks for the feedback, i was not using any formatting tools, just did the code in vscode without any tools. Now i have installed paredit and will update my code for more readablility. I did face unexpected errors and i am still not fully comfortable with repl as past decade i am used to debugging. Anything tools available for debugging along with repl.

shravan a20:08:38

@U01RL1YV4P7 Thanks for the feedback This point is not clear for me move-cells- put each let variable on the new line and use only one let can you explain further move-left is the expected behaviour to convert

(4 4 0 2)
(2 0 0 0)
(0 0 0 2)
(0 0 0 0)

to 

(8 2 0 0)
(2 0 0 0)
(2 0 0 0)
(0 0 2 0)

Chase21:08:01

VS Code now has a great Clojure option called https://calva.io/. I highly recommend getting that installed and play around with it. That'll really help you start seeing the amazing benefits of an editor connected Clojure repl. The folks in the #calva channel will help you with any questions that come up.

Chase21:08:56

Calva includes a debugger too. I'm not too familiar with the debugging options (and I don't use VS Code myself) so I've been playing around with this debugger I just discovered: https://clojurians.slack.com/archives/C06MAR553/p1657736395365869

Chase21:08:23

But tbh I would just focus on Calva and getting familiar with that before taking on too many tools. The cool thing is, if I'm not mistaken, Calva is going to come with the LSP, linter, debugger and formatting capabilities all built in for you.

Martin Půda03:08:50

@U03T0J7DF88 Ok, your move-cells:

(defn move-cells [arr]
  (let [non-zero (filter pos? arr) width (count arr)]
    (let [add-similar (add-pairs non-zero)]
      (concat add-similar (repeat (- width (count add-similar)) 0)))))
Reviewed (I didn't check functionality, just the way code is written):
(defn move-cells [arr]
  (let [non-zero (filter pos? arr)
        width (count arr)
        add-similar (add-pairs non-zero)]
    (concat add-similar (repeat (- width (count add-similar)) 0))))

shravan a08:08:15

@U01RL1YV4P7 @U9J50BY4C The code is running fine when i do "lein run" While i am trying to create jar and execute "lein uberjar" i am getting this exception, kindly help clojure/git/clojure-practice-main/clojure-2408$ java -jar target/uberjar/clojure-2408-0.1.0-SNAPSHOT.jar Exception in thread "main" java.lang.NoClassDefFoundError: clojure/lang/Var at clojure_2408.core.<clinit>(Unknown Source) Caused by: java.lang.ClassNotFoundException: clojure.lang.Var at http://java.net.URLClassLoader.findClass(URLClassLoader.java:387) at java.lang.ClassLoader.loadClass(ClassLoader.java:418) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352) at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

Ed14:08:54

Your execute function looks like a map to me. So you could write something like

(def game-keys {"w" move-up
                "a" move-left
                "s" move-down
                "d" move-right})

(defn collect-user-input-and-move [game-board]
  (let [input (read-line)]
    (if-let [move (game-keys input)]
      (move game-board)
      (do
        (println "invalid input")
        game-board))))
to look up the movement function and apply it to the game-board. This would make it easier to change the keys (for example) which was the first thing I tried to do when I started running your code (w/a/s/d are not near each other on my keyboard)

shravan a15:08:28

Thanks that was helpful

Ed15:08:03

I'm also not such a fan of the way the behaviour of the game is coupled to the mechanics of collecting user input. You have a play-next function which asks for user input using read-line, plays the move and draws the board. This makes it difficult to control from the repl or implement an AI player. I'd like to be able to write a rich comment to play around with the game at the repl and test the code, maybe something like

(comment
  (-> (initialise-board 4 2048)
      add-cell
      (game-step move-down)
      (game-step move-left)
      (game-step move-down)
      print-board)
  )
and see what state the game is in. I do this all the time in codebases I work in, and sometimes commit them as examples of how code works. In general more functional code is easier to do this with. The more state you have to deal with the harder it is to work with at the repl.

🙌 1
Ed15:08:15

I think breaking the code into more delineated layers would help. I think you have some board level operations like get-rows and get-columns, some game level ops like add-cell and the move-* fns, some player level ops like collect-user-input (or even a generate-move if you implemented a automated player) and some game mechanics like printing the board and going round the loop. I think it would be easier to work with if you separated the layers a little more. That aside, generally what you've written is good and with a little tidying as others have mentioned seems like a great start to your Clojure journey ;)

shravan a15:08:59

I will apply all the comments over weekend All the responses are just motivating me

👍 1
Ed15:08:17

I hope you don't mind, but I've had a bit of a play and rejigged your code a bit here: https://gist.github.com/l0st3d/d9e53f3d79d4acdf560f6031c65d0275 ... I'm not suggesting that what I've done is distinctly better than what you've done (I've modelled the board differently for example), just that this made more sense to my brain 😉 ... feel free to compare and contrast and tell me where I've done something stupid ... cos I almost certainly have 😉

shravan a15:08:57

Sure @U0P0TMEFJ i will check it out, I am not able to execute uberjar getting noclassdeffound error, do you have any idea on this

Ed15:08:35

if you look in the target dir, there will be 2 jars. One is just your code, and the other is the uberjar. You need to run that one

java -jar /var/home/ed/dev/l0st3d/clojure-practice/clojure-2408/target/uberjar/clojure-2408-0.1.0-SNAPSHOT-standalone.jar
on my machine.

Ed15:08:36

the one with -standalone in the name is an uberjar which has all the dependencies in it - I think that error is from trying to run the one without the dependencies packaged into it.

shravan a15:08:52

I was running only other one thank you

👍 1
zharinov13:08:15

(defproject blabla "1.2.3" ...)
Is there some standard way to print Leiningen project version in the console? I looked at the help output and found nothing... What I want is something like:
$ lein info --version blabla
1.2.3

dpsutton13:08:53

You want this information for a scenario where you have the code locally and from the code’s directory you want to print its version information?

zharinov13:08:03

I want to use it in the CI pipeline

dpsutton14:08:33

from a repl:

app.core=> (System/getProperty "app.version")
"0.1.0-SNAPSHOT"

zharinov14:08:42

Thank you for the answer. Now need to figure out how to execute it in the one-liner form :thinking_face:

dpsutton14:08:00

i thought there was some kind of `lein run -e ‘(System… )’

dpsutton14:08:31

At the very least you can throw that into a namespace and you can call a main function which returns that

dpsutton14:08:57

i’d ask in #leiningen if anyone knows. I suspect there is such a feature

zharinov14:08:39

Great, I'll try

mafcocinco20:08:13

Could someone explain to me this beahvior? I’m using Clojure version 1.10.3

Alex Miller (Clojure team)20:08:36

the behavior varies based on whether the data is seq? - here it is not in the first example but is in the second

Alex Miller (Clojure team)20:08:13

if seq, then the input is poured into a map to allow for further map destructuring

Alex Miller (Clojure team)20:08:03

but in both cases, you are assuming things when the behavior is undocumented and undefined

Alex Miller (Clojure team)20:08:58

for the seq case, you're seeing implementation details leak through for kwarg rest destructuring

mafcocinco21:08:47

okay. So the correct solution is probably to make sure you are destructuring a map if you are using map destructuring. 🙂

Davi Suga21:08:27

Hello everyone! I've been trying to use https://github.com/opencypher/cypher-for-gremlin/blob/master/translation/README.md in my project using lein, but I'm getting the following error:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See  for further details.
Exception in thread "main" Syntax error macroexpanding clojure.core/ns at (hello_world/handler.clj:1:1).
Call to clojure.core/ns did not conform to spec.
        at clojure.lang.Compiler.checkSpecs(Compiler.java:6971)
        at clojure.lang.Compiler.macroexpand1(Compiler.java:6987)
        at clojure.lang.Compiler.macroexpand(Compiler.java:7074)
        at clojure.lang.Compiler.eval(Compiler.java:7160)
        at clojure.lang.Compiler.load(Compiler.java:7635)
        at clojure.lang.RT.loadResourceScript(RT.java:381)
        at clojure.lang.RT.loadResourceScript(RT.java:372)
        at clojure.lang.RT.load(RT.java:463)
        at clojure.lang.RT.load(RT.java:428)
        at clojure.core$load$fn__6824.invoke(core.clj:6126)
        at clojure.core$load.invokeStatic(core.clj:6125)
        at clojure.core$load.doInvoke(core.clj:6109)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invokeStatic(core.clj:5908)
        at clojure.core$load_one.invoke(core.clj:5903)
        at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
        at clojure.core$load_lib.invokeStatic(core.clj:5947)
        at clojure.core$load_lib.doInvoke(core.clj:5928)
        at clojure.lang.RestFn.applyTo(RestFn.java:142)
        at clojure.core$apply.invokeStatic(core.clj:667)
        at clojure.core$load_libs.invokeStatic(core.clj:5985)
        at clojure.core$load_libs.doInvoke(core.clj:5969)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.core$apply.invokeStatic(core.clj:667)
        at clojure.core$require.invokeStatic(core.clj:6007)
        at clojure.core$require.doInvoke(core.clj:6007)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at user$eval5.invokeStatic(form-init16536261567822868000.clj:1)
        at user$eval5.invoke(form-init16536261567822868000.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:7176)
        at clojure.lang.Compiler.eval(Compiler.java:7165)
        at clojure.lang.Compiler.load(Compiler.java:7635)
        at clojure.lang.Compiler.loadFile(Compiler.java:7573)
        at clojure.main$load_script.invokeStatic(main.clj:452)
        at clojure.main$init_opt.invokeStatic(main.clj:454)
        at clojure.main$init_opt.invoke(main.clj:454)
        at clojure.main$initialize.invokeStatic(main.clj:485)
        at clojure.main$null_opt.invokeStatic(main.clj:519)
        at clojure.main$null_opt.invoke(main.clj:516)
        at clojure.main$main.invokeStatic(main.clj:598)
        at clojure.main$main.doInvoke(main.clj:561)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:705)
        at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec. {:clojure.spec.alpha/problems [{:path [], :reason "Extra input", :pred (clojure.spec.alpha/cat :docstring (clojure.spec.alpha/? clojure.core/string?) :attr-map (clojure.spec.alpha/? clojure.core/map?) :ns-clauses :clojure.core.specs.alpha/ns-clauses), :val ((:require [compojure.core :refer :all] [org.opencypher.gremlin/translation :as translation] [compojure.route :as route] [ring.middleware.defaults :refer [wrap-defaults site-defaults]])), :via [:clojure.core.specs.alpha/ns-form], :in [1]}], :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2509 0x6413eeb7 "[email protected]"], :clojure.spec.alpha/value (hello-world.handler (:require [compojure.core :refer :all] [org.opencypher.gremlin/translation :as translation] [compojure.route :as route] [ring.middleware.defaults :refer [wrap-defaults site-defaults]])), :clojure.spec.alpha/args (hello-world.handler (:require [compojure.core :refer :all] [org.opencypher.gremlin/translation :as translation] [compojure.route :as route] [ring.middleware.defaults :refer [wrap-defaults site-defaults]]))}
        at clojure.spec.alpha$macroexpand_check.invokeStatic(alpha.clj:705)
        at clojure.spec.alpha$macroexpand_check.invoke(alpha.clj:697)
        at clojure.lang.AFn.applyToHelper(AFn.java:156)
        at clojure.lang.AFn.applyTo(AFn.java:144)
        at clojure.lang.Var.applyTo(Var.java:705)
        at clojure.lang.Compiler.checkSpecs(Compiler.java:6969)
        ... 43 more

pppaul21:08:09

org.opencypher.gremlin/translation you can't use that syntax

pppaul21:08:14

is that a subclass?

pppaul21:08:29

use $ instead of / if it's subclass

pppaul21:08:59

if it's a var you have to put that i your :refer

Davi Suga21:08:30

Its a .jar at .m2/repository/org/opencypher/gremlin/translation/1.0.4/translation-1.0.4.jar

Davi Suga21:08:44

If I use with [org.opencypher.gremlin$translation :refer :all] it throws

java.io.FileNotFoundException: Could not locate org/opencypher/gremlin$translation__init.class, org/opencypher/gremlin$translation.clj or org/opencypher/gremlin$translation.cljc on classpath.

pppaul21:08:10

you gotta tell me what org.opencypher.gremlin/translation is

pppaul21:08:22

also, subclasses are imports, not refers

pppaul21:08:35

(:import org.opencypher.gremlin$translation)

Davi Suga21:08:12

I think that org.opencypher.gremlin/translation is a library, I added in the project as a dependency

seancorfield21:08:45

@U03S85Q5P38 Coordinates do not match class names or namespaces.

seancorfield21:08:17

The closest thing I can find, in the opencypher repo be org.opencypher.gremlin.translation.TranslationFacade as a qualified class name and you would import that in Clojure like this:

(ns my.clojure.code
  ...
  (:import (org.opencypher.gremlin.translation TranslationFacade)))
and then use the classname TranslationFacade

seancorfield21:08:21

But it really does depend on what you are trying to do. Are you following some specific documentation / tutorial about this and trying to get it working in Clojure instead of Java/Scala?

pppaul21:08:23

yeah, you have to do that for clojure libs too. make issue requests if repos don't tell you what their namesspaces are in the readme

seancorfield22:08:19

@U0LAJQLQ1 The Gremlin translation repo does show how to use it in its README. @U03S85Q5P38 is struggling to "translate" how to use a Java/Scala library into Clojure code I think (and not realizing that the library name -- coordinates -- rarely match the packages in the Java code / namespaces in the Clojure code).

seancorfield22:08:43

(I'm out for a while but will check back in on this thread later if you're still stuck @U03S85Q5P38)

Davi Suga22:08:54

Yeah, I'm trying to build a simple API server to translate Cypher to Gremlin

Davi Suga22:08:23

At least it is compiling now 🙂

1
pppaul22:08:33

@U03S85Q5P38 are you able to get the classes imported in your ns?

pppaul22:08:34

doing java stuff in clojure isn't very easy. if you are already an expert with the libs, or are proficient in java, that helps a lot. if your editor can inspect objects, that is very helpful. also setting up your editor to use javadocs is good too.

1
Ben Lieberman21:08:42

if I'm using a template/stub that is written in Java, can I just toss it in my project folder and import it like its a core Java library, or will I need to explicitly add it to my classpath

seancorfield21:08:15

It will need to be compiled to a .class file and that will need to be on the classpath.

avillegas23:08:12

Hi! don't want to start a fight or a hot take or anything like that, but is there a simple (I guess in the clojure sense) editor setup for clojure? What I mean by that is something that doesn't require too many pieces interacting with each other in surprising ways. For example, Calva is amazing for getting starter but I usually find that it brings too many new things into my vscode configuration, new keymappings, changes syntax highlighting, etc. I also tried conjure for nvim and at least in all the tutorials I found around it requires that you install many plugins and change many configurations to make it work. What I am looking for is something like a minimal extension that let me sends s-expression from my editor to be evaluated in my running repl. Does something like that exists?

pppaul23:08:30

i don't use it, but people tell me good things about spacemacs

hiredman23:08:55

Just running a repl in a terminal and copying and pasting from your editor works great

😄 2
avillegas23:08:06

I think that is part of the problem too. Clojure for almost everything promotes the use of very decouple pieces that come together to form a solution. how ever for editors I still don't feel that's the case. I which I couldn't had to reset my muscle memory of 10s of years programming in the same enviroment just to fully enjoy clojure. I which those were ortogonal in a way.

didibus23:08:35

If you use Emacs, there is inf-clojure, which is very minimal

💯 1
didibus23:08:37

If you use IntelliJ, Cursive I would say is very much following the IntelliJ patterns, so it won't feel any different, and it is all self-contained, unlike Calva which relies on orchard, lsp, kondo, etc.

Chase00:08:28

I think https://gitlab.com/clj-editors/clover might be a smaller scope solution for VS Code but I personally have no experience with it.

seancorfield01:08:56

I guess the first question should be: what is your favorite editor? What do you have the most muscle memory with?

💯 1
seancorfield01:08:13

Then we can suggest the simplest add-on that provides some Clojure interaction.

avillegas02:08:20

I can do vscode or nvim. both most of the time with vim keybindings

didibus02:08:32

For vim I've heard people say good thing about https://github.com/liquidz/vim-iced but never used it.

didibus02:08:32

You could also try either Spacemacs or Doom Emacs with inf-clojure, both have Vim default keybindings and modal behavior

1
avillegas03:08:28

just took a look at vim-iced, seem a bit more minimal than conjure which is nice. I guess another option will be to write a minimal extension for vscode that sends stuff to a socket repl, no inline evaluation, just very basic stuff.

seancorfield03:08:32

If you're using VS Code and want something very minimal, I can recommend Clover. I used that daily for a long time, before switching to Calva, but you have to start a Socket REPL instead of using nREPL -- Socket REPL is more minimal so that may suit you.

avillegas03:08:21

Thanks a lot for al the help!

seancorfield03:08:15

There are specific channels for almost everything mentioned in this thread so feel free to drop into those and ask more Qs if you need to! #chlorine-clover #calva #conjure #neovim #spacemacs #inf-clojure and probably others.

flowthing06:08:33

If you're willing to give Sublime Text (presumably with Vim key bindings) a try, both https://github.com/tonsky/Clojure-Sublimed and https://tutkain.flowthing.me would fit your requirements, I think.

1
Mattias11:08:46

Already mentioned, but what you describe is precisely how I feel about Chlorine+Atom and Clover-VSCodium. Simple and clean and easy to understand.

👍 1
athomasoriginal13:08:04

• Sublime • Intellij The above are the only editors remaining that I believe could be called “easy”. Prior to Atom being sunset, I would have recommended that as well.

Ed16:08:41

I recal seeing this recently : https://lambdaisland.com/episodes/ultimate-dev-setup ... which definitely demonstrates how easy it is to get basic tooling set up out of the small composable tools in quite a light hearted way 😉