summaryrefslogtreecommitdiff
path: root/northwoods/guile.scm
diff options
context:
space:
mode:
Diffstat (limited to 'northwoods/guile.scm')
-rw-r--r--northwoods/guile.scm100
1 files changed, 100 insertions, 0 deletions
diff --git a/northwoods/guile.scm b/northwoods/guile.scm
new file mode 100644
index 0000000..8dea315
--- /dev/null
+++ b/northwoods/guile.scm
@@ -0,0 +1,100 @@
+#|
+Babysitter Kata
+
+Background
+----------
+This kata simulates a babysitter working and getting paid for one night. The rules are pretty straight forward:
+
+The babysitter
+- starts no earlier than 5:00PM
+- leaves no later than 4:00AM
+- gets paid $12/hour from start-time to bedtime
+- gets paid $8/hour from bedtime to midnight
+- gets paid $16/hour from midnight to end of job
+- gets paid for full hours (no fractional hours)
+
+
+Feature:
+As a babysitter
+In order to get paid for 1 night of work
+I want to calculate my nightly charge
+|#
+
+;; doing this in guile scheme because they said i could use any language, and if
+;; i'm gonna be writing code on saturday morning its gonna be lisp :P
+
+(import (only (rnrs base) assert))
+
+(define +max+ 4) ;; can't work longer than 4am
+(define +start+ 5) ;; can't start before 5pm
+
+;; times are represented as a pair of (hours . minutes)
+(define (h time) (car time))
+(define (m time) (cdr time))
+
+;; if hour is > 1 && < +max+, add 12h to account for midnight rollover
+;; (always call this on end time)
+(define (handle-midnight time)
+ (if (and (>= (h time) 1) (<= (h time) +max+))
+ `(,(+ 12 (h time)) . ,(m time))
+ time))
+
+;; round-* functions return just the hour for simplicity, but ideally these
+;; would operate on a <time> object that has + and - methods defined
+
+(define (round-down time) (h time))
+
+(define (round-up time)
+ (if (> (m time) 0)
+ (+ (h time) 1)
+ (h time)))
+
+;; lookup table for pay rates
+(define (payrate s)
+ (case s
+ ((prebed) 12) ;; before baby is asleep
+ ((postbed) 8) ;; after baby is asleep
+ ((postmidnight) 16) ;; from midnight until 4am or end of job
+ (else #f) ;; bad input, blowup
+ ))
+
+;; better way to do this would be with contracts, or parsing, or objects, but
+;; base scheme doesn't have these so whatever
+(define (valid-inputs? start end bedtime)
+ (and (pair? start)
+ (pair? end)
+ (pair? bedtime)
+ (or (>= (h start) +start+)
+ (error "cannot start before 5pm"))
+ (or (<= (h (handle-midnight end)) (h (handle-midnight `(,+max+ . 00))))
+ (error "working hours must be between 5pm-4am (+start+ and +max+)"))))
+
+;; after validating and conforming the data, the core of the program is just
+;; bucket the hours by payrate and sum them up
+(define (charge start end bedtime)
+ (if (valid-inputs? start end bedtime)
+ (let ((hours-prebed (- (round-up bedtime) (round-down start)))
+ (hours-postbed (- 12 (round-up bedtime)))
+ (hours-postmidnight (- (round-up (handle-midnight end)) 12)))
+ (+ (* (payrate 'prebed) hours-prebed)
+ (* (payrate 'postbed) hours-postbed)
+ (* (payrate 'postmidnight) hours-postmidnight)))
+ #f))
+
+;; simple tests
+(define (test)
+ ;; happy path
+ ;; 3*12 + 4*8 + 3*12 = 116
+ (assert (= 116
+ (charge
+ '(5 . 34)
+ '(2 . 45) ;; late night!
+ '(8 . 00))))
+ ;; 12*4 + 8*2 + 16*0 = 64
+ (assert (= 64
+ (charge
+ '(6 . 00)
+ '(11 . 30) ;; this is more my style
+ '(9 . 15))))
+ ;; test bad inputs
+ (assert (equal? #f (charge '(60 . 60) "string" 1234))))