---
layout: ../Site.layout.js
---
# cl-unicode-chicken-sudoku : a common lisp program on the back of a cereal box

```
🐜 ⬜ πŸ•· 🦟 πŸͺ³ πŸ¦€ ⬜ 🐌 🐚
⬜ ⬜ πŸ¦€ πŸͺ³ ⬜ πŸ•· 🐜 πŸ¦‚ ⬜
🐚 πŸͺ³ πŸ¦‚ πŸ¦€ 🐌 🦟 πŸ•· 🐞 🐜
πŸ•· 🦟 🐜 ⬜ πŸ¦‚ 🐌 🐚 πŸ¦€ πŸͺ³
πŸ¦€ ⬜ 🐌 🐚 ⬜ 🐞 ⬜ 🦟 πŸ•·
πŸͺ³ ⬜ ⬜ ⬜ 🐞 🐜 🦟 πŸ•· ⬜
⬜ πŸ•· 🐞 🐜 🐚 πŸ¦‚ ⬜ πŸͺ³ 🐌
🐞 ⬜ 🦟 πŸ•· ⬜ πŸͺ³ 🐌 🐚 πŸ¦‚
⬜ πŸ¦€ πŸͺ³ 🐌 πŸ•· 🐚 ⬜ 🐜 🦟
```

I had a lot of fun writing this - my reward over effort fraction was much greater than `1`. Initially I tried *polishing it* but I think the polish made it worse, somehow. So here's cl-unicode-chicken-sudoku. I wrote it as a joke response to my friend [shizamura](https://ciberlandia.pt/@shizamura) [(O Sarilho Future Romans webcomic)](https://sarilho.net/en/) saying ['sudoku lisp engine' on the Mastodon](https://ciberlandia.pt/@shizamura/114972299471462758).

Without further ado

# `map-derangements` from Alexandria sudoku

[Alexandria](https://alexandria.common-lisp.dev/), whose name I think is a play on the library at Alexandria and the UCal Architect Christopher Alexander who wrote a forward to Richard P Gabriel's [Patterns of Software](https://www.dreamsongs.com/Files/PatternsOfSoftware.pdf) essays - anyway, it's stuff-you-need-that's-not-in-the-ANSI-standard for common lisp.

One thing is computing [derangement](https://en.wikipedia.org/wiki/Derangement) permutations, which would be tedious to program yourself but reduce the search space of making-a-sudoku a lot.

*ACT* is a derangement of *TAC* because every letter changed position.

## Setup

```
β€’ (setq inferior-lisp-program "ecl")
β€’ (slime)
β€’ (setq eepitch-buffer-name "*slime-repl ECL*")

(require :alexandria)
(use-package :alexandria)

'(a b c d)
(map-derangements 'print *)
```

```
(B A D C)
(D A B C)
(C A D B)
(B D A C)
(C D A B)
(D C A B)
(B C D A)
(C D B A)
(D C B A)
```

## `try-derangement` - maybe add a row to the sudoku

This is just how it worked when I programmed it; initially I had the idea to keep shrinking the derangements, but it seemed to be fast-enough in practice already, so. Similarly I initially tried randomising the starting point in the derangement, but this basically made it a lot slower and was a reasonably subtle improvement best handled differently; not a key issue.

```
(defun try-derangement
   (all-syms
    &aux (syms (car all-syms)))
 (let* ((ders '((9 . 133496)
                (8 . 14833)
                (7 . 1854)
                (6 . 265)
                (5 . 44)
                (4 . 9)
                (3 . 2)
                (2 . 1)))
        (count 0)
        (max (cdr (assoc (length syms) ders))))
   (flet
       ((any-equal (&rest rest)
          (mapl
           (lambda (l)
             (when (member (car l) (cdr l))
               (return-from any-equal t)))
           rest)
          (values)))
     (map-derangements
      (lambda (d)
        (let* ((any-equals
                 (apply 'mapcar #'any-equal d all-syms)))
          (when (not (some 'identity any-equals))
            (return-from try-derangement
              (nconc all-syms (list d)))))
        (incf count)
        (when (equal count max)
          (return-from try-derangement nil)))
      syms))))
```

Whence.

```
TRY-DERANGEMENT
CL-USER> '((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟))
((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟))
CL-USER> (try-derangement *)
((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟) (πŸ¦‘ πŸ™ πŸ‹ 🐚 🐑 🐬 🐟 🐠 🦈))
CL-USER> (try-derangement *)
((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟) (πŸ¦‘ πŸ™ πŸ‹ 🐚 🐑 🐬 🐟 🐠 🦈) (🐚 πŸ‹ πŸ™ πŸ¦‘ 🦈 🐟 🐬 🐑 🐠))
CL-USER>
```

It doesn't matter what pictures you use in a Latin squares like sudoku: We could use numbers.

```
'((1 2 3 4 5 6 7 8 g))
(try-derangement *)
```

```
CL-USER> '((1 2 3 4 5 6 7 8 g))
((1 2 3 4 5 6 7 8 G))
CL-USER> (try-derangement *)
((1 2 3 4 5 6 7 8 G) (2 1 4 3 6 5 G 7 8))
```

# Simple approach to making one sudoku

```
(defvar *last*)
(setq *last* `(,(shuffle '(🦀 🦩 🐣 πŸ¦‰ 🐧 🦚 πŸ¦ƒ 🦫 🍎))))
(loop :while (< (length *last*) 9) :do
 (setq *last* (or (try-derangement *last*)
                  *last*))
 :finally (return (shuffle *last*)))
```

```
CL-USER> (defvar *last*)
*LAST*
CL-USER> (setq *last* `(,(shuffle '(🦀 🦩 🐣 πŸ¦‰ 🐧 🦚 πŸ¦ƒ 🦫 🍎))))
((🍎 🦀 πŸ¦‰ πŸ¦ƒ 🐧 🦩 🦚 🦫 🐣))
CL-USER> (loop :while (< (length *last*) 9) :do
            (setq *last* (or (try-random-derangement *last*)
                                          *last*))
              :finally (return (shuffle *last*)))
((🐧 🐣 🦚 🦫 🦀 🍎 🦩 πŸ¦‰ πŸ¦ƒ) (🦫 🦩 🐣 🐧 🦚 πŸ¦‰ πŸ¦ƒ 🍎 🦀) (🦚 🦫 🐧 🦩 πŸ¦‰ πŸ¦ƒ 🦀 🐣 🍎)
(🦩 🦚 🦫 🐣 🍎 🦀 πŸ¦‰ πŸ¦ƒ 🐧) (🦀 🍎 πŸ¦ƒ πŸ¦‰ 🦩 🐧 🐣 🦚 🦫) (πŸ¦ƒ πŸ¦‰ 🦀 🍎 🐣 🦚 🦫 🐧 🦩)
(πŸ¦‰ πŸ¦ƒ 🍎 🦀 🦫 🐣 🐧 🦩 🦚) (🐣 🐧 🦩 🦚 πŸ¦ƒ 🦫 🍎 🦀 πŸ¦‰) (🍎 🦀 πŸ¦‰ πŸ¦ƒ 🐧 🦩 🦚 🦫 🐣))
```

## Put that setq scheme in a function

```
(defun make-complete-sudoku (things)
 "
things should be a list, elements different under EQL. Returns a shuffled 9x9 sudoku, hopefully.
"
 (loop
   :for last := (list things) :then (or new last)
   :for new := (try-derangement last)
   :while (< (length last) 9)
   :finally (return (shuffle last))))
```

We Knuth shuffle the rows in the end (we should probably transpose it and shuffle again, but eh).

## Nice printing function

```
(defun print-sudoku (list-of-lists &optional (stream t))
 (format stream "~{~{~2,,,' a~}~^~%~}" list-of-lists))
```

Good old [lisp formatted output aesthetic print parameters](https://novaspec.org/cl/22_3_Formatted_Output#sec_22_3_2_2).

# Print that bird sudoku

```
'((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟))
(make-complete-sudoku *)
(print-sudoku *)
```

```
CL-USER> '((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟))
((πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟))
CL-USER> (make-complete-sudoku *)
((🐟 🐬 🐑 🐠 πŸ‹ 🦈 πŸ™ πŸ¦‘ 🐚) (🐚 πŸ‹ πŸ™ πŸ¦‘ 🦈 🐟 🐬 🐑 🐠) (🐬 🐟 🐠 🦈 πŸ¦‘ πŸ™ 🐑 🐚 πŸ‹)
(🐠 🦈 🐬 🐑 🐚 πŸ‹ πŸ¦‘ 🐟 πŸ™) (πŸ‹ 🐚 πŸ¦‘ πŸ™ 🐟 🐠 🦈 🐬 🐑) (πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟)
(πŸ¦‘ πŸ™ πŸ‹ 🐚 🐑 🐬 🐟 🐠 🦈) (🦈 🐑 🐟 🐬 🐠 🐚 πŸ‹ πŸ™ πŸ¦‘) (🐑 🐠 🦈 🐟 πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬))
CL-USER> (print-sudoku *)
🐟 🐬 🐑 🐠 πŸ‹ 🦈 πŸ™ πŸ¦‘ 🐚
🐚 πŸ‹ πŸ™ πŸ¦‘ 🦈 🐟 🐬 🐑 🐠
🐬 🐟 🐠 🦈 πŸ¦‘ πŸ™ 🐑 🐚 πŸ‹
🐠 🦈 🐬 🐑 🐚 πŸ‹ πŸ¦‘ 🐟 πŸ™
πŸ‹ 🐚 πŸ¦‘ πŸ™ 🐟 🐠 🦈 🐬 🐑
πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬 🐑 🐠 🦈 🐟
πŸ¦‘ πŸ™ πŸ‹ 🐚 🐑 🐬 🐟 🐠 🦈
🦈 🐑 🐟 🐬 🐠 🐚 πŸ‹ πŸ™ πŸ¦‘
🐑 🐠 🦈 🐟 πŸ™ πŸ¦‘ 🐚 πŸ‹ 🐬
NIL
```

# Fish unicode

πŸ‘πŸŸβ—‰πŸ₯πŸŽ£πŸ πŸ¦ˆπŸ¦«πŸ‰β₯ΏπŸ™πŸ¦‘πŸšπŸ¦­πŸ‹πŸ¬πŸͺΈπŸ¦€

thanks to [unix_surrealism](https://analognowhere.com/) for puffy of fish linux.

More unicode came from [my bespoke collection over here](/lispgames/LCKR-running-the-simulation/) of unicode plants, birds and bugs.

# Randomly erase tiles

```
(defun randomly-erase (n sudoku &key (blank '⬜))
 (let ((cols (length (car sudoku)))
       (rows (length sudoku))
       (count 0))
   (loop
     :while (< count n)
     :for r := (random rows)
     :for c := (random cols)
     :unless (equal (nth c (nth r sudoku)) blank) :do
       (setf (nth c (nth r sudoku)) blank
             count (1+ count))
     :finally (return sudoku))))
```

# Complete example with numbers

```
'((1 2 3 4 5 6 7 8 9))
(make-complete-sudoku *)
(randomly-erase 20 * :blank '_)
(print-sudoku *)
```

```
CL-USER> '((1 2 3 4 5 6 7 8 9))
((1 2 3 4 5 6 7 8 9))
CL-USER> (make-complete-sudoku *)
(randomly-erase 20 * :blank '_)
((9 5 6 7 4 8 1 2 3) (6 7 8 9 1 2 3 4 5) (2 1 4 3 6 5 9 7 8)
(8 6 9 5 7 3 4 1 2) (4 3 2 1 9 7 8 5 6) (5 9 7 8 2 1 6 3 4)
(3 4 1 2 8 9 5 6 7) (7 8 5 6 3 4 2 9 1) (1 2 3 4 5 6 7 8 9))
CL-USER> ((9 5 6 7 4 _ _ _ _) (_ _ 8 9 1 2 3 4 _) (2 1 4 3 6 5 9 7 8)
(8 6 _ _ 7 3 4 1 2) (4 3 2 _ 9 _ _ 5 6) (5 9 7 _ 2 1 6 3 4)
(3 _ 1 2 8 9 5 _ 7) (7 8 5 _ 3 4 _ _ _) (1 2 3 4 _ 6 7 8 9))
CL-USER> (print-sudoku *)
9 5 6 7 4 _ _ _ _
_ _ 8 9 1 2 3 4 _
2 1 4 3 6 5 9 7 8
8 6 _ _ 7 3 4 1 2
4 3 2 _ 9 _ _ 5 6
5 9 7 _ 2 1 6 3 4
3 _ 1 2 8 9 5 _ 7
7 8 5 _ 3 4 _ _ _
1 2 3 4 _ 6 7 8 9
NIL
```

emoji are bigger than alphanumeric characters, hence the changing of the `:blank` symbol for numbers above.

# Bug sudoku

```
CL-USER> '((🐞 🐜 🦟 πŸ•· πŸ¦€ πŸͺ³ 🐌 🐚 πŸ¦‚))
((🐞 🐜 🦟 πŸ•· πŸ¦€ πŸͺ³ 🐌 🐚 πŸ¦‚))
CL-USER> (make-complete-sudoku *)
((🐜 🐞 πŸ•· 🦟 πŸͺ³ πŸ¦€ πŸ¦‚ 🐌 🐚) (🐌 🐚 πŸ¦€ πŸͺ³ 🦟 πŸ•· 🐜 πŸ¦‚ 🐞) (🐚 πŸͺ³ πŸ¦‚ πŸ¦€ 🐌 🦟 πŸ•· 🐞 🐜)
(πŸ•· 🦟 🐜 🐞 πŸ¦‚ 🐌 🐚 πŸ¦€ πŸͺ³) (πŸ¦€ πŸ¦‚ 🐌 🐚 🐜 🐞 πŸͺ³ 🦟 πŸ•·) (πŸͺ³ 🐌 🐚 πŸ¦‚ 🐞 🐜 🦟 πŸ•· πŸ¦€)
(🦟 πŸ•· 🐞 🐜 🐚 πŸ¦‚ πŸ¦€ πŸͺ³ 🐌) (🐞 🐜 🦟 πŸ•· πŸ¦€ πŸͺ³ 🐌 🐚 πŸ¦‚) (πŸ¦‚ πŸ¦€ πŸͺ³ 🐌 πŸ•· 🐚 🐞 🐜 🦟))
CL-USER> (randomly-erase 20 *)
((🐜 ⬜ πŸ•· 🦟 πŸͺ³ πŸ¦€ ⬜ 🐌 🐚) (⬜ ⬜ πŸ¦€ πŸͺ³ ⬜ πŸ•· 🐜 πŸ¦‚ ⬜) (🐚 πŸͺ³ πŸ¦‚ πŸ¦€ 🐌 🦟 πŸ•· 🐞 🐜)
(πŸ•· 🦟 🐜 ⬜ πŸ¦‚ 🐌 🐚 πŸ¦€ πŸͺ³) (πŸ¦€ ⬜ 🐌 🐚 ⬜ 🐞 ⬜ 🦟 πŸ•·) (πŸͺ³ ⬜ ⬜ ⬜ 🐞 🐜 🦟 πŸ•· ⬜)
(⬜ πŸ•· 🐞 🐜 🐚 πŸ¦‚ ⬜ πŸͺ³ 🐌) (🐞 ⬜ 🦟 πŸ•· ⬜ πŸͺ³ 🐌 🐚 πŸ¦‚) (⬜ πŸ¦€ πŸͺ³ 🐌 πŸ•· 🐚 ⬜ 🐜 🦟))
CL-USER> (print-sudoku *)
🐜 ⬜ πŸ•· 🦟 πŸͺ³ πŸ¦€ ⬜ 🐌 🐚
⬜ ⬜ πŸ¦€ πŸͺ³ ⬜ πŸ•· 🐜 πŸ¦‚ ⬜
🐚 πŸͺ³ πŸ¦‚ πŸ¦€ 🐌 🦟 πŸ•· 🐞 🐜
πŸ•· 🦟 🐜 ⬜ πŸ¦‚ 🐌 🐚 πŸ¦€ πŸͺ³
πŸ¦€ ⬜ 🐌 🐚 ⬜ 🐞 ⬜ 🦟 πŸ•·
πŸͺ³ ⬜ ⬜ ⬜ 🐞 🐜 🦟 πŸ•· ⬜
⬜ πŸ•· 🐞 🐜 🐚 πŸ¦‚ ⬜ πŸͺ³ 🐌
🐞 ⬜ 🦟 πŸ•· ⬜ πŸͺ³ 🐌 🐚 πŸ¦‚
⬜ πŸ¦€ πŸͺ³ 🐌 πŸ•· 🐚 ⬜ 🐜 🦟
NIL
```

# Conclusion

Anyway, I thought it was a lot of fun! Shizamura says that sudoku with at least 21 clues have a unique solution. My joke was that instead of writing numbers, you have to draw the emojis: Then if all of an emoji got lost the sudoku becomes unsolveable (what animal was it?).

I'll stick sudoku on the front page in an editable textbox after the show.

I am not sure if one of my mathematical friends is going to tell me there was a better way than iteratively checking derangements of a single row: I couldn't come up with one off hand, and what we did was fast enough and quite concise.

# Fin.

See everyone in one hour on https://anonradio.net:8443/anonradio for my live interview with Larian of the [Chronicles of Ember ttrpg](https://www.chroniclesofember.com/)!

This being 000UTC / 0:00 Zulu time Wednesday (8pm Tuesday in Boston). If you saw this late the archive will be at https://communitymedia.video/c/screwtape_channel/videos as normal now.

Remember live chat for the show happens in lambdaMOO. This would work:

```
telnet lambda.moo.mud.org 8888
co guest
@join screwtape>
"Hey, I have a question for Larian!
:presses enter but does not close quotes in MOOlish
```

Also [on the Mastodon to talk about this fun post](https://gamerplus.org/@screwlisp/114978530713751015).