Skip to content

Latest commit

 

History

History
123 lines (99 loc) · 4.12 KB

guess-my-number.org

File metadata and controls

123 lines (99 loc) · 4.12 KB

PicoLisp is Simple

PicoLisp is built to be simple from the bottom-up. The author first designed a virtual machine architecture. This allows for simplicity to be baked into the language. To emphasize simplicity, PicoLisp only has one single data structure, the cell. All higher level data types, numbers, symbols, and lists, are built from cells.

Guess my number

We’ll start with a simple game - represented with a cell - and use some numbers and symbols. Perhaps we can use lists when writing messages to the player. The game is… actually its a number guessing AI. You’ll think of a number, and by answering simple questions, the program will either guess your number, or know you cheated!

How can we represent this game with a cell? Well, the boundaries for this game are two numbers your number is between. We can create a cell to represent those boundaries and use a symbol to point to it with a name like this:

(set '*Bounds '(1 . 100))

Actually, I just realized we’re already using lists, this is Lisp after all! List processing is what it is all about. Everything in the parenthesis above is a list. The PicoLisp interpreter knows how to read lists like that as a program. We can read it too. That list starts with set which is a PicoLisp function that lets you set the value or val of a symbol. So, following set is the symbol whose value you want to set. Now, there’s something special here… the =’= mark. This is a shorthand for the quote function which marks something as a piece of data, rather than something the PicoLisp reader should evaluate. Without that quote, *Bounds would evaluate to NIL and NIL is a protected symbol:

: (set *Foo 1)
!? (set *Foo 1)
NIL -- Protected symbol

Bad things would happen if we could set the value of NIL. So, we’re setting the value of the symbol *Bounds to another quoted list. This is our cell. The dot in between 1 and 100 is one way to link these two numbers into a cell. We put 1 in the car of the cell and 100 in the cdr (pronounced “could’r”) by using that dot to indicate the cell. We could also construct a cell by using the cons function:

: (cons 1 100)
-> (1 . 100)

So now, if you type *Bounds in the repl, it will return its value, (1 . 100). There. That is all the data we need to represent our guessing game. But we need to be able to access the lower bound and the upper bound individually. In order to do that we will use the car and cdr functions:

: (car *Bounds)
-> 1
: (cdr *Bounds)
-> 100

The names of those functions are tied to the virtual machine, and mean “contents of the address register” and “contents of the decrement register”. What matters to us though, is how they access parts of a cell. Since everything in PicoLisp is made of cells, you can expect to see car and cdr (and their relatives) quite often.

Since we will be using both those values throughout the guessing game program, lets write some functions that make it easier to use them and know what they mean. I’ll write a function called small here, and I’ll leave it to you to implement big:

(de small ()
   (car *Bounds) )

PicoLisp defines new functions with de. You give it a name, its arguments if any, and then the body of the function. What we get back is the name as a new symbol whose VAL is the rest of the arguments.

(set '*Bounds '(1 . 100))

(de small () 
   (car *Bounds) )

(de big () 
   (cdr *Bounds) )

(de guess-my-number ()
   (if (> (small) (big)) 
      (cheater) 
      (>> 1 (+ (small) (big))) ) )

(de smaller ()
   (set '*Bounds (cons (small) (dec (guess-my-number))))
   (guess-my-number) )

(de bigger ()
   (set '*Bounds (cons (inc (guess-my-number)) (big)))
   (guess-my-number) )

(de cheater ()
   (prinl '(You " " cheated!))
   (bye) )

(de go ()
   (prinl (text "Think of a number between @1 and @2!" (small) (big)))
   (wait 2000)
   (prinl "Here we go! My first guess is... ") 
   (guess-my-number) )

(go)