0

I am learning Clojure and trying to implement a simple tic-tac-toe (or morpion). But i am working hard in order to avoid any ref/atom/agent ...

I know I could do it easily in a console application, as it would not be event-driven, so that I would exactly know when to pass in a new board value. I mean

  • my board would be a vector of vectors ([[:circle 0 :cross][:any :circle :empty][:cross :none :none]] where only :circle and :cross values matter)
  • I would have a simple text-based method which takes a board and return a string

But, whenever I want to implement a graphical Panel, I wonder how can I do the same thing. For example :

  • I am creating an instance of javax.swing.JPanel (please don't care about the other methods that are used here) :

    (defn make-board-panel
    "Builds the board JPanel, whose state is given by board argument,
    and turn-piece argument (must be either :circle or :cross, I mean the next
    value to set in the board)."
    [board turn-piece]
    {:pre ['morpion.core/is-board? board, 'morpion.core/is-a-player-piece? turn-piece]}
      (proxy [JPanel MouseListener] []
        (paintComponent [g]
            (proxy-super paintComponent g)
            (paint-board-lines g)
            (paint-board board g)
        )
        (mouseClicked [e]
            (if (and (abs-coord-in-cell? (.getX e)) (abs-coord-in-cell? (.getY e)))
                (let [cell-x (abs-coord-to-rel (.getX e))
                        cell-y (abs-coord-to-rel (.getY e))]
                    ;; Here I compute the new board value
                )  
                nil ;; Here I wish I could return the new board value to the caller of make-board-panel, but this seems impossible !
            ) 
        )
        (mouseEntered [e])
        (mouseExited [e])
        (mousePressed [e])
        (mouseReleased [e])
       )
    )
    
  • But it seems that there are no way for me to fetch the new value of the board, from the mouseClicked event of the Panel : unless I introduce a state variable.

So is there a workaround :

Sources for my complete project :

(I've tried to improve thanks to Igrapenthin comment, but I am still failing.)

omiel
  • 1,573
  • 13
  • 16
loloof64
  • 5,252
  • 12
  • 41
  • 78
  • 1
    You could pass a callback-function as an additional parameter that will be called from inside the MouseListener with the new board value. – Leon Grapenthin Aug 27 '13 at 14:26
  • Thanks for your idea : I am trying it right now. I did not think about this way (Clojure beginner :) ) – loloof64 Aug 27 '13 at 14:29
  • Are you sure you're using your preconditions right? It looks like all it's doing now is checking of the provided values are non-nil, but that you actually want to check that `board` is a board and `turn-piece` is a piece. – DaoWen Aug 27 '13 at 19:12
  • Why ? Did I forgot parenthesis or something similar ? – loloof64 Aug 27 '13 at 19:30
  • 1
    You can't avoid state unless you want to replace your panel by a new one after each turn. – cgrand Aug 27 '13 at 20:11
  • Yes, that what I planned to do. But for now, I still have some errors to fix. – loloof64 Aug 27 '13 at 21:17

1 Answers1

2
(defn make-board-panel
 [board turn-piece output-fn]
 (proxy [JPanel MouseListener] []
   ;; ...
   (mouseClicked [e]
     (when (and (abs-coord-in-cell? (.getX e)) 
           (abs-coord-in-cell? (.getY e)))
       (let [cell-x (abs-coord-to-rel (.getX e))
             cell-y (abs-coord-to-rel (.getY e))]
          ;; Here I compute the new board value
          (output-fn new-board-value))))
    ;; ...
    ))
Leon Grapenthin
  • 9,246
  • 24
  • 37
  • Yes, I've written something like that, but I am still facing a strange ArityException in my code (it does not point me to the problem, but it just says ArityException in : Symbol) – loloof64 Aug 27 '13 at 16:18
  • 1
    your error is certainly due to the quote (') before morpion.core/play-board – cgrand Aug 27 '13 at 20:14
  • Thanks, I quoted 'morpion.core/play-board because I thought I could fix the namespace problem that way. But it didn't. So I must try with require/use instead. – loloof64 Aug 27 '13 at 20:50
  • If the issue is one of recursive require (both namespaces require the other), either make a third that both can require, or use `resolve` to look up the namespace and function at runtime. – noisesmith Aug 28 '13 at 17:15