## Nim

### January 8, 2010

We represent a game position as a list of the numbers of the stones in each pile; the game ends when the list is reduced to all zeros. A move is a pair with a pile in its `car`

and a number of stones to remove in its `cdr`

. The `nim-sum`

is calculated by a simple recursion:

`(define (nim-sum pos)`

(if (null? pos) 0

(logxor (car pos) (nim-sum (cdr pos)))))

The heart of the program is the function `choose-move`

, which takes a position and returns a move. If the `nim-sum`

of the position is zero, in the first leg of the `if`

, the computer is in a losing position, and `choose-move`

simply removes one stone from the first non-empty pile. Otherwise, `choose-move`

loops through the piles until it finds one where the exclusive-or of the number of stones with the `nim-sum`

is less than the number of stones, and makes the appropriate move:

`(define (choose-move pos)`

(let ((x (nim-sum pos)))

(if (zero? x)

(let loop ((pos pos) (k 1))

(if (positive? (car pos)) (cons k 1)

(loop (cdr pos) (+ k 1))))

(let loop ((pos pos) (k 1))

(if (< (logxor (car pos) x) (car pos))

(cons k (- (car pos) (logxor (car pos) x)))

(loop (cdr pos) (+ k 1)))))))

`Apply-move`

takes a position and a move and returns the resulting position:

`(define (apply-move pos move)`

(let* ((k (- (car move) 1))

(fs (take k pos))

(bs (drop k pos)))

(append fs (cons (- (car bs) (cdr move)) (cdr bs)))))

We need to be able to display positions and moves for the interactive game:

`(define (display-pos pos)`

(do ((pos pos (cdr pos)) (k 1 (+ k 1)))

((null? pos))

(display k)

(display ": ")

(display (car pos))

(newline)))

`(define (display-move move)`

(display "I remove ") (display (cdr move))

(if (= (cdr move) 1)

(display " stone from pile ")

(display " stones from pile "))

(display (car move)) (newline))

The computer calls `choose-move`

to determine its next move; `ask-move`

displays the current position, then asks the human player to enter his move:

`(define (prompt message) (display message) (read))`

`(define (ask-move pos)`

(display-pos pos)

(let* ((pile (prompt "Pile? ")) (stones (prompt "Stones? ")))

(cons pile stones)))

The `play`

function processes human moves and computer moves; it recognizes the end of the game when the count of all the stones remaining in all the piles is zero:

`(define (play pos who)`

(cond ((zero? (apply + pos))

(display (if (eq? who 'human) "I win" "You win"))

(newline))

((eq? who 'human)

(play (apply-move pos (ask-move pos)) 'computer))

(else (let ((move (choose-move pos)))

(display-move move)

(play (apply-move pos move) 'human)))))

The game starts with the `nim`

function, which takes the number of stones in each pile as its arguments:

`(define (nim . xs)`

(let* ((msg "Enter 1 to move first or 2 to move second: "))

(play xs (if (= (prompt msg) 1) 'human 'computer))))

Here’s a sample game, so you can see how everything fits together:

`> (nim 3 4 5)`

Enter 1 to move first or 2 to move second: 1

1: 3

2: 4

3: 5

Pile? 1

Stones? 2

I remove 1 stone from pile 1

1: 0

2: 4

3: 5

Pile? 3

Stones? 1

I remove 1 stone from pile 2

1: 0

2: 3

3: 4

Pile? 3

Stones? 1

I remove 1 stone from pile 2

1: 0

2: 2

3: 3

Pile? 3

Stones? 1

I remove 1 stone from pile 2

1: 0

2: 1

3: 2

Pile? 3

Stones? 1

I remove 1 stone from pile 2

1: 0

2: 0

3: 1

Pile? 3

Stones? 1

You win

We use `take`

, `drop`

, and `logxor`

from the Standard Prelude. The code is collected at http://programmingpraxis.codepad.org/aNviDpKG.

Pages: 1 2

[…] Praxis – Nim By Remco Niemeijer In today’s Programming Praxis we have to program the game Nim. Let’s get […]

My Haskell solution (see http://bonsaicode.wordpress.com/2010/01/08/programming-praxis-nim/ for a version with comments):

Python version. For fun, it simulates the slow typing of an old 300 baud modem.