leaflogo

Iowa Dave

Living and Learning

A LISP Script for Bridge

September 23, 2025

Let LISP deal the cards for your next game of Bridge.

Here is a Emacs LISP script designed to run on the command line of a terminal window. It simulates shuffling a deck of 52 standard playing cards then dealing four lists of 13 cards each.

This script was written as a newcomer’s project in learning rudiments of the LISP programming language embedded within the GNU Emacs utility.

Figure 1, below, presents a heavily-commented version of the script. I hope the comments provde some value to the reader toward understanding how everything works.

A version with comments stripped-out of the actual LISP code is available for download at this link: bridge-hands.el.

I did my best to write it so it could be run on any platform that has GNU Emacs properly installed and provides a command-line terminal. I believe, without testing, that Mac OS and Windows would satisfy those criteria, along with many flavors of Linux. I leave finding out for sure as an exercise for the reader.

It may help readers unfamiliar with LISP to keep in mind a few things about the way the code instructions are written.

  1. LISP expressions are written as lists inside pairs of parentheses like this: (+ 1 2). The first symbol, which may be a word or, as in this example, a math symbol such as “+”, names a body of code for the LISP interpreter to process.

    In C, it would be a function name, perhaps ”add()“. In MicroBlocks or other graphical languages, it would correspond to one of the puzzle pieces.

    Any remaining items in the list are arguments, that is, the parameter values being passed into the code block. Those arguments are placed inside parentheses in C, for example, add(1, 2). They would be put into little round-shaped places in the puzzle pieces of a graphical language.
  2. Many LISP expressions feature nested pairs of parentheses, which means you have code-inside-of-code.

    It (looks (like (+ 1 2))). I totally made that up. LISP does not have any instructions named looks or like, as far as I know.

    LISP processes such expressios from the inside toward the outside.

    In the made-up example given here, the innermost instruction, (+ 1 2) would be processed first, returning a value of 3. That value, 3, would then be used as an argument to the (like ...) expression. Finally, whatever results from the expression (like 3) becomes an argument to be processed by the (looks ...) expression.
  3. LISP clings to pet names for some of its most important, built-in operations, such as “car”, “cdr” and “cons”. The names had some plausible claim to intuitive meaningfulness when LISP ran only on an IBM model 704 computer in 1970. But now, no longer. Why do LISP enthusiasts stick with those names? Out of fondness, I suppose. Oh, well. After you learn what they mean, they serve just as well as any other name would do.
  4. Above here, I used the word “expression” to mean code inside a pair of parenthesis. (+ 1 2) is an expression. A synonym often used by LISP people is “form”. Expression and form are two different names for the same thing in LISP. I have used both names in this article. Please forgive me.
#! /usr/bin/emacs --script

;; This example presents a LISP script
;; designed to be run from the command-line of a terminal window.
;; The script models a deck of playing cards,
;; shuffles the deck randomly then outputs
;; four 'hands' of 'cards', as for a game of bridge.
;;
;; It was written to be executed with GNU Emacs
;; on a PC running a version of Debian Linux.
;; Please ensure that Emacs is installed on your system.
;; CAUTION: your system might install Emacs differently.
;; PLEASE verify the correct location
;; for the Emacs program on your system.
;; Adjust the 'shebang' line ("#!") above, to recite that location.

;; I put a lot of comments into the script
;; to remind me of what everything does
;; the next time I look at it.
;; Old men should not rely too much on memory.

;; Define a string of card value characters,
;; i.e., numbers and letters such as
;; "T" for Ten, "Q" for Queen and "A" for Ace.
(setq cardvalues "23456789TJQKA")

;; Prepare a "deck of cards" as a list of items
;; associating those card values with different suits, such as Clubs.
(setq cardindex 0)  ;; begin at position zero in the cardvalues string
(setq deck ())      ;; initialize deck as an empty list

;; The while form listed below implements a loop
;; combining thirteen card value characters with four suit indicators
;; to encode fifty-two distinct strings representing different cards.
;; The strings are incorporated into the deck list.

;; for each character in the cardvalues string...
(while (< cardindex (length cardvalues))
  
  ;; The following setq forms have the effect of
  ;; adding items to the deck list,
  ;; where each item is a string encoding one card,
  ;; such as "2D" signifying the Two of Diamonds.
  ;; I indent the steps in this first form for easier reading
  (setq deck
	;; The cons form adds an item to the list named deck.
	(cons
	 ;; The concat form prepares the new item
	 ;; by concatenating a card value and a suit indicator,
	 ;; in this case "D" for Diamonds.
	 (concat
	  ;; The char-to-string form converts
	  ;; a character number into a string character.
	  (char-to-string
	   ;; The elt form obtains the character number
	   ;; corresponding to the character
	   ;; at the cardindex position in the cardvalues string.
	   (elt cardvalues cardindex)
	   ) ;; Complete the char-to-string form
	  "D") ;; Attach "D" to the value character, completing the concat form
	 deck) ;; Complete the cons form; the new card item is ready now.
	) ;; Complete the setq form, replacing the prior deck list
          ;; with a new one having the new card added to the prior deck list.
  
  ;; The following setq forms function identically to the one above
  ;; for the suits "C" Clubs, "H" Hearts and "S" Spades.
  (setq deck (cons (concat (char-to-string (elt cardvalues cardindex)) "C") deck))
  (setq deck (cons (concat (char-to-string (elt cardvalues cardindex)) "H") deck))
  (setq deck (cons (concat (char-to-string (elt cardvalues cardindex)) "S") deck))
  
  ;; After completing four suits with one cardvalue character,
  ;; increment the cardindex to point to the next character
  (setq cardindex (+ cardindex 1))
  
  ;; re-enter the loop while the new cardindex value
  ;; remains less than the length of the cardvalues string
  
  ) ;; exit the while form when cardindex >= length of cardvalues string

;; the deck now contains 52 distinct string items
;; representing the different cards in a bridge deck

;; shuffle the deck
(setq home-position 0)
;; The home-position number will be incremented so as
;; to step through all item positions 0 through 51 in the deck list.

;; for each item position in the deck list...
(while (< home-position (length deck))
  ;; Preserve the item value at the home position temporarily 
  (setq home-value (nth home-position deck))

  ;; Generate a random item number for a location
  ;; anywhere within the deck list.
  (setq away-position (random (length deck)))

  ;; This setcar form overwrites the value of the item at the home-position
  ;; with that of the item at the away-position.
  (setcar
   ;; The nthcdr form selects the item at the home-position.
   (nthcdr home-position deck)
   ;; The nth form returns the value of the item at the away-position.
   (nth away-position deck)
   ) ;; End the setcar form. Now the home-position item
     ;; has the same value as the away-position item.

  ;; One more thing remains to change before moving on to the next position.
  ;; Overwrite the value of the item at the away-position
  ;; with the saved, prior value of the item at the home-position.
  (setcar (nthcdr away-position deck) home-value)

  ;; The values of the home- and away- items have been swapped.

  ;; Increment the home-position 
  (setq home-position (+ home-position 1))

  ;; re-enter the loop while the home-position value
  ;; remains less than the length of the deck list
  
  ) ;; Exit the while form when home-position >= length of deck list

;; The deck list is now randomly sorted.

;; By the way, assuming the Lisp random number generator
;; satisfies the requirement for independent and indentically-
;; distributed random numbers, the number of distinct shuffles
;; of 52 cards would be given by 52!, fifty-two factorial,
;; a number so big it takes two lines to write it out.
;;  80,658,175,170,943,878,571,660,636,856,403,
;; 766,975,289,505,440,883,277,824,000,000,000,000.
;; What this means is that the next set of four bridge hands you generate
;; with this program is unlikely to have ever been seen before,
;; nor likely ever to be seen again.
;; Don't say you never did anything unique! (Because you probably just did!)

;; Deal four hands of thirteen cards each.
(princ "\nFour Bridge Hands\n")
;; for each of four rows...
(setq rownum 0) ;; Think of rownum as the count of completed rows.
(while (< rownum 4)
  ;; Copy thirteen successive values from the sorted deck
  ;; into a formatted string beginning with
  ;; a sensible row number, i.e., rownum + 1.
  (setq row-string
	;; The format form works similarly to printf in the shell and C
	(format "\n%d: %s   %s   %s   %s   %s   %s   %s   %s   %s   %s   %s   %s   %s\n"
		(+ rownum 1)
		(nth (* rownum 13) deck)
 		(nth (+ 1 (* rownum 13)) deck)
		(nth (+ 2 (* rownum 13)) deck)
		(nth (+ 3 (* rownum 13)) deck)
		(nth (+ 4 (* rownum 13)) deck)		     
		(nth (+ 5 (* rownum 13)) deck)		     
		(nth (+ 6 (* rownum 13)) deck)		     
		(nth (+ 7 (* rownum 13)) deck)		     
		(nth (+ 8 (* rownum 13)) deck)		     
		(nth (+ 9 (* rownum 13)) deck)		     
		(nth (+ 10 (* rownum 13)) deck)		     
		(nth (+ 11 (* rownum 13)) deck)		     
	        (nth (+ 12 (* rownum 13)) deck)
	  ) ;; Conclude the format form.
	) ;; Conclude the setq form. The row is ready to print.
  
  ;; Print the row then increment row number
  (princ row-string)
  (setq rownum (+ rownum 1))

  ;; Again, think of row-number as the count of completed rows.
  ;; Re-enter loop while row-number < 4.
  
  ) ;; Exit the while form when row-number >= 4
    ;; Here ends printing of the rows.

;; The terpri form sends a newline to terminate printing.
;; Optional items t t prevent sending more than one newline.
(terpri t t)

;; The program will now exit back to the command-line shell.

;; ===============================================
;;
;; Save this program with a name like 'bridge-hands.el'.
;; Make the file executable:
;;
;;  chmod +x bridge-hands.el
;;
;; Run the file from the command line of a terminal:
;;	  
;;  ./bridge-hands.el
;;
;; It is easy to suppress display of the "Loading ..." lines
;; that appear when you run an Emacs Lisp script on the command line.
;; (See Figure 2, below)
;; My friend Ken Kashmarek would say, "Send them on a one-way trip to the weeds."
;; Simply redirect the stderr output stream to the null device:
;;
;;  ./bridge-hands.el 2>/dev/null
;;
;; Those "Loading ..." lines come from deep in the C-code belly of Emacs.
;; They are likely to differ from one operating system or version of Emacs
;; to the next. They are probably not telling you anything you need to know.
;; After a while one grows accustomed to seeing the lines.
;; They do no harm. I hardly notice them.
	
Figure 1: Emacs LISP script for the command line

Figure 2 illustrates invoking the script in the directory where it was saved, in a terminal window of a Raspberry Pi running RaspberryPi OS, a Debian flavor of Linux.

$ ./bridge-hands.el
Loading /etc/emacs/site-start.d/00debian.el (source)...
Loading /etc/emacs/site-start.d/50dictionaries-common.el (source)...
Loading debian-ispell (native compiled elisp)...
Loading /var/cache/dictionaries-common/emacsen-ispell-default.el (source)...
Loading /var/cache/dictionaries-common/emacsen-ispell-dicts.el (source)...

Four Bridge Hands

1: 2H   8C   5C   2S   KH   2D   KD   6H   4D   7C   3C   9C   5D

2: 6S   AC   3D   KS   9D   6C   8S   TS   8H   AH   6D   4C   JC

3: 4S   QD   3H   7H   JS   7S   9S   2C   QS   8D   AD   9H   QH

4: 5S   TH   KC   3S   5H   4H   QC   JD   AS   TC   7D   TD   JH
	
Figure 2: Example of running the script

As noted below, I assert a copyright on this article. That claim is to protect the words I wrote in the html here and in the script’s comments. No one is allowed to use my words without my permission.

However, and exclusive of the code comments, with respect to the sequence of LISP code forms and expressions, the bits doing the actual work of the script listed in Figure 1, I claim authorship to the extent that it reflects original work resulting from my own study of the Emacs LISP info document, along with the effort and time expended to understand forms and expressions of the LISP language.

Reserving the right to assert that claim of authorship, I will be happy to see anyone enjoy using the code forms any way they like, and hereby release that part of the work into the public domain.

I cannot think of anything else to say at the time of writing. If I do think of something later, I will come back here and revise the page.