Peg solitaire game in Clojure

2022-01-29

I made another silly little Clojure game, it’s the peg solitaire game with 33 holes. Making simple games like this is a fun way to learn a new language, I think.

The game mechanics follows the version of the game in the book BASIC Computer Games, but there its called "High I-Q" for some reason. Here’s a short animation showing how the game is played:

You select pegs and holes by entering the row number first (1-7) and then the column number (1-7). So to move the piece at row 6 column 4, you enter 64. To move the piece to row 4 column 4, you enter 44.

The code is on GitLab. Run it with

clj -M peg-solitaire.clj

Takeaways:

  • 1: Clear the console with (println "\033[H\033[2J").
  • 2: map-index is like map but you also get the index of the current item.
  • 3: Simplify nested expressions with ->> and ->.
  • 4: Access and change non-trivial data structures with get-in and update-in.
  • 5: A loop with recur can remove the need for atoms.

1: It was surprisingly difficult to find out how to clear the console in Clojure. After a long search I settled on this weird thing which does work (for me): (println "\033[H\033[2J"). I wonder how portable it is, though.

2: map is very useful but this time I also needed to know the index of the current item being processed and map just doesn't provide it. Thankfully, map has a siebling called map-indexed that does just that.

3: The first version of this game had a lot of nested expressions which were hard to read. I tried to fix that with let, which did simplify things to an extent, but it was still quite messy. Then I figured out how to use -> and ->> and readability improved significantly.

4: I’m using a vector of vectors to represent the game board, i.e. a "matrix". A matrix felt like a natural choice but it took me a while to figure out that I should access and change its values with get-in and update-in.

5: My initial instinct was to store the game board in an atom since I would have to update it after every move the player makes. But by using loop and recur, I can set the board to its initial state in the beginning of the loop and then rebind it to the new board state after the player’s move.