Wednesday, 14 January 2015

Think of a number... Any number.

Learning to program it's not an easy task: there are several aspects to consider, for example which language and system. One could even start learn programming for the embedded world directly in assembly or choose to master WEB development with javascript. Anyway we think that the learning process is more efficient when it's carried out with practical examples so let's start!


 Guessing the number.

The easiest of all computer games to program is the one in which the computer choose randomly from a set of integer numbers and the user tries to guess it. To start programming without installing anything on the computer you can use this site and follow its instruction to setup an user account. After you logged in you face the REPL with input point represented by a "?"


 The random function.

All Common Lisp implementations have a random number generation function to allow people invent such games and others. As described in the hyperspec the function random is:
(random limit &optional random-state)
It returns a number equal to zero or less than the limit parameter which could be integer or float. According to the book "Common Lisp the Language, 2nd Edition" (pag. 391) the random-state is an object used to maintain the state of the pseudo-random-number generator and is altered as a side effect of the random operation. Its default value is *random-state*. The random-state type is implementation-dependent; it means that it is possible to print out an object of this type and then read back but this may or may not work correctly in another implementation. Let's go on and enter from REPL:
(defparameter *generator-random-state* (make-random-state t)) 
(defparameter *minimum-guess-number* 1 "The smallest integer to choose from.") 
(defparameter *maximum-guess-number* 10 "The greatest integer to choose from.")
(defun generate-integer (minimum-value maximum-value)
   "Generate a random number inside a span." 
   (+ minimum-value (random (1+ (- maximum-value minimum-value)) *generator-random-state*)))
So what have we done here is create a new function named GENERATE-INTEGER along with the special variable *generator-random-state*. Useful programs are often made of functions, called in a particular sequence, that accept zero, one or more parameters and returns single or more values. There's no a privileged function, like in other languages, where to start a program, since code execution is made by calling functions from REPL or from other functions. We entered another kind of form (defun ...) which is a "special form" the evaluation of this won't execute code in the sense of the function itself, but instruct the interpreter how to form our function. If we enter from REPL:
(loop repeat 10 do
     (format *terminal-io* "~a " (generate-integer 1 6)))

We obtain for example:
6 4 1 6 3 4 3 3 5 4

now let's move toward a proper user interface for a game and enter two more functions:

(defun guess-an-integer (number-of-attempts)
  "Ask for the secret number for a limited number of times."
  (let ((secret-number (generate-integer *minimum-guess-number* *maximum-guess-number*)))
    (unless (loop named try-loop for i from 1 upto number-of-attempts do 
          (format *terminal-io* "try number ~a, insert a number from ~a to ~a: " i *minimum-guess-number* *maximum-guess-number*)
         (finish-output *query-io*) 
          (let ((guess (read *query-io*))) 
            (when (= guess secret-number)
             (format *terminal-io* "Bingo! You did it in ~a attempts.~%" i)
             (return-from try-loop t)))) 
       (format *terminal-io* "I'm sorry you loose!~%")))) 
(defun play-repeatedly ()
  "Play the guessing game repeatedly until user type exit." 
  (let ((number-of-attempts nil)) 
     (loop named guess-game-loop do
      (format *terminal-io* "How many attempts would you like to try? (type 'exit' to exit): ")
     (finish-output *query-io*)
     (setq number-of-attempts (read *query-io*)) 
        ((and (symbolp number-of-attempts)
         (string-equal 'exit number-of-attempts)) 
         (return-from guess-game-loop))
       ((typep number-of-attempts 'integer) 
         (if (< number-of-attempts 1)
          (format *standard-output* "Only positive number or zero to exit!~%")
           (guess-an-integer number-of-attempts)))))))
 Now the important thing to understand is that in Common Lisp programs are made of functions: we defined three of them for our guessing game, GENERATE-INTEGER, GUESS-AN-INTEGER and PLAY-REPEATEDLY. The latter must be evaluated to actually play the game.

Using variables.

In the code above we used variable to hold particular values useful in the program for particular computations. However there is no need to declare the type of object that a variable will hold. The object/value carries information to perform type check during runtime. Assigning a variable a new value changes what object the variable refers to but has no effect on the previously referenced object/values. If a variable refers to a mutable object, that reference could be used to modify the object itself, then this modification will be visible to any code that has a reference to the same mutable object. Practically a variable name refers to a place in memory that actually holds the value/object, when the reference between the two is created it is called a "binding", through it it's possible to operate on a mutable object or value. In our case multiple bindings are created during runtime, for example in the call of GENERATE-INTEGER where parameters a and b are binded to 1 and 6 respectively. In the let special form in PLAY-REPEATEDLY function, the symbol number-of-attempts is binded to nil.

The (read) special form.

The program will ask the player how many attempts he has left to guess the secret number. Having generated this value, the next step is to instruct the computer to accept the player guess. To enter the number of attempts and the attempt itself we used the special form (read). For now you could think at read as a command to make the computer waits until some data is provided by external means, in this case the keyboard. The value entered is then placed in the memory referred by the symbol guess, due to the let form in GUESS-AN-INTEGER. The reader must understand that there are a lot of way to enter a number from outside a program just using the read special form, but enter data from the keyboard is the fastest and most natural way to interact with the user, which is now a player, that runs the program. Generally speaking the read form could take data from streams and perform several interesting operations on them as a plus. For now just play and have fun:


No comments:

Post a Comment