Cards implemented in Common Lisp

in Programming & Dev3 years ago

1 cards implemented in common lisp

commonlisp.png

1.1 Choosing the implementation

The absolute first thing we need to talk about is which implementation of common lisp we will be using, since there is quite a few. Personally I have almost gone with SBCL, but there is a lot of other implementations I think two of which is gnu projects, that is clisp, and the gcl.

1.2 Compatibility

Also when you hear a lisper (what lisp programmers refer to themselves as) talk about cross platform compatibility, this is not platform as in OS, rather platform as in which lisp platform you are using, since some common lisps use a little bit extra beyond the common lisp spec (which is in fact quite big).

1.3 Performance

Lisp is also by no means slow, some benchmarks put it around the same speed as c++, the small snag here is that some other factors might have a larger impact on some programs, for instance data structures, where c++ is quite happy to have several different means implementing an array differently from a linked list, or double linked list. Meaning that the data structure is c++ might very well be much faster than the same datastructures in common lisp, since common lisp is mostly implementing all the things in terms of the double linked lists, which does cost some performance.

1.4 Code

Now the code will probably be rather simple, since it is a simple program.

(defun myrange (n lis)
  (if (= n 0)
      lis
      (myrange (- n 1) (cons n lis))))

(defvar suits '(hearts diamonds clubs spades))
(defvar ranks (myrange 13))

a little bit of explanation before we go on. defun is declaring a function, defvar declares a variable the ** on the names on some functions is just a convention for global variables. sbcl handles recursion very well, it is tail call optimized.

The way functions are called in lisp in the first thing in a set of parens is the function or macro you are calling (or a special form, but it does not matter they are all called the same). The next thing is the body. If is for instance a special form, since it will only evaluate one branch, depending on the first expression in the body. In our example the first expression is (= n 0), if this is true the first form will be executed, in our example this is simply lis, however in the case that the expression returns false, it will execute the second expression, and this is where our function adds a new element to the list. Essentially this is range in python.

Now we can make the whole deck of cards with some clever putting together of these two lists since I for one do not want to write out 52 cards.

(loop for a in *suits*
   for b in *ranks*
     collect (list a b))

This is a very simple looping construct in common lisp there are in fact several other ways to iterate through a list, which makes sense with how lisps are used for everything in lisp.

(defun myrange (n lis)
  (if (= n 0)
      lis
      (myrange (- n 1) (cons n lis))))

(defvar suits '(hearts diamonds clubs spades))
(defvar ranks (myrange 13))

(loop for a in suits
for b in ranks
collect (list a b))

This is our code so far, the loop instruction will make the 52 cards as in '(hearts 3) for instance though it will not put in the kings queens jacks and such.

Okay so now that we have a way to create a list containing all the cards, perhaps we should think of a way to shuffle a deck. I have a rather dumb solution to this problem, but it makes it reasonably random and runs very fast.

1.4.1 Shuffling

So the basic premise of what I made up on my own is to take a random element in the list remove it and stick it on the front. Then repeat some thousands of times, 52 thousand should be good. This is very simple.

(defun card-front (n lis)
  (let ((init (subseq lis 0 n))
         (tail (subseq lis (+ n 1) (length lis)))
         (card (nth n lis)))
    (cons card (append init tail))))

This code might be a little complicated, so let's take a look at it in a bit more detail the definition is nothing special. The first form is the let form, this allows several variables to be defined. So the next forms is simply the variables for the final form, with cons and append

This will however allow us to create a shuffle function for the deck.

(defun deck-shuffle (times lis)
  (dotimes (number times)
    (let
        ((r (random (length lis))))
      (setq lis (card-front r lis))))
  lis)

This is the shuffle function, though not as clean as I would have preferred, perhaps a recursive style would suit this better, the singular expression in the let is only used once, so it is not really a big gain. The setq kind of bothers me. (setq is for setting a symbol to some value, and auto quote it.)

This makes it very simple to get a shuffled deck.

Now we just need to create a single function to get a shuffled deck.

(defun myrange (n lis)
  (if (= n 0)
      lis
      (myrange (- n 1) (cons n lis))))

(defvar suits '(hearts diamonds clubs spades))
(defvar ranks (myrange 13))

(defvar deck)
(defun make-deck ()
(setq deck (loop for a in suits
(setq deck for b in ranks
(setq deck collect (list a b)))) ;; not the correct result in fact.

Thus we have a deck, now wy only need to get this shuffled

(defun deck-create ()
  (progn (setq *deck* '())
   (dolist (r *ranks*)
     (dolist (s *suits*)
       (setq *deck*
             (cons (list s r) *deck*))))))
(defun deck-shuffle (times lis)
  (dotimes (number times)
    (let
        ((r (random (length lis))))
      (setq lis (card-front r lis))))
  lis)
(deck-shuffle 52000 *deck*)

1.5 Turns out loop is not well suited for this

I did not test it, when I wrote it originally but it gives bad output So I found a better way to construct this instead

(defun deck-create ()
  (progn (setq *deck* '())
   (dolist (r *ranks*)
     (dolist (s *suits*)
       (setq *deck*
             (cons (list s r) *deck*))))))

And this allowed us to get the shuffled deck, now we only need to do a little bit more to make this into the game war.

Putting it all together we are ready to deal cards out.

(defun myrange (n lis)
  (if (= n 0)
      lis
      (myrange (- n 1) (cons n lis))))

(defvar suits '(hearts diamonds clubs spades))
(defvar ranks (myrange 13))

(defun deck-create ()
(progn (setq deck '())
(dolist (r ranks)
(dolist (s suits)
(setq deck
(cons (list s r) deck))))))
(defun deck-shuffle (times lis)
(dotimes (number times)
(let
((r (random (length lis))))
(setq lis (card-front r lis))))
lis)

(defvar *player* '())
(defvar *comp* '())
(defun card-deal ()
  (dotimes (i 32)
    (if (= (length *deck*) 0)
        'done
        (progn
          (setq *player* (cons (nth 0 *deck*) *player*))
          (setq *comp* (cons (nth 1 *deck*) *comp*))
          (setq *deck* (subseq *deck* 2 (length *deck*)))))))

This is in fact dealing the items in the global variable deck to the player and comp. Just need to shuffle the deck sufficiently before calling this function.

This is an actual deck implemented in common lisp. Initially I wanted to write war for this post as well, but now I decided to publish this as is, to have this as a building block for several card games I might implement. Since I might want to make a few.

Sort:  

Congratulations @iobates! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s) :

You distributed more than 29000 upvotes.
Your next target is to reach 30000 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Support the HiveBuzz project. Vote for our proposal!