Developing the Game Loop

There are many paths to developing successful applications with Clojure (or any language) and many ways this application could be written. However, writing any program involves starting from high-level goals and breaking the problem down into smaller problems, until it’s easy to solve each subproblem with a single (usually small) function.

Our application is a game with a single player where the player guesses letters to slowly reveal a word. The goal for the player is to guess all of the letters in the word with as few guesses as possible.

When you don’t know where to start, it’s always useful to think about the problem and ask what inputs the code must take and what it will return. That’s enough to give you the shape of a function. Then break that function down into smaller problems and repeat.

The inputs to our game are the word the player will guess and a player that can make guesses, so those will be your initial arguments. The return value will be the score the player received.

 (​defn​ game [word player] ...)

This game (and most games) centers around a repeated series of actions where the player makes a choice, the game state is updated, and the player makes another choice. This game loop gives us the overall framework for the game.

The game loop also serves as a guide for what code you need to write next. As you break the loop into steps, these help you identify both the functions that are the sub-problems and the data structures passed between the functions. Repeat this process for each function until you hit the bottom, where you’ll find functions that are self-contained.

Within each iteration, the game needs to ask the player for the next guess and update the progress made on the word. If the word has been guessed, the game is complete and the game should exit with the number of guesses that were made.

In almost every use of a loop, you’ll see a check for termination. If the game is complete, the loop should terminate with the final score. Otherwise, a recur should take the game back to the top of the loop, ready for the next iteration.

 (​defn​ game
  [word player]
  (loop [progress (new-progress word), guesses 1]
  (​let​ [guess (next-guess player progress)
  progress' (update-progress progress word guess)]
  (​if​ (complete? progress' word)
  guesses
  (recur progress' (inc guesses))))))

This code maps very closely to the textual description of the game, with a lot of missing details. There are two pieces of state carried by the loop across iterations: the progress in guessing the word and the score.

The game function invokes next-guess to obtain the next guess from a player, and we’ll consider this later in the chapter when you implement the players.

The other functions we used but didn’t define all relate to creating, updating, or checking the progress of the game: new-progress, update-progress, and complete?. We haven’t yet defined the data structure of the word progress, but it’s clearly a central part of the implementation.

At this point, it’s good to create empty functions for all of the functions we invented while writing the game loop. These empty functions serve as a road map of work you still need to complete. They also allow you to successfully start loading and running the code in the REPL.

 (​defn​ next-guess [player progress])
 (​defn​ new-progress [])
 (​defn​ update-progress [progress word guess])
 (​defn​ complete? [progress])

You can’t write any of the functions to create, update, or check the game progress without knowing what the progress data structure looks like, so that’s where you should focus next.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset