(define (build-or-buy qty item-id #!optional (buy-type 'asking) (unbuyable-cost 0)) (let* ( (item-craftable (craftable? item-id)) (item-buyable (query-price item-id)) (item-name (query-item-name item-id)) (purchase-price (* qty (cassoc buy-type item-buyable 0))) (outcome (list (cons item-id qty))) (msgfmt (lambda (msg-type msg-qty msg-name msg-bought msg-craft msg-buy) (list (fmt #f " | " (fit 10 msg-type) " | " (fit/left 10 msg-qty) " | " (fit 30 msg-name) " | " (fit/left 10 msg-bought) " | " (fit/left 10 msg-craft) " | " (fit/left 10 msg-buy) " | " " | " nl)))) (purchase-item (lambda (msg) (if item-buyable ;; buyable, return bought `( (spent . ,purchase-price) (inv . ,outcome) (bought . ,outcome) (unbuyable . ()) (messages . ,(msgfmt "Buy" qty item-name purchase-price "" msg))) ;; not buyable, return it as such `( (spent . ,unbuyable-cost) (inv . ,outcome) (bought . ()) (unbuyable . ,outcome) (messages . ,(msgfmt "Quest" qty item-name "" msg ""))) )))) (if (not item-craftable) ;; buy it (purchase-item (fit/left 30 "")) ;; craft it (let* ( (recipe (query-recipe-by-item item-id)) (batches (ceiling (/ qty (cassoc 'output_item_count recipe)))) (parts (map (lambda (x) (cons (car x) (* batches (cdr x)))) (query-parts-by-item item-id))) (result (map (lambda (x) (build-or-buy (cdr x) (car x) buy-type)) parts)) (crafting-cost (fold + 0 (map (lambda (x) (cassoc 'spent x 0)) result))) (map-result (lambda (k) (map (lambda (x) (cassoc k x '())) result))) ) (if (and (> crafting-cost purchase-price) item-buyable) ;; buy it (purchase-item (fit/left 10 crafting-cost)) ;; craft it (list (cons 'spent crafting-cost) ; sum of spent amount (cons 'bought (fold merge-inventories '() (map-result 'bought))) (cons 'unbuyable (fold merge-inventories '() (map-result 'unbuyable))) ;; must sum together all the bought parts into inventory, and then remove items consumed by the recipe (cons 'inv (fold merge-inventories '() (list (negate-inventory parts) ; the negated recipe, parts is already multiplied by batches (list (cons item-id (* batches (cassoc 'output_item_count recipe 0)))) ; the crafting result (fold merge-inventories '() (map-result 'inv)) ))) ; the recursion results (cons 'crafted (fold merge-inventories (list (cons item-id (* batches (cassoc 'output_item_count recipe 0)))) ; the crafting result (map-result 'crafted))) (cons 'messages (fold append (msgfmt "Craft" batches item-name "" crafting-cost purchase-price) (map-result 'messages))) )))))) #;140> (pretty-print-build (build-or-buy 1 13459)) Spent: 36739 Result: - [ ] 1 (13459) Ruby Orichalcum Earrin Bought: - [ ] 28 (19701) Orichalcum Ore - [ ] 5 (19721) Glob of Ectoplasm - [ ] 25 (24276) Pile of Incandescent D - [ ] 10 (24474) Ruby Crystal Quest: Recipes to craft: - [ ] 14 (19685) Orichalcum Ingot - [ ] 1 (12815) Orichalcum Setting - [ ] 1 (12809) Orichalcum Hook - [ ] 5 (12834) Orichalcum Filigree - [ ] 5 (24508) Ruby Orb - [ ] 5 (24498) Exquisite Ruby Jewel - [ ] 1 (13459) Ruby Orichalcum Earrin Messages: |------------+------------+--------------------------------+------------+------------+------------+--| | Buy | 4 | Orichalcum Ore | 1132 | | | | | Craft | 2 | Orichalcum Ingot | | 1132 | 1142 | | | Craft | 1 | Orichalcum Setting | | 1132 | 1599 | | | Buy | 4 | Orichalcum Ore | 1132 | | | | | Craft | 2 | Orichalcum Ingot | | 1132 | 1142 | | | Craft | 1 | Orichalcum Hook | | 1132 | 1417 | | | Buy | 5 | Glob of Ectoplasm | 17610 | | | | | Buy | 20 | Orichalcum Ore | 5660 | | | | | Craft | 10 | Orichalcum Ingot | | 5660 | 5710 | | | Craft | 5 | Orichalcum Filigree | | 5660 | 9470 | | | Buy | 25 | Pile of Incandescent Dust | 7975 | | | | | Buy | 10 | Ruby Crystal | 3230 | | 19160 | | | Craft | 5 | Ruby Orb | | 11205 | 11500 | | | Craft | 5 | Exquisite Ruby Jewel | | 34475 | 36575 | | | Craft | 1 | Ruby Orichalcum Earring | | 36739 | 40805 | | |------------+------------+--------------------------------+------------+------------+------------+--| | | | | | | | |