You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
4.1 KiB
Racket
115 lines
4.1 KiB
Racket
#lang typed/racket
|
|
|
|
(define-type Item (List Symbol Natural Natural Natural))
|
|
|
|
(define-type HP Integer)
|
|
(define-type Attack-Power Natural)
|
|
(define-type Defense-Power Natural)
|
|
(define-type Player (List HP Attack-Power Defense-Power))
|
|
|
|
(define no-item : Item
|
|
'(None 0 0 0))
|
|
|
|
(define weapons : (Listof Item)
|
|
'((Dagger 8 4 0)
|
|
(Shortsword 10 5 0)
|
|
(Warhammer 25 6 0)
|
|
(Longsword 40 7 0)
|
|
(Greataxe 74 8 0)))
|
|
|
|
(define armors : (Listof Item)
|
|
'((Leather 13 0 1)
|
|
(Chainmail 31 0 2)
|
|
(Splintmail 53 0 3)
|
|
(Bandedmail 75 0 4)
|
|
(Platemail 102 0 5)))
|
|
|
|
(define rings : (Listof Item)
|
|
'((Damage+1 25 1 0)
|
|
(Damage+2 50 2 0)
|
|
(Damage+3 100 3 0)
|
|
(Defense+1 20 0 1)
|
|
(Defense+2 40 0 2)
|
|
(Defense+3 80 0 3)))
|
|
|
|
(require typed/rackunit)
|
|
(provide (all-defined-out))
|
|
|
|
(: cost (-> (Listof Item) Natural))
|
|
(define (cost equipment-set)
|
|
(apply + (map (inst second Any Natural Any) equipment-set)))
|
|
|
|
(: equipment-sets-by-cost (Listof (Listof Item)))
|
|
(define equipment-sets-by-cost
|
|
(let ([equipment-sets
|
|
(for*/list ([weapon (in-list weapons)]
|
|
[armor (in-list (cons no-item armors))]
|
|
[lh-ring (in-list (cons no-item rings))]
|
|
[rh-ring (in-list (cons no-item (remove lh-ring rings)))])
|
|
: (Listof (List Item Item Item Item))
|
|
(list weapon armor lh-ring rh-ring))])
|
|
((inst sort (Listof Item) Natural) equipment-sets < #:key cost)))
|
|
|
|
(define player-hit-points 100)
|
|
(define min-damage 1)
|
|
|
|
(: equipment-set->player (-> (Listof Item) Player))
|
|
(define (equipment-set->player equipment-set)
|
|
(let ([total-damage (apply + (map (inst third Any Any Natural Any) equipment-set))]
|
|
[total-armor (apply + (map (inst fourth Any Any Any Natural Any) equipment-set))])
|
|
(list player-hit-points total-damage total-armor)))
|
|
(: player-turn? (-> Integer Boolean))
|
|
(define player-turn? even?)
|
|
(: hit-points (-> Player HP))
|
|
(define hit-points first)
|
|
(: damage (-> Player Attack-Power))
|
|
(define damage second)
|
|
(: armor (-> Player Defense-Power))
|
|
(define armor third)
|
|
|
|
(: attack (-> Player Player Player))
|
|
(define (attack attacker defender)
|
|
(define net-damage (max (- (damage attacker) (armor defender)) min-damage))
|
|
(list (- (hit-points defender) net-damage) (damage defender) (armor defender)))
|
|
|
|
(: we-win? (-> Player Player Boolean))
|
|
(define (we-win? player boss)
|
|
(define-values (last-player-state last-boss-state)
|
|
(let loop : (Values Player Player)
|
|
;;bg;(for/fold : (Values Player Player)
|
|
([player-state : Player player][boss-state : Player boss]
|
|
[turn-number : Natural 0]
|
|
;;bg; #:break
|
|
)
|
|
(cond
|
|
[(<= (min (hit-points player-state) (hit-points boss-state)) 0)
|
|
(values player-state boss-state)]
|
|
[(player-turn? turn-number)
|
|
(loop player-state (player-state . attack . boss-state) (+ 1 turn-number))]
|
|
[else
|
|
(loop (boss-state . attack . player-state) boss-state (+ 1 turn-number))])))
|
|
(<= (hit-points last-boss-state) 0))
|
|
|
|
(: q1 (-> String (U #f Integer)))
|
|
(define (q1 input-str)
|
|
(define boss (cast (filter number? (map string->number (string-split input-str))) Player))
|
|
(for/or : (U #f Integer) ([equip (in-list equipment-sets-by-cost)]
|
|
#:when (let ([player (equipment-set->player equip)])
|
|
(we-win? player boss)))
|
|
(cost equip)))
|
|
|
|
(: q2 (-> String (U #f Integer)))
|
|
(define (q2 input-str)
|
|
(define boss (cast (filter number? (map string->number (string-split input-str))) Player))
|
|
(for/or : (U #f Integer) ([equip (in-list (reverse equipment-sets-by-cost))]
|
|
#:when (let ([player (equipment-set->player equip)])
|
|
(not (we-win? player boss))))
|
|
(cost equip)))
|
|
|
|
(module+ test
|
|
(define input-str (file->string "../day21-input.txt"))
|
|
(check-equal? (q1 input-str) 111)
|
|
(check-equal? (q2 input-str) 188))
|
|
|
|
|