# Nostalgia number game done the CHICKEN way

Today I would like to share a little coffee break game I have written with CHICKEN Scheme. I call it "The Number Game", Alaric calls it "a sliding block puzzle". You see a grid of numbered tiles and a hole. You can move one tile at a time and the game's goal is to rearrange the tiles in ascending order.

We will start out with with a basic text based implementation and will end up with a nice graphical version where we navigate the tiles with our mouse.

## The beginning

First let's settle on some design decisions. We will have a square as our game board, let's represent this as a continuous list of elements and worry about the presentation later. The hole in our set shall be represented by the symbol * (star).

To hold our game state consisting of the board, the length of our cube's sides and the position of the hole (here called the cursor), we define a record.

A new board is initialized with a random distribution of numbers and the cursor put at the end of the board.

```(use srfi-1)

(define-record game board size cursor-pos)

(define (new-board size)
(let* ((b (make-list (* size size)))
(l (length b))
(numbers (iota (sub1 l) 1)))
(set! (list-ref b (sub1 l)) '*)
(let loop ((i 0)
(numbers numbers))
(let ((n (random (length numbers))))
(cond ((null? numbers) b)
(else
(set! (list-ref b i) (list-ref numbers n))
(loop (add1 i) (delete (list-ref numbers n) numbers))))))))

(define (new-game size)
(make-game (new-board size)
size
(sub1 (* size size))))
```

To neatly print this board we need a way to separate the columns. SRFI-1's split-at procedure helps us with this. As this procedure returns multiple values, we need to catch those specially with let-values.

```(define (board->cols b s)
(let helper ((l b)
(r '()))
(cond ((null? l) (reverse r))
(else (let-values (((c tl)
(split-at l s)))
(helper tl (cons c r)))))))

(define (print-board s)
(for-each
(lambda (r) (print r))
(board->cols (game-board s)
(game-size s))))```

As you can see, once we are done with that, printing a list of lists is a piece of cake! Let's try this in our repl:

```#;1> (define g (new-game 3))
#;2> (print-board (new-game 3))
(3 5 2)
(1 6 4)
(7 8 *)```

Excellent! To know when we have won we need a procedure that tells us that by looking at the game state. Since the iota procedure usually starts building the list with a 0, we need to tell it explicitly what we need.

```(define (game-over? state)
(equal? (reverse (iota (sub1 (* (game-size state)
(game-size state))) 1))
(cdr (reverse (game-board state)))))```

Now once this is done, let's check whether we have won our game above:

```#;3> (game-over? g)
#f```

Now let's move this cursor around. As we will need this a lot, let's define a procedure that moves the cursor and returns a new game state:

```(define (move-cursor state u)
(let ((state state)
(n (list-ref
(game-board state)
u)))
(set! (list-ref (game-board state)
(game-cursor-pos state))
n)
(set! (list-ref (game-board state)
u)
'*)
(game-cursor-pos-set! state u)
state))```

The argument u will tell us the new position to set it to. Where does u come from? This all depends on the direction we want to move the cursor and the constraints given by the board size.

To ease things a bit, let's write a macro, that defines a movement procedure for us. This can be done without of course, let's do it anyway.

```(define-syntax define-movement
(ir-macro-transformer
(lambda (x i c)
(let ((name (second x))
(update (third x))
(pred (fourth x)))
`(define (,name ,(i 'state))
(if ,pred
(move-cursor ,(i 'state) ,update)
,(i 'state)))))))

(define-movement up
(- (game-cursor-pos state)
(game-size state))
(<= 0 (- (game-cursor-pos state)
(game-size state))))

(define-movement down
(+ (game-cursor-pos state)
(game-size state))
(< (+ (game-cursor-pos state)
(game-size state))
(* (game-size state)
(game-size state))))

(define-movement left
(sub1 (game-cursor-pos state))
(not (= 0 (modulo
(game-cursor-pos state)
(game-size state)))))

(define-movement right
(not (= 0 (modulo
(game-size state)))))```

See? And it only hurt a bit. Now that we now when the game is over and that we can move the hole around, the only thing that's left for the game to be playable is a simple loop that takes our input, updates the game state and prints the new board.

```(define (start-game s)
(call-with-current-continuation
(lambda (exit)
(let game-loop ((b (new-game s)))
(print-board b)
(when (game-over? b)
(print "Congratulations, you won the game!")
(exit 'done))
(printf "> ")
((#\u #\U) (up b))
((#\d #\D) (down b))
((#\l #\L) (left b))
((#\r #\R) (right b))
((#\q #\Q) (exit 'aborted))
(else (print "please use (u)p (d)own (l)eft (r)ight or (q)uit")
b)))))))```

Let's play!

```#;8> (start-game 3)
(4 5 2)
(1 7 8)
(6 3 *)
> u
(4 5 2)
(1 7 *)
(6 3 8)
> l
(4 5 2)
(1 * 7)
(6 3 8)
> l
(4 5 2)
(* 1 7)
(6 3 8)
> u
(* 5 2)
(4 1 7)
(6 3 8)
> r
(5 * 2)
(4 1 7)
(6 3 8)
> d
(5 1 2)
(4 * 7)
(6 3 8)
> q```

## Graphics!

With this in place we can start writing our graphical version of it. For this game I will use a 400x400 pixel window with a dark gray background.

```(use doodle matchable)

(new-doodle height: 400 width: 400 background: '( 0.5 0.5 0.5 1))

(define offsetx 20)
(define offsety 20)

(define area-length 360)```

Within this area we have a 20x20 pixels border, thus leaving us with a square of 360x360 pixels as a gaming area. Before we start drawing our tiles we need to define a mapping procedure which will give us screen coordinates for a given tile.

```(define (tile->coords game tile)
(let* ((tile-size (/ area-length (game-size game)))
(idx (list-index (cut equal? tile <>) (game-board game)))
(row (quotient idx (game-size game)))
(r (remainder idx (game-size game)))
(col (if (null? r) 3 r)))
(values (+ offsetx (* col tile-size))
(+ offsety (* row tile-size)))))```

Whith this, we can draw the board. If we encounter our cursor, we will draw a light grey square, else we will draw a white rectangle with the number in the centre (well almost in the centre).

```(define (draw-board s)
(let ((tile-size  (/ area-length (game-size s))))
(for-each
(lambda (tile)
(let-values (((x y) (tile->coords s tile)))
(cond ((eq? tile '*)
(filled-rectangle x y tile-size tile-size '(0.7 0.7 0.7 1))
(rectangle x y tile-size tile-size solid-black))
(else
(rectangle x y tile-size tile-size solid-black)
(text (+ x
(/ tile-size 2))
(+ y
(/ tile-size 2))
(->string tile)
align: #:center)))))
(game-board s))))```

Now we are ready to write a simple gaming loop with doodle. We will store the current game state in the variable *b* and the current board size in *s*. When the world initializes we create a new world, then listen in on the input.

```(define *b* #f)
(define *s* 4)

(world-inits
(lambda ()
(set! *b* (new-game *s*))))

(world-changes
(lambda (events dt quit)
(clear-screen)
;; draw the gaming area white
(filled-rectangle offsetx offsety area-length area-length solid-white)

;; Handle user input
(for-each
(lambda (e)
(set! *b*
(match e
;; handle movement with cursor keys
(('key 'pressed 'up) (up *b*))
(('key 'pressed 'down) (down *b*))
(('key 'pressed 'left) (left *b*))
(('key 'pressed 'right) (right *b*))
;; Exit by pressing q
(('key 'pressed #\q) (quit 'aborted))
;; Restart a game with r
(('key 'pressed #\r) (set! *b* (new-game *s*)) *b*)
;; Increase and decrease the number of tiles
(('key 'pressed #\+) (set! *s* (add1 *s*)) (set! *b* (new-game *s*)) *b*)
(('key 'pressed #\-) (when (< 3 *s*) (set! *s* (sub1 *s*)) (set! *b* (new-game *s*))) *b*)
;; Move the blocks by clicking on them
(('mouse 'pressed x y _) (handle-clicks *b* x y))
(else *b*))))
events)

;; draw our board
(draw-board *b*)
;; update the window
(show!)
(when (game-over? *b*)
(parameterize
((font-color '(1 0 0 1))
(font-size 42))
(text 100 200  "YOU WIN!" '(1 0 0 1) align: #:center))
(show!)
(set! *b* (new-game *s*)))))

(run-event-loop run-in-background: #t)```

You may notice one missing part. Above we also listen to mouse events. Let's fill in this blank. Again we start out by defining a mapping procedure that will give us a tile index when we have clicked on a tile. Then we compute the tiles that can be moved, compare the clicked tile to that list and update our cursor accordingly.

```(define (coords->tile s x y)
(and-let* ((_ (>= x offsetx))
(_ (>= y offsety))
(_ (<= y (+ offsety area-length)))
(_ (<= x (+ offsetx area-length)))
(x (- x offsetx))
(y (- y offsety))
(tile-size  (/ area-length (game-size s)))
(col (quotient x tile-size))
(row (quotient y tile-size)))
(+ col (* row (game-size s)))))

(define (find-movable-tiles game)
(let* ((star (game-cursor-pos game))
(size (game-size game))
(board-size (length (game-board game)))
(potentials (list (- star size)
(+ star 1)
(- star 1)
(+ star size)))
(inside? (lambda (p)
(and (<= 0 p)
(< p board-size)))))
(filter inside? potentials)))

(define (handle-clicks b x y)
(let ((idx (coords->tile b x y))
(star (game-cursor-pos b)))
(when (member idx (find-movable-tiles *b*))
(set! b (move-cursor b idx)))
b))```

That's it! We have now everything we need to play our number game. Once assembled yours will look a bit like this:

## Bonus points

I invite you to play (with) our silly little game a bit. You can

• Make the pieces prettier
• Add a status bar and count moves
• Implement an AI for solving the puzzle for you

## Farewell

I hope you have enjoyed the little coffee break game of mine. I appreciate any feedback on it and I would love to hear of your attempts with this game and doodle.

Take care and have fun writing games in CHICKEN scheme!