;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-advanced-reader.ss" "lang")((modname week10_w_full) (read-case-sensitive #t) (teachpacks ((lib "image.ss" "teachpack" "2htdp"))) (htdp-settings #(#t constructor repeating-decimal #t #t none #f ((lib "image.ss" "teachpack" "2htdp"))))) ;; a graph is: ;; (make-graph (listof X) (X -> (listof X)) (X X -> boolean)) (define-struct graph (nodes neighbor same-node?)) ; A ; / ; / ; B C ; |\ /| ; | \/ | ; D E F ; \ | / ; \|/ ; G ; (arrows pointing down) (define mygraph (make-graph '(a b c d e f g) (lambda (node) (cond [(symbol=? node 'a) '(b)] [(symbol=? node 'b) '(d e)] [(symbol=? node 'c) '(e f)] [(member node '(d e f)) '(g)] [(symbol=? node 'g) '()])) symbol=?)) ; find-path : graph X X -> list-of-X or false ; returns path between origin and dest if exists, otherwise false (define (find-path g origin dest) (find-path-acc g origin dest empty)) ; find-path-acc : graph (define (find-path-acc g origin dest seen-so-far) (cond [((graph-same-node? g) origin dest) (list dest)] [(already-seen? g origin seen-so-far) false] [else (maybe-cons origin (find-path/list g ((graph-neighbor g) origin) dest (cons origin seen-so-far)))])) (check-expect (find-path mygraph 'a 'd) (list 'a 'b 'd)) (check-expect (find-path mygraph 'a 'c) false) ;; maybe-cons : X (list-of-X or false) -> list-of-X or false ; returns false if second arg false, otherwise list with symbol pre-pended (define (maybe-cons a b) (cond [(boolean? b) b] [else (cons a b)])) ; already-seen? graph symbols list-of-symbols -> boolean ; determines whether node is in seen nodes according ; to graph-same-node function (define (already-seen? g node nodes) (cond [(empty? nodes) false] [else (or ((graph-same-node? g) (first nodes) node) (already-seen? g node (rest nodes)))])) ; find-path/list : graph list-of-X X -> list-of-X or false ; returns a path from any nodes in list to dest or false if none exists ; (cond [(empty? origins) ...] ; [else ... (first origins) .... (fun-for-list-of-symbols (rest origins))] (define (find-path/list graph origins dest seen-so-far) (cond [(empty? origins) false] [else (pick-one (find-path-acc graph (first origins) dest seen-so-far) (find-path/list graph (rest origins) dest seen-so-far))]) ) ;pick-one : (list-of-symbol or false) (list-of-symbol or false) -> list-of-symbol or false (define (pick-one a b) (cond [(boolean? a) b] [else a] )) (define a-graph (make-graph '(x y z) (lambda (x) (cond [(symbol=? x 'x) '(y)] [(symbol=? x 'y) '(x)] [(symbol=? x 'z) '()])) symbol=?)) (check-expect (find-path a-graph 'x 'z) false) (check-expect (find-path a-graph 'x 'y) (list 'x 'y)) ;an m-and-c-group is: ; - number ; - number (define-struct m-and-c-group (missionaries cannibals)) ;an m-and-c is: ; - m-and-c-group ; - m-and-c-group ; - symbol (either 'left or 'right) (define-struct m-and-c (left right boat-loc)) ; m-and-c-complement: number m-and-c-group -> m-and-c-group ; returns the leftover missionaries/cannibals for the given limit and ; group (define (m-and-c-complement limit mc) (make-m-and-c-group (- limit (m-and-c-group-missionaries mc)) (- limit (m-and-c-group-cannibals mc)))) (check-expect (m-and-c-complement 3 (make-m-and-c-group 1 2)) (make-m-and-c-group 2 1)) ; generate-legal-neighbors : m-and-c number -> list-of-m-and-cs ; returns all legal neighbors of the given configuration (define (generate-legal-neighbors mc boatcap sidecap) (discard-illegal-configs (generate-neighbors mc boatcap) sidecap)) (check-expect (generate-legal-neighbors (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'right) 2 6) empty) (check-expect (generate-legal-neighbors (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) 2 6) (list (make-m-and-c (make-m-and-c-group 2 2) (make-m-and-c-group 1 1) 'right) (make-m-and-c (make-m-and-c-group 3 1) (make-m-and-c-group 0 2) 'right) (make-m-and-c (make-m-and-c-group 3 2) (make-m-and-c-group 0 1) 'right))) ; generate-neighbors : m-and-c number -> list-of-m-and-cs ; returns all possible neighbors of the given configuration (define (generate-neighbors mc cap) (local [(define boatloads (generate-boatloads (cond[(symbol=? (m-and-c-boat-loc mc) 'left) (m-and-c-left mc)] [else (m-and-c-right mc)]) cap))] (cond[(symbol=? (m-and-c-boat-loc mc) 'left) (map (lambda (x) (make-m-and-c (subtract-group (m-and-c-left mc) x) (add-group (m-and-c-right mc) x) 'right)) boatloads)] [else (map (lambda (x) (make-m-and-c (add-group (m-and-c-left mc) x) (subtract-group (m-and-c-right mc) x) 'left)) boatloads)]))) (check-expect (generate-neighbors (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'right) 2) empty) (check-expect (generate-neighbors (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) 2) (list (make-m-and-c (make-m-and-c-group 1 3) (make-m-and-c-group 2 0) 'right) (make-m-and-c (make-m-and-c-group 2 2) (make-m-and-c-group 1 1) 'right) (make-m-and-c (make-m-and-c-group 2 3) (make-m-and-c-group 1 0) 'right) (make-m-and-c (make-m-and-c-group 3 1) (make-m-and-c-group 0 2) 'right) (make-m-and-c (make-m-and-c-group 3 2) (make-m-and-c-group 0 1) 'right))) ; generate-boatloads : m-and-c-group number -> list-of-m-and-c-groups ; returns all possible boatloads from m-and-c-group (define (generate-boatloads mc cap) (generate-m-and-c-groups cap 1 (m-and-c-group-missionaries mc) (m-and-c-group-cannibals mc))) (check-expect (generate-boatloads (make-m-and-c-group 1 1) 1) (list (make-m-and-c-group 1 0) (make-m-and-c-group 0 1))) ; subtract-group : m-and-c-group m-and-c-group -> m-and-c-group ; returns the result of subtracting people (define (subtract-group g1 g2) (make-m-and-c-group (- (m-and-c-group-missionaries g1) (m-and-c-group-missionaries g2)) (- (m-and-c-group-cannibals g1) (m-and-c-group-cannibals g2)))) (check-expect (subtract-group (make-m-and-c-group 3 2) (make-m-and-c-group 2 1)) (make-m-and-c-group 1 1)) ; add-group : m-and-c-group m-and-c-group -> m-and-c-group ; returns the result of adding people (define (add-group g1 g2) (make-m-and-c-group (+ (m-and-c-group-missionaries g1) (m-and-c-group-missionaries g2)) (+ (m-and-c-group-cannibals g1) (m-and-c-group-cannibals g2)))) (check-expect (add-group (make-m-and-c-group 3 2) (make-m-and-c-group 2 1)) (make-m-and-c-group 5 3)) ; generate-configs : number -> list-of-m-and-c ; returns all legal configurations for n=# missionaries (also # cannibals) (define (generate-configs n) (local [(define groups (generate-m-and-c-groups (* n 2) 0 n n))] (append (map (lambda (x) (make-m-and-c x (m-and-c-complement n x) 'left)) groups) (map (lambda (x) (make-m-and-c x (m-and-c-complement n x) 'right)) groups)))) ; discard-illegal-mcs : list-of-m-and-c -> list-of-m-and-c ; removes illegal configurations (define (discard-illegal-configs lomc capacity) (filter (lambda (x) (and (legal-group? (m-and-c-left x) capacity 0) (legal-group? (m-and-c-right x) capacity 0))) lomc)) (check-expect (discard-illegal-configs (list (make-m-and-c (make-m-and-c-group 2 0) (make-m-and-c-group 0 2) 'left)) 10) (list (make-m-and-c (make-m-and-c-group 2 0) (make-m-and-c-group 0 2) 'left))) (check-expect (discard-illegal-configs (list (make-m-and-c (make-m-and-c-group 1 2) (make-m-and-c-group 0 2) 'left)) 10) empty) ; generate-m-and-c-groups : number number number number -> list-of-m-and-c-group ; returns all legal groups of missionaries and cannibals within given limits (define (generate-m-and-c-groups capacity lowerbound mlimit climit) (discard-illegal-groups (map (lambda (x) (make-m-and-c-group (first x) (second x))) (all-pairs mlimit climit)) capacity lowerbound) ) (check-expect (generate-m-and-c-groups 2 0 2 2) (list (make-m-and-c-group 2 0) (make-m-and-c-group 1 1) (make-m-and-c-group 1 0) (make-m-and-c-group 0 2) (make-m-and-c-group 0 1) (make-m-and-c-group 0 0))) ; discard-illegal-groups : list-of-m-and-c-group number -> list-of-m-and-c-group ; removes groups either exceeding capacity or having missionaries in danger (define (discard-illegal-groups lomc capacity lowerbound) (filter (lambda (x) (legal-group? x capacity lowerbound)) lomc) ) (check-expect (discard-illegal-groups (list (make-m-and-c-group 2 3) (make-m-and-c-group 3 3) (make-m-and-c-group 2 2)) 5 0) (list (make-m-and-c-group 2 2))) ; legal-group? : list-of-m-and-c-group number -> list-of-m-and-c-group ; removes groups either exceeding capacity or having missionaries in danger (define (legal-group? mc limit lowerbound) (and (or (= (m-and-c-group-missionaries mc) 0) (<= (m-and-c-group-cannibals mc) (m-and-c-group-missionaries mc) limit)) (<= lowerbound (+ (m-and-c-group-cannibals mc) (m-and-c-group-missionaries mc)) limit) )) (check-expect (legal-group? (make-m-and-c-group 2 3) 6 0) false) (check-expect (legal-group? (make-m-and-c-group 2 2) 3 0) false) (check-expect (legal-group? (make-m-and-c-group 2 2) 4 0) true) (check-expect (legal-group? (make-m-and-c-group 0 2) 4 0) true) (check-expect (legal-group? (make-m-and-c-group 0 0) 4 1) false) ; all-pairs : number number -> list-of-numbers ; generates all pairs of numbers (x, y), x ranging from 0 to a and y ranging from 0 to b (define (all-pairs a b) (local [(define 0-to-a (n-to-zero a)) (define 0-to-b (n-to-zero b))] (cross 0-to-a 0-to-b))) (check-expect (all-pairs 1 2) (list (list 1 2) (list 1 1) (list 1 0) (list 0 2) (list 0 1) (list 0 0))) ; zero-to-n : number -> list-of-numbers (define (n-to-zero n) (cond [(= n 0) (list 0)] [else (cons n (n-to-zero (- n 1)))])) (check-expect (n-to-zero 4) (list 4 3 2 1 0)) (define (cross l1 l2) (cond [(empty? l1) empty] [else (append (map (lambda (x) (list (first l1) x)) l2) (cross (rest l1) l2))])) (check-expect (cross (list 1 2) (list 3 4)) (list (list 1 3) (list 1 4) (list 2 3) (list 2 4))) (generate-neighbors (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) 2) ; m-and-c-equal : m-and-c m-and-c -> boolean ; returns true if m-and-cs are equal (define (m-and-c-equal mc1 mc2) (and (m-and-c-group-equal (m-and-c-left mc1) (m-and-c-left mc2)) (m-and-c-group-equal (m-and-c-right mc1) (m-and-c-right mc2)) (symbol=? (m-and-c-boat-loc mc1) (m-and-c-boat-loc mc2)))) (check-expect (m-and-c-equal (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) ) true) (check-expect (m-and-c-equal (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) (make-m-and-c (make-m-and-c-group 3 2) (make-m-and-c-group 0 1) 'left) ) false) (check-expect (m-and-c-equal (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'right) (make-m-and-c (make-m-and-c-group 3 3) (make-m-and-c-group 0 0) 'left) ) false) ; m-and-c-group-equal : m-and-c-group m-and-c-group -> boolean ; returns true if m-and-c-groups are equal (define (m-and-c-group-equal mcg1 mcg2) (and (= (m-and-c-group-missionaries mcg1) (m-and-c-group-missionaries mcg2)) (= (m-and-c-group-cannibals mcg1) (m-and-c-group-cannibals mcg2)))) (check-expect (m-and-c-group-equal (make-m-and-c-group 3 3) (make-m-and-c-group 3 0)) false) (check-expect (m-and-c-group-equal (make-m-and-c-group 3 3) (make-m-and-c-group 3 3)) true) (define MC 5) (define BOATSIZE 3) (define m-and-c-graph (make-graph (generate-configs MC) (lambda (x) (generate-legal-neighbors x BOATSIZE (* MC 2))) m-and-c-equal)) (define START (make-m-and-c (make-m-and-c-group MC MC) (make-m-and-c-group 0 0) 'left)) (define END (make-m-and-c (make-m-and-c-group 0 0) (make-m-and-c-group MC MC) 'right)) (find-path m-and-c-graph START END)