; The room draw problem
; Kathi Fisler

;; First, the data definitions

; a request is (make-request symbol symbol (number number -> symbol))
(define-struct request (student1 student2 choose))

; an asgmt is (make-asgmt symbol symbol symbol)
(define-struct asgmt (student1 student2 dorm))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Now, the function.  This is like any other list problem until you get to filling 
;; in the template.  Let's start with the template.

(define (request-func areq)
  (request-choose areq)
  (request-student1 areq)
  (request-student2 areq) ... )

(define (draw-rooms mor dan alor)
  (cond [(empty? alor) ...]
        [(cons? alor) (request-func (first alor)) ...
                      (draw-rooms (rest alor)) ... ]))

;; Now think about the function.  We clearly need to know which dorm the first request
;; is going to, so introduce a cond to check which dorm is requested:

(define (draw-rooms mor dan alor)
  (cond [(empty? alor) ...]
        [(cons? alor) (cond [(symbol=? 'morgan ????)
                             (request-func (first alor)) ...
                             (draw-rooms (rest alor)) ... ]
                            [else ???])]))

;; How do we figure out which dorm the first request produces?  Recall that the
;; request contains a choose function, so perhaps we should use that:

(define (draw-rooms mor dan alor)
  (cond [(empty? alor) ...]
        [(cons? alor) (cond [(symbol=? 'morgan (request-choose (first alor)))
                             (request-func (first alor)) ...
                             (draw-rooms (rest alor)) ... ]
                            [else ???])]))

;; This isn't right though -- symbol=? needs a SYMBOL and request-choose produces a FUNCTION
;; that _produces_ a symbol.  So we need to call the function in request choose, sending it
;; the current number of rooms in morgan and daniels.  How do we do that?

;; Remember the rule: to call a function, put a paren in front, then put the arguments
;; after the function.

(define (draw-rooms mor dan alor)
  (cond [(empty? alor) ...]
        [(cons? alor) (cond [(symbol=? 'morgan ((request-choose (first alor)) mor dan))
                             (request-func (first alor)) ...
                             (draw-rooms (rest alor)) ... ]
                            [else ???])]))

;; Look at that closely: ((request-choose (first alor)) mor dan) extracts the operator
;; from the request, then calls it with mor and dan.  THE DOUBLE PAREN IS OKAY IN THIS
;; CASE BECAUSE THE EXPRESSION AFTER THE FIRST PAREN EVALUATES TO AN OPERATOR.

;; From here, the function just has to cons the right asgmt to the list, sending
;; updated numbers of rooms to draw-rooms on the recursive calls.  Three versions 
;; follow, any of which would be fine as answers on the exam.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Version 1:
    
; draw-rooms : number number list [request] -> list[asgmt]
; produce dorm assignments given number of rooms in morgan and daniels
(define (draw-rooms mor dan alor)
  (cond [(empty? alor) empty]
        [(cons? alor) (cond [(symbol=? 'morgan ((request-choose (first alor)) mor dan))
                             (cons (make-asgmt (request-student1 (first alor))
                                               (request-student2 (first alor))
                                               'morgan)
                                   (draw-rooms (- mor 1) dan (rest alor)))]
                            [else (cons (make-asgmt (request-student1 (first alor))
                                                    (request-student2 (first alor))
                                                    'daniels)
                                        (draw-rooms mor (- dan 1) (rest alor)))])]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; another version, using local
(define (draw-rooms2 mor dan alor)
  (cond [(empty? alor) empty]
        [(cons? alor) (local [(define dorm ((request-choose (first alor)) mor dan))
                              (define asgmt (make-asgmt (request-student1 (first alor))
                                                        (request-student2 (first alor))
                                                        dorm))]
                        (cond [(symbol=? 'morgan dorm)
                               (cons asgmt (draw-rooms2 (- mor 1) dan (rest alor)))]
                              [else (cons asgmt (draw-rooms2 mor (- dan 1) (rest alor)))]))]))

                    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; a third version using the request-func template to locate the dorm

; get-dorm : request number number -> symbol
; produces the dorm chosen by the given request
(define (get-dorm areq mor dan)
  ((request-choose areq) mor dan))

(define (draw-rooms3 mor dan alor)
  (cond [(empty? alor) empty]
        [(cons? alor) (local [(define dorm (get-dorm (first alor) mor dan))
                              (define asgmt (make-asgmt (request-student1 (first alor))
                                                        (request-student2 (first alor))
                                                        dorm))]
                        (cond [(symbol=? 'morgan dorm)
                               (cons asgmt (draw-rooms3 (- mor 1) dan (rest alor)))]
                              [else (cons asgmt (draw-rooms3 mor (- dan 1) (rest alor)))]))]))

;; An example for testing

(define reqslist (list (make-request 'beavis 'butthead 
                                     (lambda (m d) (cond [(>= d 2) 'daniels]
                                                         [else 'morgan])))))
