2
votes

I'm currently learning Common Lisp and, as part of the process, am trying to implement a generic tic-tac-toe game where the board can be any odd numbered size (so there's a center square). I got to where I'm checking for winners and am working on this function to check if a row or column have a winner.

(defun straight-winner-p (board start size)
  (let ((row-player (aref board start 0))
        (row-count 0)
        (col-player (aref board 0 start))
        (col-count 0))

    (loop for step from 0 to (- size 1) do

         (if (equal
              (aref board start step)
              row-player)
             (incf row-count))
         (if (equal
              (aref board step start)
              col-player)
             (incf col-count))
         )
    (format t "row ~a, col ~a~%" row-count col-count)))

The format call would eventually be replaced with a check if the player is nil and count equals size. Anyway, I wanted to replace the two ifs with a macro. So, it would be something like

(check row start step)

And the macro would generate the if statement

         (if (equal
              (aref board start step)
              row-player)
             (incf row-count))

Then call the same macro with (check col step start). I can't seem to get the macro to generate row-count and row-player from row. How would you do that?

1
That's not a very Lisp'ish way if programming.DYZ
Why do you need a macro here? Just make that a function and pass the data in as you always would. Macros make your code much harder to understand if used improperly.Carcigenicate
I know it's a fairly dumb way to do things. I'm intentionally doing weird things to hit situations I don't know how to do in the language. There's no good reason to use a macro here, but I'd like to know how to do it.Rich
You could at least use emacs and indent your code right (ie. like emacs does). Also, are you missing a do? I added one in my indented version.Spenser Truex

1 Answers

3
votes

How about using the functionality in the loop macro when you already use the loop macro?:

(defun straight-winner-p (board start size)
  (loop :with row-player := (aref board start 0)
        :and col-player := (aref board 0 start)
        :for step :below size
        :count (equal (aref board start step) row-player) :into row-count
        :count (equal (aref board step start) col-player) :into col-count
        :finally (format t "row ~a, col ~a~%" row-count col-count)
                 (return (or (= row-count size) (= col-count size)))))

How you do what you want:

(defmacro check (prefix start step)
  (let ((player (intern (concatenate 'string (string prefix) (string '-player)) (symbol-package prefix)))
        (count (intern (concatenate 'string (string prefix) (string '-count)) (symbol-package prefix))))
    `(when (equal (aref board ,start ,step) ,player)
       (incf ,count))))

While special care has been taken in case the macro and your code ends up in different packages and use the package of the provided symbol it will not work if the files are reade with different reader settings. If you compile one but not the other it might not work.