Committer: hpcdisrespecter <noreply@git.asperger.pro> modified: .gitignore modified: README.md new file: menus/peoples-organic.sexp new file: menus/townhall-shortnorth.sexp new file: organic-cafe.asd new file: run.lisp new file: src/package.lisp new file: src/planner.lisp new file: test/tests.lisp
121 lines
3.7 KiB
Markdown
121 lines
3.7 KiB
Markdown
# organicCafe
|
|
|
|
~~Practical joke~~ ~~vent art~~ SERIOUS DISCUSSION code for generating multi-day,
|
|
budget-constrained meal plans from a menu using MC sampling.
|
|
|
|
- Never make a decision about food at a technical conference again!
|
|
- Make wait staff feel awkward by ordering as soon as you arrive!
|
|
- Avoid the temptation to eat the same thing every day!
|
|
|
|
The menu and meal templates are a small, data-only S-expression DSL.
|
|
|
|
Shoutout to People's Organic in MSP (Federal per diem: $92, FY2026) and
|
|
TownHall Short North in Columbus ($80, FY2025), the respective off-site ordering adventures
|
|
for ACM PEARC26 and PEARC25.
|
|
|
|
People's Organic has a small menu and fewer options, whereas TownHall produces
|
|
a Cartesian product of garnishes. Both are supported in this framework.
|
|
|
|
## General Ideas
|
|
|
|
Prices are converted to integer cents when loaded, so budget comparisons do
|
|
not rely on floating-point arithmetic.
|
|
|
|
For normal generation, the planner computes the weighted number of completions
|
|
at each remaining budget and samples only branches that can finish beneath the
|
|
cap. This avoids both rejection loops and materializing large Cartesian products.
|
|
|
|
Any given day, a small pool of feasible days is drawn, and least-repetitive
|
|
candidates are favored.
|
|
|
|
`feasible-candidates` still eagerly enumerates combinations for tests,
|
|
diagnostics, and suitably small menus such as People's Organic.
|
|
|
|
## Variables
|
|
|
|
- `--budget DOLLARS` sets the daily per diem.
|
|
- `--days N` sets the number of days to plan.
|
|
- `--menu FILE` selects a menu file from `menus/`.
|
|
- `--plan PLAN` selects a plan from the menu. If omitted, the first plan is used.
|
|
- `--add-lazy-day DOLLARS` appends a breakfast-only day with its own budget. For outbound flight days.
|
|
- `--seed N` sets the random seed for reproducibility (don't use this you orthorexic nerd).
|
|
|
|
## Usage
|
|
|
|
SBCL and ASDF are the only requirements.
|
|
|
|
```sh
|
|
# PEARC26 default
|
|
sbcl --script run.lisp
|
|
# the same thing explicitly with seed
|
|
sbcl --script run.lisp -- --budget 92 --days 3 --seed 1337 --add-lazy-day 30
|
|
# PEARC25 default
|
|
sbcl --script run.lisp -- \
|
|
--menu menus/townhall-shortnorth.sexp \
|
|
--budget 80 --add-lazy-day 30 --seed 1337
|
|
```
|
|
|
|
Use `--help` for all options.
|
|
|
|
Ask for help if you need it. Someone will probably answer.
|
|
|
|
## Tests
|
|
|
|
Run the tests:
|
|
|
|
```sh
|
|
sbcl --non-interactive \
|
|
--eval '(require "asdf")' \
|
|
--eval '(asdf:load-asd (truename "organic-cafe.asd"))' \
|
|
--eval '(asdf:test-system "organic-cafe/tests")'
|
|
```
|
|
|
|
## The DSL
|
|
|
|
The complete menus live in `menus/` and defaults to `menus/peoples-organic.sexp`. The DSL is a small S-expression language for describing menus and meal plans.
|
|
|
|
`:lazy-day` is a special plan item, for the day you leave town.
|
|
|
|
### Basic example
|
|
|
|
```lisp
|
|
(:menu "A Cafe"
|
|
(:group :mains
|
|
(:item "Soup and sandwich" 12.50)
|
|
(:item "Grain bowl" 14))
|
|
(:group :drinks
|
|
(:item "Coffee" 3.25))
|
|
|
|
(:plan :lunch
|
|
(:meal :lunch
|
|
(:one :main :mains)
|
|
(:maybe :drink :drinks :chance 1/2))))
|
|
```
|
|
|
|
`(:one LABEL GROUP)` always chooses one item. `(:maybe LABEL GROUP :chance P)`
|
|
chooses no item with probability `1-P`; if present, each item in the group has
|
|
an equal share of `P`.
|
|
|
|
### Composable forms
|
|
|
|
TownHall's menu has options, so it demonstrates compositional forms:
|
|
|
|
```lisp
|
|
(:choose :side :bowl-side :count 2)
|
|
|
|
(:either :meal
|
|
(:branch 1/2
|
|
(:one :shared-plate :pour-la-table))
|
|
(:branch 1/2
|
|
(:one :protein :bowl-protein)
|
|
(:one :base :bowl-base)))
|
|
```
|
|
|
|
`:choose` samples the requested number of distinct items. `:either` selects one
|
|
weighted branch, whose body may contain any number of other plan forms. Branch
|
|
weights must sum to one. `:one` and `:maybe` are also valid. You are also valid.
|
|
|
|
### License and Sharing
|
|
|
|
Do what the fuck you want.
|