; Take the stick!
;
; by Alexander Griesser <tuxx@aon.at>
;    Christoph Pittracher <pitt@gmx.at>
;    Manfred Steinkellner <manfred.steinkellner@gmx.net>
;
; <http://www.tuxx-home.at/lisp/>


; Read a number from stdin and check, if it is in a valid
; range (1 2 3), (1 2) or even just (1) (depends on n_sticks)
(defun input(n_sticks)
  ; Determine the number of items for the valid_input list
  (setq n_vi (if (>= (- 3 n_sticks) 0) (- 3 n_sticks) 0))

  ; If necessary, remove some possibilities from this list
  (setq valid_input (nthcdr n_vi '(3 2 1)))

  ; Prompt the user for an input, as long as he enters
  ; invalid values
  (format t "You take? ~A " valid_input)
  (do ((inp (read) (read)))
      ; check if the user-input is a member of our valid-input list
      ((member inp valid_input) (return inp))
      (format t "invalid input, you take? ~A " valid_input)
  )
)

(defun splash_screen()
  (format t "   _______~
           ~%  / take /~
           ~%/ stick/~%")
  
)

(defun getRandom(range)
  (return-from getRandom (+ (mod (- (random (get-universal-time)) (+ (random (round (/ (get-universal-time) (get-internal-run-time)))) (random (get-internal-real-time)))) (+ (random range) 1)) 1))
)

; The CPU's brain ;)
; (this function returns the remaining no. of sticks)
(defun cpu_take_sticks(n_sticks)
  ; Determine the number of items for the valid_input list
  (setq n_vi (if (>= (- 3 n_sticks) 0) (- 3 n_sticks) 0))

  ; If necessary, remove some possibilities from this list
  (setq valid_input (nthcdr n_vi '(3 2 1)))

  ; replace random movement through state of the art intelligence :)
  ; random movement
  ; (setq tmp (+ (mod (random 10000) (+ (random (list-length valid_input)) 1)) 1))
  (setq keyValues '(1 5 9 13 17 21))

  ; if the current number of elements left is one of the key values the computer 
  ; should take only one element, thus the chance is highest to get on one of 
  ; the key elements in the next move
  (setq tmp 0)
  (if (member n_sticks keyValues) (setq tmp (getRandom (list-length valid_input))))

  ; check the cases between one and three sticks distance from a 
  (if (equal tmp 0) 
  (setq tmp (loop as i from 1 to 3
      do (if (member (- n_sticks i) keyValues) (return i)))
  ))
  (loop as i from 1 to 10
      do (format t ".")
         (sleep 0.1))
  ; formatted output text
  (format t "CPU takes ~D stick~:P~%" tmp)
  (return-from cpu_take_sticks (- n_sticks tmp))
)

; The player's interface
; (this function returns the remaining no. of sticks)
(defun p1_take_sticks(n_sticks)
  (setq tmp (input n_sticks))
  (format t "Player takes ~R stick~:P~%" tmp)
  (return-from p1_take_sticks (- n_sticks tmp))
)

; Display some information about this programm
(splash_screen)

; determine the winner
(setq winner (do ((n_sticks 23) (run 1 (+ run 1))) 
    ((<= n_sticks 0) (return (mod run 2)))
    (setq n_sticks (if (eql (mod run 2) 0) (cpu_take_sticks n_sticks) (p1_take_sticks n_sticks)))
    (format t "~R stick~:P left~%~%" n_sticks)
))

; print the name of the winner (computer/player) dep. on "winner"
(format t "~[Computer~;Player~] has won!~%" winner)


syntax highlighted by Code2HTML, v. 0.9