;;; Control program for students to use on Project 3 ;;; -- Dana Nau, Friday, April 23, 2010 ;;; Updated April 28 to call start-game with 3 args: boundary, finish-line, and time-limit. ;;; See the discussion forum for an explanation why. ;;; P3-CONTROL will run your code on a racetrack. Args are as follows: ;;; - STATE0 is the starting state for the racecar. ;;; - BOUNDARY is the boundary of the racetrack. ;;; - FINISH-LINE is the racetrack's finish line. ;;; - TIME-LIMIT is a positive number telling how much time per move. ;;; P3-CONTROL requires the following functions from your code: ;;; - START-GAME to start a new game; ;;; - NEW-VELOCITY to get the racecar's next velocity; ;;; - CRASHP to detect whether the racecar has crashed; ;;; - INTERSECTP to determine whether the racecar has crossed the finish line. ;;; While it's running, P3-CONTROL will print out a move-by-move summary of what the ;;; racecar is doing. When the game ends, P3-CONTROL will return a list of three elements: ;;; - the atom FAILURE if your racecar crashed, or SUCCESS if it reached the finish line; ;;; - the number of moves your racecar made; ;;; - your racecar's path (sequence of states). ;;; Instead of doing the entire program inline, it might have been better programming style to ;;; have some helper functions. But I decided not to have any helper functions, in order to ;;; avoid any possible name conflicts with helper functions in your racecar code. The "right" ;;; way to avoid name conflicts is to use a separate package (a package is the Lisp version of ;;; a namespace), but I wanted to avoid the extra complications that would cause. (defun p3-control (state0 boundary finish-line time-limit) ;; BLOCK gives a name to the code, so that we can use RETURN-FROM later (block game (let ((location (first state0)) ; the current location (velocity (second state0)) ; the current velocity new-velocity ; velocity returned by NEXT-VELOCITY new-location ; location to move to edge ; line segment from old location to new location time-called ; time before calling NEXT-VELOCITY time-elapsed ; time used by NEXT-VELOCITY moves-this-time ; computed from TIME-LIMIT and NEXT-VELOCITY's runtime (status 'continue) ; SUCCESS, FAILURE, or CONTINUE path-reverse-order) ; sequence of states, from newest to oldest ;; tell the racecar code that a game is starting (setq time-called (get-internal-run-time)) (start-game boundary finish-line time-limit) (setq time-elapsed (- (get-internal-run-time) time-called)) ;; convert elapsed time to milliseconds (setq time-elapsed (* 1000 (/ time-elapsed internal-time-units-per-second))) (setq moves-this-time (- (max 1 (ceiling (/ time-elapsed time-limit))) 1)) (if (> moves-this-time 1) (format t "~%START-GAME took ~s ms => ~s moves at velocity (0,0)" time-elapsed moves-this-time)) (dotimes (i moves-this-time) (push (list location '(0 0)) path-reverse-order)) (push state0 path-reverse-order) (loop ; keep doing moves until the game ends ;; call NEXT-VELOCITY, see how long it takes (setq time-called (get-internal-run-time)) (setq new-velocity (next-velocity (list location velocity) time-limit)) (setq time-elapsed (- (get-internal-run-time) time-called)) ;; convert elapsed time to milliseconds (setq time-elapsed (* 1000 (/ time-elapsed internal-time-units-per-second))) (setq moves-this-time (max 1 (ceiling (/ time-elapsed time-limit)))) (format t "~%NEW-VELOCITY took ~s ms =>" time-elapsed) (if (> moves-this-time 1) (format t " ~s at velocity ~s, 1 at velocity ~s" (- moves-this-time 1) velocity new-velocity) (format t " 1 at velocity ~s" new-velocity)) (loop for i from 1 to moves-this-time do ;; the last move is at the new velocity, the others are at the old velocity (if (= i moves-this-time) (setq velocity new-velocity)) ;; the new location is the vector sum of the old location and the velocity (setq new-location (list (+ (first location) (first velocity)) (+ (second location) (second velocity)))) (format t "~%move from ~s to ~s" location new-location) (setq edge (list location new-location)) (setq location new-location) (push (list location velocity) path-reverse-order) ;; check to see if the racecar has crashed or finished (cond ((crashp edge boundary) (format t "~%car crashed") (setq status 'failure)) ((intersectp edge finish-line) (format t "~%car reached finish line") (setq status 'success))) (if (not (eql status 'continue)) (return-from game (list status ;; path length = number of states = number of moves + 1 (- (length path-reverse-order) 1) (reverse path-reverse-order)))))))))