├── .gitattributes ├── .gitignore ├── README.md ├── facts ├── auto-begets ├── auto-unbuyable ├── bar ├── begets ├── combinations ├── recipes └── unbuyable ├── generate-auto-facts ├── list-ingredients ├── mixologician.dl ├── parse ├── results └── .gitignore ├── shell.nix └── test.t /.gitattributes: -------------------------------------------------------------------------------- 1 | # i know but github doesn't have datalog and this gives the best syntax 2 | # highlighting 3 | *.dl linguist-language=Prolog 4 | 5 | # no they aren't really i just don't github to say that "shell" is the most 6 | # used language 7 | *.t linguist-documentation 8 | list-ingredients linguist-documentation 9 | generate-auto-facts linguist-documentation 10 | parse linguist-documentation 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tuxedono2/ 2 | mixable 3 | shopping-list 4 | *.err 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mixologician 2 | 3 | This is the code accompanying a blog post I wrote called [Drinking with Datalog](https://ianthehenry.com/posts/drinking-with-datalog/). 4 | 5 | # show me 6 | 7 | Given the contents of my bar: 8 | 9 | ```shell-session 10 | $ cat facts/bar 11 | brandy 12 | dry vermouth 13 | lemon 14 | light rum 15 | lime 16 | orange liqueur 17 | reposado tequila 18 | sugar 19 | ``` 20 | 21 | And my cocktail recipe book: 22 | 23 | ```shell-session 24 | $ head facts/recipes 25 | martini <- london dry gin 26 | martini <- dry vermouth 27 | daiquiri <- light rum 28 | daiquiri <- lime juice 29 | daiquiri <- simple syrup 30 | margarita <- blanco tequila or reposado tequila 31 | margarita <- lime juice 32 | margarita <- orange liqueur 33 | margarita <- lime wedge 34 | ... 35 | ``` 36 | 37 | What cocktails can I mix? 38 | 39 | ```shell-session 40 | $ cat results/mixable 41 | daiquiri 42 | margarita 43 | sidecar 44 | ``` 45 | 46 | Gosh, that's not very many. What could I add to my bar to expand my options? 47 | 48 | ```shell-session 49 | $ cat results/shopping-list 50 | london dry gin -> gimlet 51 | london dry gin -> martini 52 | champagne -> airmail 53 | cognac -> between-the-sheets 54 | sherry -> sherry-cobbler 55 | rhum agricole -> ti-punch 56 | ``` 57 | 58 | Well that'll make things a bit more interesting. 59 | 60 | # this seems dumb 61 | 62 | No you're wrong it's smart. Take a closer look at that daiquiri: the recipe calls for lime juice, but we don't have lime juice in our bar. We only have lime. So why does it show up as mixable? 63 | 64 | Well, because it knows some rules. It knows that limes make lime juice (and lime wedge, and lime zest...). It even knows that lime zest plus sugar makes lime cordial -- so it knows that we're only one ingredient away from being able to make a [Gimlet](https://www.tuxedono2.com/gimlet-cocktail-recipe), even though we don't *technically* have a single ingredient that the drink calls for. 65 | 66 | # cool 67 | 68 | It *is* cool. And you can read all about how it works, right here: [Drinking with Datalog](https://ianthehenry.com/posts/drinking-with-datalog/). 69 | 70 | # dependencies 71 | 72 | - [Install Nix](https://nixos.org/guides/install-nix.html) 73 | - Run `nix-shell` 74 | 75 | *Or:* 76 | 77 | - [Install Soufflé](https://souffle-lang.github.io/install) 78 | 79 | And if you want to run the tests: 80 | 81 | - [Install Python](https://www.python.org/downloads/) 82 | - [Install Cram](https://pypi.org/project/cram/) 83 | 84 | # let me try 85 | 86 | From within your `nix-shell`: 87 | 88 | $ souffle mixologician.dl -F facts -D results 89 | 90 | That will create three files: `results/mixable`, `results/mixable-recipes`, and `results/shopping-list`. `mixable` basically exists for tests and demos; `mixable-recipes` is a much more useful output. `shopping-list` will tell you all the ingredients you can buy that will allow you to make new drinks. 91 | 92 | `shopping-list` only looks for recipes that you are *one ingredient* away from being able to make. So you might not get results, if you have a very sparse (or very comprehensive) bar. You might need to buy two new things to be able to make a new drink, and it won't tell you that. I think it would be *possible* to modify it to handle multiple missing ingredients, but I didn't try to do that. 93 | 94 | # what else can i do 95 | 96 | Customize the contents of your bar by editing `facts/bar`. 97 | 98 | Customize your recipe book by editing [`facts/recipes`](facts/recipes). If you change recipes, you should probably re-run [`./generate-auto-facts`](generate-auto-facts). It's not strictly necessary, but it will make your output better. 99 | 100 | `./generate-auto-facts` will overwrite the `facts/auto-*` files, so you shouldn't make changes to those. 101 | 102 | You can also change the generation rules in [`facts/begets`](facts/begets), or filter things out of your `shopping-list` by adding them to [`facts/unbuyable`](facts/unbuyable). You can add new two-ingredient combinations by editing [`facts/combinations`](facts/combinations). If you have a combination that requires more than two ingredients, it wouldn't be hard to extend the logic with a new relation. 103 | 104 | # it seems hard to make a full inventory of my giant bar 105 | 106 | Yeah. I recommend starting with the universe of all possible ingredients, by running [`./list-ingredients`](list-ingredients), and just removing anything that you don't have. It's easier than typing everything up, and you can be sure that you're using the same names for things that the recipes use. 107 | 108 | Of course you can use whatever names you want -- you can be as specific as you want -- and just add rules to [`facts/begets`](facts/begets) that make them compatible with your recipe book. Do you have Cointreau and Grand Marnier in your bar? Do you have recipes that distinguish between them? Add 'em in, and write that they both beget orange liqueur. 109 | 110 | Remember that you don't need to worry about ingredients like lime juice or simple syrup -- you can just write that you have lime, or just write that you have sugar. Unless you bought one of those little green bottles of lime juice. Then you should just write lime juice. You get it. Be honest, in this most of all. 111 | 112 | # testing 113 | 114 | From within `nix-shell`: 115 | 116 | $ cram test.t --shell=$SHELL -i 117 | 118 | # where did all those recipes come from 119 | 120 | I scraped them from [Tuxedo No. 2](https://tuxedono2.com), an excellent cocktail site that I highly recommend you explore. 121 | 122 | The recipes are named after the paths to the cocktails on that site, so if you want to learn more about a cocktail, just go to `https://tuxedono2.com/$name-cocktail-recipe` (and buy them a drink while you're at it). 123 | -------------------------------------------------------------------------------- /facts/auto-begets: -------------------------------------------------------------------------------- 1 | lillet -> lillet or cocchi americano 2 | cocchi americano -> lillet or cocchi americano 3 | london dry gin -> london dry gin or new american gin 4 | new american gin -> london dry gin or new american gin 5 | lemon juice -> lemon juice or lime juice 6 | lime juice -> lemon juice or lime juice 7 | whole egg -> whole egg or egg white 8 | egg white -> whole egg or egg white 9 | madeira -> madeira or sherry 10 | sherry -> madeira or sherry 11 | london dry gin -> london dry gin or new american gin 12 | new american gin -> london dry gin or new american gin 13 | rye -> rye or bourbon 14 | bourbon -> rye or bourbon 15 | amer picon -> amer picon or china-china amer 16 | china-china amer -> amer picon or china-china amer 17 | herbsaint -> herbsaint or absinthe 18 | absinthe -> herbsaint or absinthe 19 | lillet -> lillet or cocchi americano 20 | cocchi americano -> lillet or cocchi americano 21 | reposado tequila -> reposado tequila or blanco tequila 22 | blanco tequila -> reposado tequila or blanco tequila 23 | absinthe -> absinthe or pastis 24 | pastis -> absinthe or pastis 25 | aged dark rum -> aged dark rum or light rum 26 | light rum -> aged dark rum or light rum 27 | amer picon -> amer picon or china-china amer 28 | china-china amer -> amer picon or china-china amer 29 | amaro averna -> amaro averna or sweet amaro 30 | sweet amaro -> amaro averna or sweet amaro 31 | rye -> rye or bourbon 32 | bourbon -> rye or bourbon 33 | london dry gin -> london dry gin or new american gin 34 | new american gin -> london dry gin or new american gin 35 | curacao -> curacao or orange liqueur 36 | orange liqueur -> curacao or orange liqueur 37 | rye -> rye or bourbon 38 | bourbon -> rye or bourbon 39 | blanco tequila -> blanco tequila or reposado tequila 40 | reposado tequila -> blanco tequila or reposado tequila 41 | mole bitters -> mole bitters or chocolate bitters 42 | chocolate bitters -> mole bitters or chocolate bitters 43 | bourbon -> bourbon or rye 44 | rye -> bourbon or rye 45 | 2:1 simple syrup -> 2:1 simple syrup or sugar cube 46 | sugar cube -> 2:1 simple syrup or sugar cube 47 | rye -> rye or bourbon 48 | bourbon -> rye or bourbon 49 | gold rum -> gold rum or dark rum 50 | dark rum -> gold rum or dark rum 51 | pastis -> pastis or absinthe 52 | absinthe -> pastis or absinthe 53 | rye -> rye or scotch 54 | scotch -> rye or scotch 55 | amaro nonino -> amaro nonino or sweet amaro 56 | sweet amaro -> amaro nonino or sweet amaro 57 | london dry gin -> london dry gin or new american gin 58 | new american gin -> london dry gin or new american gin 59 | simple syrup -> simple syrup or sugar cube 60 | sugar cube -> simple syrup or sugar cube 61 | absinthe -> absinthe or herbsaint 62 | herbsaint -> absinthe or herbsaint 63 | zucca -> zucca or cynar 64 | cynar -> zucca or cynar 65 | absinthe -> absinthe or pastis 66 | pastis -> absinthe or pastis 67 | lillet -> lillet or cocchi americano 68 | cocchi americano -> lillet or cocchi americano 69 | orange bitters -> orange bitters or angostura bitters 70 | angostura bitters -> orange bitters or angostura bitters 71 | curacao -> curacao or orange liqueur 72 | orange liqueur -> curacao or orange liqueur 73 | pineapple chunk -> pineapple chunk or pineapple frond 74 | pineapple frond -> pineapple chunk or pineapple frond 75 | cane syrup -> cane syrup or simple syrup 76 | simple syrup -> cane syrup or simple syrup 77 | london dry gin -> london dry gin or old tom gin 78 | old tom gin -> london dry gin or old tom gin 79 | salers -> salers or suze 80 | suze -> salers or suze 81 | herbsaint -> herbsaint or absinthe 82 | absinthe -> herbsaint or absinthe 83 | -------------------------------------------------------------------------------- /facts/auto-unbuyable: -------------------------------------------------------------------------------- 1 | lillet or cocchi americano 2 | london dry gin or new american gin 3 | lemon juice or lime juice 4 | whole egg or egg white 5 | madeira or sherry 6 | london dry gin or new american gin 7 | rye or bourbon 8 | amer picon or china-china amer 9 | herbsaint or absinthe 10 | lillet or cocchi americano 11 | reposado tequila or blanco tequila 12 | absinthe or pastis 13 | aged dark rum or light rum 14 | amer picon or china-china amer 15 | amaro averna or sweet amaro 16 | rye or bourbon 17 | london dry gin or new american gin 18 | curacao or orange liqueur 19 | rye or bourbon 20 | blanco tequila or reposado tequila 21 | mole bitters or chocolate bitters 22 | bourbon or rye 23 | 2:1 simple syrup or sugar cube 24 | rye or bourbon 25 | gold rum or dark rum 26 | pastis or absinthe 27 | rye or scotch 28 | amaro nonino or sweet amaro 29 | london dry gin or new american gin 30 | simple syrup or sugar cube 31 | absinthe or herbsaint 32 | zucca or cynar 33 | absinthe or pastis 34 | lillet or cocchi americano 35 | orange bitters or angostura bitters 36 | curacao or orange liqueur 37 | pineapple chunk or pineapple frond 38 | cane syrup or simple syrup 39 | london dry gin or old tom gin 40 | salers or suze 41 | herbsaint or absinthe 42 | -------------------------------------------------------------------------------- /facts/bar: -------------------------------------------------------------------------------- 1 | brandy 2 | dry vermouth 3 | lemon 4 | light rum 5 | lime 6 | orange liqueur 7 | reposado tequila 8 | sugar 9 | -------------------------------------------------------------------------------- /facts/begets: -------------------------------------------------------------------------------- 1 | aged dark rum -> aged rum 2 | aged dark rum -> dark rum 3 | aged rum -> rum 4 | amaro averna -> sweet amaro 5 | amaro montenegro -> sweet amaro 6 | amaro nonino -> sweet amaro 7 | anejo tequila -> tequila 8 | blanco tequila -> tequila 9 | blended scotch -> scotch 10 | coconut water -> coconut water ice cube 11 | coffee -> freshly brewed coffee 12 | coffee -> unsweetened cold brew coffee 13 | cognac -> brandy 14 | dark rum -> rum 15 | demarara sugar -> demerara syrup 16 | demerara rum -> rum 17 | egg -> egg white 18 | egg -> whole egg 19 | fine sea salt -> salt 20 | heavy cream -> hand-whipped cream 21 | honey -> honey syrup 22 | islay single malt scotch -> scotch 23 | lemon -> lemon juice 24 | lemon -> lemon peel 25 | lemon -> lemon twist 26 | lemon -> lemon wedge 27 | lemon -> lemon wheel 28 | lemon -> lemon zest 29 | light rum -> rum 30 | lime -> lime juice 31 | lime -> lime peel 32 | lime -> lime wedge 33 | lime -> lime wheel 34 | lime -> lime zest 35 | london dry gin -> gin 36 | maple syrup -> 1:1 diluted maple syrup 37 | new american gin -> gin 38 | orange -> orange juice 39 | orange -> orange peel 40 | orange -> orange wedge 41 | pineapple -> pineapple chunk 42 | pineapple -> pineapple frond 43 | pineapple -> pineapple wedges 44 | reposado tequila -> tequila 45 | sugar -> 2:1 simple syrup 46 | sugar -> simple syrup 47 | -------------------------------------------------------------------------------- /facts/combinations: -------------------------------------------------------------------------------- 1 | cinnamon syrup, sugar, cinnamon sticks 2 | ginger syrup, sugar, ginger 3 | lime cordial, sugar, lime zest 4 | pineapple syrup, sugar, pineapple 5 | raspberry syrup, sugar, raspberry 6 | sage syrup, sugar, sage 7 | -------------------------------------------------------------------------------- /facts/recipes: -------------------------------------------------------------------------------- 1 | 18th-century <- batavia arrack 2 | 18th-century <- creme de cacao 3 | 18th-century <- sweet vermouth 4 | 18th-century <- lime juice 5 | 20th-century <- london dry gin 6 | 20th-century <- creme de cacao 7 | 20th-century <- lillet or cocchi americano 8 | 20th-century <- lemon juice 9 | 212 <- reposado tequila 10 | 212 <- aperol 11 | 212 <- grapefruit juice 12 | 212 <- soda water 13 | airmail <- light rum 14 | airmail <- champagne 15 | airmail <- lime juice 16 | airmail <- simple syrup 17 | alabazam <- cognac 18 | alabazam <- orange liqueur 19 | alabazam <- lemon juice 20 | alabazam <- simple syrup 21 | alabazam <- angostura bitters 22 | alaska <- london dry gin or new american gin 23 | alaska <- yellow chartreuse 24 | alaska <- orange bitters 25 | americano <- campari 26 | americano <- sweet vermouth 27 | americano <- grapefruit peel 28 | americano <- soda water 29 | and-to-all-a-goodnight <- bourbon 30 | and-to-all-a-goodnight <- reposado tequila 31 | and-to-all-a-goodnight <- cherry heering 32 | and-to-all-a-goodnight <- orange bitters 33 | and-to-all-a-goodnight <- angostura bitters 34 | angostura-fizz <- angostura bitters 35 | angostura-fizz <- lemon juice or lime juice 36 | angostura-fizz <- egg white 37 | angostura-fizz <- grenadine 38 | angostura-fizz <- heavy cream 39 | angostura-fizz <- soda water 40 | apotheke <- fernet 41 | apotheke <- creme de menthe 42 | apotheke <- sweet vermouth 43 | arrack-strap <- dark rum 44 | arrack-strap <- batavia arrack 45 | arrack-strap <- sweet vermouth 46 | arrack-strap <- campari 47 | arrack-strap <- demerara syrup 48 | arrack-strap <- mole bitters 49 | arrack-strap <- orange bitters 50 | aviation <- london dry gin 51 | aviation <- creme de violette 52 | aviation <- maraschino liqueur 53 | aviation <- simple syrup 54 | aviation <- lemon juice 55 | aviation <- cherry 56 | baltimore-eggnog <- whole egg or egg white 57 | baltimore-eggnog <- whole milk 58 | baltimore-eggnog <- half and half 59 | baltimore-eggnog <- madeira or sherry 60 | baltimore-eggnog <- brandy 61 | baltimore-eggnog <- dark rum 62 | baltimore-eggnog <- simple syrup 63 | bamboo <- sherry 64 | bamboo <- dry vermouth 65 | bamboo <- angostura bitters 66 | bamboo <- orange bitters 67 | bees-knees <- old tom gin 68 | bees-knees <- honey syrup 69 | bees-knees <- lemon juice 70 | belfast-bastard <- london dry gin 71 | belfast-bastard <- dry vermouth 72 | belfast-bastard <- campari 73 | belfast-bastard <- pamplemousse liqueur 74 | belfast-bastard <- orange bitters 75 | between-the-sheets <- cognac 76 | between-the-sheets <- light rum 77 | between-the-sheets <- orange liqueur 78 | between-the-sheets <- lemon juice 79 | bijou <- london dry gin or new american gin 80 | bijou <- green chartreuse 81 | bijou <- sweet vermouth 82 | bijou <- orange bitters 83 | black-betty <- dark rum 84 | black-betty <- fernet 85 | black-betty <- amaro montenegro 86 | black-betty <- sherry 87 | black-flip <- black chocolate stout 88 | black-flip <- dark rum 89 | black-flip <- demerara syrup 90 | black-flip <- whole egg 91 | black-manhattan <- rye or bourbon 92 | black-manhattan <- amaro averna 93 | black-manhattan <- aromatic bitters 94 | black-manhattan <- orange bitters 95 | blood-and-sand <- blended scotch 96 | blood-and-sand <- cherry heering 97 | blood-and-sand <- sweet vermouth 98 | blood-and-sand <- orange juice 99 | blood-and-sand <- orange peel 100 | bobby-burns <- blended scotch 101 | bobby-burns <- sweet vermouth 102 | bobby-burns <- benedictine 103 | bobby-burns <- absinthe 104 | bobby-burns <- lemon twist 105 | boulevardier <- bourbon 106 | boulevardier <- campari 107 | boulevardier <- sweet vermouth 108 | bramble <- london dry gin 109 | bramble <- lemon juice 110 | bramble <- simple syrup 111 | bramble <- blackberry 112 | brandy-alexander <- brandy 113 | brandy-alexander <- creme de cacao 114 | brandy-alexander <- heavy cream 115 | brandy-crusta <- brandy 116 | brandy-crusta <- curacao 117 | brandy-crusta <- maraschino liqueur 118 | brandy-crusta <- lemon juice 119 | bronx <- london dry gin 120 | bronx <- sweet vermouth 121 | bronx <- dry vermouth 122 | bronx <- orange juice 123 | bronx <- angostura bitters 124 | brooklyn <- rye 125 | brooklyn <- dry vermouth 126 | brooklyn <- amer picon or china-china amer 127 | brooklyn <- maraschino liqueur 128 | brown-derby <- bourbon 129 | brown-derby <- grapefruit juice 130 | brown-derby <- lemon juice 131 | brown-derby <- honey syrup 132 | burl-ives <- blended scotch 133 | burl-ives <- sweet vermouth 134 | burl-ives <- creme de cacao 135 | burl-ives <- angostura bitters 136 | burl-ives <- orange peel 137 | buttermilk-flip <- bourbon 138 | buttermilk-flip <- 1:1 diluted maple syrup 139 | buttermilk-flip <- unsweetened cold brew coffee 140 | buttermilk-flip <- buttermilk 141 | buttermilk-flip <- whole egg 142 | champagne-cocktail <- champagne 143 | champagne-cocktail <- sugar cube 144 | champagne-cocktail <- angostura bitters 145 | champagne-cocktail <- brandy 146 | champagne-julep <- mint 147 | champagne-julep <- simple syrup 148 | champagne-julep <- brandy 149 | champagne-julep <- champagne 150 | champs-elysees <- brandy 151 | champs-elysees <- green chartreuse 152 | champs-elysees <- lemon juice 153 | champs-elysees <- simple syrup 154 | champs-elysees <- angostura bitters 155 | chartreuse-swizzle <- green chartreuse 156 | chartreuse-swizzle <- pineapple juice 157 | chartreuse-swizzle <- lime juice 158 | chartreuse-swizzle <- falernum 159 | chartreuse-swizzle <- gold rum 160 | chartreuse-swizzle <- angostura bitters 161 | chicago-cocktail <- brandy 162 | chicago-cocktail <- orange liqueur 163 | chicago-cocktail <- angostura bitters 164 | chicago-cocktail <- champagne 165 | chrysanthemum <- dry vermouth 166 | chrysanthemum <- benedictine 167 | chrysanthemum <- absinthe 168 | cinnamon-girl <- reposado tequila 169 | cinnamon-girl <- gold rum 170 | cinnamon-girl <- lime juice 171 | cinnamon-girl <- cinnamon syrup 172 | cinnamon-girl <- simple syrup 173 | cinnamon-girl <- orange bitters 174 | cinnamon-girl <- orange wedge 175 | claridge <- dry vermouth 176 | claridge <- london dry gin 177 | claridge <- apricot brandy 178 | claridge <- orange liqueur 179 | clover-club <- london dry gin 180 | clover-club <- raspberry syrup 181 | clover-club <- lemon juice 182 | clover-club <- egg white 183 | clover-club <- dry vermouth 184 | cock-n-bull <- bourbon 185 | cock-n-bull <- cognac 186 | cock-n-bull <- benedictine 187 | cock-n-bull <- curacao 188 | cock-n-bull <- angostura bitters 189 | cocktail-a-la-louisianne <- rye 190 | cocktail-a-la-louisianne <- benedictine 191 | cocktail-a-la-louisianne <- sweet vermouth 192 | cocktail-a-la-louisianne <- herbsaint or absinthe 193 | cocktail-a-la-louisianne <- peychaud's bitters 194 | coffee-cocktail <- cognac 195 | coffee-cocktail <- tawny port 196 | coffee-cocktail <- simple syrup 197 | coffee-cocktail <- whole egg 198 | coldfoot <- london dry gin 199 | coldfoot <- green chartreuse 200 | coldfoot <- maraschino liqueur 201 | coldfoot <- creme de menthe 202 | conifer <- pear brandy 203 | conifer <- green chartreuse 204 | conifer <- maraschino liqueur 205 | conifer <- dry vermouth 206 | conifer <- spruce essence 207 | corn-n-oil <- dark rum 208 | corn-n-oil <- falernum 209 | corn-n-oil <- lime juice 210 | corn-n-oil <- angostura bitters 211 | corpse-reviver-2 <- london dry gin 212 | corpse-reviver-2 <- orange liqueur 213 | corpse-reviver-2 <- lemon juice 214 | corpse-reviver-2 <- lillet or cocchi americano 215 | corpse-reviver-2 <- absinthe 216 | creole <- rye 217 | creole <- sweet vermouth 218 | creole <- amer picon 219 | creole <- benedictine 220 | cynar-flip <- cynar 221 | cynar-flip <- whole egg 222 | cynar-flip <- 2:1 simple syrup 223 | daiquiri <- light rum 224 | daiquiri <- lime juice 225 | daiquiri <- simple syrup 226 | death-in-the-afternoon <- absinthe 227 | death-in-the-afternoon <- champagne 228 | debbie-don-t <- reposado tequila 229 | debbie-don-t <- amaro averna 230 | debbie-don-t <- lemon juice 231 | debbie-don-t <- maple syrup 232 | diamondback <- rye 233 | diamondback <- yellow chartreuse 234 | diamondback <- apple brandy 235 | diki-diki <- apple brandy 236 | diki-diki <- swedish punsch 237 | diki-diki <- grapefruit juice 238 | doctor-cocktail <- gold rum 239 | doctor-cocktail <- swedish punsch 240 | doctor-cocktail <- lime juice 241 | dressing-gown <- blanco tequila 242 | dressing-gown <- mezcal 243 | dressing-gown <- pastis 244 | dressing-gown <- orgeat 245 | dressing-gown <- lime juice 246 | dressing-gown <- egg white 247 | dressing-gown <- soda water 248 | east-india <- brandy 249 | east-india <- curacao 250 | east-india <- pineapple syrup 251 | east-india <- maraschino liqueur 252 | east-india <- angostura bitters 253 | el-burro <- reposado tequila or blanco tequila 254 | el-burro <- absinthe or pastis 255 | el-burro <- lime juice 256 | el-burro <- pineapple juice 257 | el-burro <- simple syrup 258 | el-burro <- ginger beer 259 | el-diablo <- reposado tequila 260 | el-diablo <- creme de cassis 261 | el-diablo <- lime juice 262 | el-diablo <- ginger beer 263 | el-presidente <- aged dark rum or light rum 264 | el-presidente <- dry vermouth 265 | el-presidente <- curacao 266 | el-presidente <- grenadine 267 | el-tigre <- reposado tequila 268 | el-tigre <- aquavit 269 | el-tigre <- whole egg 270 | el-tigre <- cinnamon syrup 271 | el-tigre <- lemon juice 272 | el-tigre <- mole bitters 273 | el-tigre <- soda water 274 | endless-summer <- blanco tequila 275 | endless-summer <- dry vermouth 276 | endless-summer <- pamplemousse liqueur 277 | endless-summer <- orange liqueur 278 | endless-summer <- pineapple juice 279 | endless-summer <- lime juice 280 | endless-summer <- simple syrup 281 | erin <- rye 282 | erin <- amer picon or china-china amer 283 | erin <- suze 284 | erin <- sweet vermouth 285 | erin <- allspice dram 286 | fair-harvard <- pisco 287 | fair-harvard <- blanc vermouth 288 | fair-harvard <- orange bitters 289 | fionia <- london dry gin 290 | fionia <- blanc vermouth 291 | fionia <- sherry 292 | fionia <- pickled chamomile liquid 293 | fish-house-punch <- cognac 294 | fish-house-punch <- dark rum 295 | fish-house-punch <- peach liqueur 296 | fish-house-punch <- lemon juice 297 | fish-house-punch <- simple syrup 298 | fish-house-punch <- soda water 299 | flannel-shirt <- scotch 300 | flannel-shirt <- apple cider 301 | flannel-shirt <- amaro averna 302 | flannel-shirt <- lemon juice 303 | flannel-shirt <- demerara syrup 304 | flannel-shirt <- allspice dram 305 | flannel-shirt <- angostura bitters 306 | fog-cutter <- light rum 307 | fog-cutter <- london dry gin 308 | fog-cutter <- brandy 309 | fog-cutter <- orgeat 310 | fog-cutter <- orange juice 311 | fog-cutter <- lemon juice 312 | fog-cutter <- sherry 313 | ford <- old tom gin 314 | ford <- dry vermouth 315 | ford <- benedictine 316 | ford <- orange bitters 317 | french-75 <- london dry gin 318 | french-75 <- simple syrup 319 | french-75 <- lemon juice 320 | french-75 <- champagne 321 | frisco <- rye 322 | frisco <- benedictine 323 | frisco <- lemon juice 324 | frisco <- lime juice 325 | frisco <- lemon wheel 326 | frisco <- lime wheel 327 | frisco <- cherry 328 | gefion <- rye 329 | gefion <- aquavit 330 | gefion <- amaro montenegro 331 | gefion <- honey syrup 332 | gilchrist <- blended scotch 333 | gilchrist <- pear brandy 334 | gilchrist <- grapefruit juice 335 | gilchrist <- amaro averna or sweet amaro 336 | gilchrist <- orange bitters 337 | gimlet <- london dry gin 338 | gimlet <- lime cordial 339 | gimlet <- lime juice 340 | gloria <- london dry gin 341 | gloria <- campari 342 | gloria <- dry vermouth 343 | gloria <- orange liqueur 344 | gold-rush <- bourbon 345 | gold-rush <- lemon juice 346 | gold-rush <- honey syrup 347 | grasshopper <- creme de cacao 348 | grasshopper <- creme de menthe 349 | grasshopper <- heavy cream 350 | green-glacier <- cognac 351 | green-glacier <- green chartreuse 352 | green-glacier <- creme de cacao 353 | green-glacier <- angostura bitters 354 | groceries-for-the-queen <- gin 355 | groceries-for-the-queen <- cherry heering 356 | groceries-for-the-queen <- amaro montenegro 357 | groceries-for-the-queen <- orange juice 358 | groceries-for-the-queen <- chocolate bitters 359 | hanky-panky <- london dry gin 360 | hanky-panky <- sweet vermouth 361 | hanky-panky <- fernet 362 | harvard <- cognac 363 | harvard <- sweet vermouth 364 | harvard <- angostura bitters 365 | harvard <- soda water 366 | hemingway-daiquiri <- light rum 367 | hemingway-daiquiri <- grapefruit juice 368 | hemingway-daiquiri <- lime juice 369 | hemingway-daiquiri <- maraschino liqueur 370 | high-divide <- new american gin 371 | high-divide <- génépi 372 | high-divide <- dry vermouth 373 | high-divide <- simple syrup 374 | honeymoon <- apple brandy 375 | honeymoon <- curacao 376 | honeymoon <- benedictine 377 | honeymoon <- lemon juice 378 | hot-buttered-rum <- dark rum 379 | hot-buttered-rum <- simple syrup 380 | hot-buttered-rum <- allspice dram 381 | hot-buttered-rum <- butter 382 | hot-buttered-rum <- mole bitters 383 | hot-buttered-rum <- aromatic bitters 384 | hotel-nacional-special <- gold rum 385 | hotel-nacional-special <- pineapple chunk 386 | hotel-nacional-special <- lime juice 387 | hotel-nacional-special <- simple syrup 388 | hotel-nacional-special <- apricot brandy 389 | hurricane <- black blended rum 390 | hurricane <- passionfruit syrup 391 | hurricane <- lemon juice 392 | improved-whiskey-cocktail <- rye or bourbon 393 | improved-whiskey-cocktail <- maraschino liqueur 394 | improved-whiskey-cocktail <- simple syrup 395 | improved-whiskey-cocktail <- absinthe 396 | improved-whiskey-cocktail <- peychaud's bitters 397 | improved-whiskey-cocktail <- angostura bitters 398 | jack-rose <- apple brandy 399 | jack-rose <- grenadine 400 | jack-rose <- lemon juice 401 | japanese <- brandy 402 | japanese <- orgeat 403 | japanese <- angostura bitters 404 | jersey <- hard apple cider 405 | jersey <- apple brandy 406 | jersey <- simple syrup 407 | jersey <- angostura bitters 408 | jimmy-roosevelt <- champagne 409 | jimmy-roosevelt <- cognac 410 | jimmy-roosevelt <- green chartreuse 411 | jimmy-roosevelt <- simple syrup 412 | jimmy-roosevelt <- angostura bitters 413 | jungle-bird <- pineapple juice 414 | jungle-bird <- dark rum 415 | jungle-bird <- campari 416 | jungle-bird <- lime juice 417 | jungle-bird <- simple syrup 418 | la-dominicana <- dark rum 419 | la-dominicana <- coffee liqueur 420 | la-dominicana <- hand-whipped cream 421 | la-perla <- reposado tequila 422 | la-perla <- sherry 423 | la-perla <- pear liqueur 424 | last-man-standing <- london dry gin 425 | last-man-standing <- rye 426 | last-man-standing <- fernet 427 | last-man-standing <- campari 428 | lawn-dart <- blanco tequila 429 | lawn-dart <- london dry gin or new american gin 430 | lawn-dart <- lime juice 431 | lawn-dart <- simple syrup 432 | lawn-dart <- green chartreuse 433 | lawn-dart <- bell pepper 434 | light-my-fire <- london dry gin 435 | light-my-fire <- pear liqueur 436 | light-my-fire <- lemon juice 437 | light-my-fire <- green chartreuse 438 | light-my-fire <- lavender bitters 439 | lion-s-tail <- bourbon 440 | lion-s-tail <- allspice dram 441 | lion-s-tail <- lime juice 442 | lion-s-tail <- simple syrup 443 | lion-s-tail <- angostura bitters 444 | live-free-or-die <- bourbon 445 | live-free-or-die <- cherry heering 446 | live-free-or-die <- ruby port 447 | live-free-or-die <- absinthe 448 | live-free-or-die <- dry champagne 449 | mai-tai <- gold rum 450 | mai-tai <- dark rum 451 | mai-tai <- lime juice 452 | mai-tai <- curacao or orange liqueur 453 | mai-tai <- orgeat 454 | mai-tai <- simple syrup 455 | mai-tai <- mint 456 | maiden-s-blush <- old tom gin 457 | maiden-s-blush <- raspberry cordial 458 | maiden-s-blush <- lemon juice 459 | maiden-s-blush <- absinthe 460 | manhattan <- rye or bourbon 461 | manhattan <- sweet vermouth 462 | manhattan <- angostura bitters 463 | margarita <- blanco tequila or reposado tequila 464 | margarita <- lime juice 465 | margarita <- orange liqueur 466 | margarita <- lime wedge 467 | mariners-ghost <- dark rum 468 | mariners-ghost <- light rum 469 | mariners-ghost <- mango puree 470 | mariners-ghost <- grapefruit juice 471 | mariners-ghost <- allspice dram 472 | mariners-ghost <- lime juice 473 | mariners-ghost <- simple syrup 474 | marmalade <- new american gin 475 | marmalade <- lemon juice 476 | marmalade <- marmalade syrup 477 | martini <- london dry gin 478 | martini <- dry vermouth 479 | milk-punch <- bourbon 480 | milk-punch <- simple syrup 481 | milk-punch <- vanilla extract 482 | milk-punch <- milk 483 | milk-punch <- heavy cream 484 | milk-punch <- nutmeg 485 | monkey-gland <- london dry gin 486 | monkey-gland <- orange juice 487 | monkey-gland <- absinthe 488 | monkey-gland <- grenadine 489 | monte-carlo <- rye 490 | monte-carlo <- benedictine 491 | monte-carlo <- angostura bitters 492 | monte-carlo <- peychaud's bitters 493 | morning-glory-fizz <- blended scotch 494 | morning-glory-fizz <- lemon juice 495 | morning-glory-fizz <- simple syrup 496 | morning-glory-fizz <- egg white 497 | morning-glory-fizz <- absinthe 498 | morning-glory-fizz <- soda water 499 | mucho-picchu <- pisco 500 | mucho-picchu <- maraschino liqueur 501 | mucho-picchu <- grapefruit juice 502 | mucho-picchu <- simple syrup 503 | mucho-picchu <- dry champagne 504 | naked-famous <- mezcal 505 | naked-famous <- yellow chartreuse 506 | naked-famous <- aperol 507 | naked-famous <- lime juice 508 | negroni <- london dry gin 509 | negroni <- campari 510 | negroni <- sweet vermouth 511 | negus-punch <- oloroso sherry 512 | negus-punch <- cognac 513 | negus-punch <- lemon juice 514 | negus-punch <- vanilla syrup 515 | negus-punch <- sugar 516 | negus-punch <- lemon zest 517 | negus-punch <- nutmeg 518 | nicholas-sage <- blanco tequila 519 | nicholas-sage <- lime juice 520 | nicholas-sage <- sage syrup 521 | nicholas-sage <- benedictine 522 | nut-bunny <- bourbon 523 | nut-bunny <- nocino 524 | nut-bunny <- lemon juice 525 | nut-bunny <- walnut bitters 526 | oaxacan-old-fashioned <- reposado tequila 527 | oaxacan-old-fashioned <- mezcal 528 | oaxacan-old-fashioned <- agave syrup 529 | oaxacan-old-fashioned <- mole bitters or chocolate bitters 530 | odd-bedfellows <- aged rum 531 | odd-bedfellows <- gold rum 532 | odd-bedfellows <- coconut cream 533 | odd-bedfellows <- pineapple juice 534 | odd-bedfellows <- creme de cacao 535 | odd-bedfellows <- fernet 536 | old-fashioned <- bourbon or rye 537 | old-fashioned <- angostura bitters 538 | old-fashioned <- 2:1 simple syrup or sugar cube 539 | old-fashioned <- orange peel 540 | old-hickory <- dry vermouth 541 | old-hickory <- sweet vermouth 542 | old-hickory <- peychaud's bitters 543 | old-hickory <- orange bitters 544 | old-pal <- rye or bourbon 545 | old-pal <- campari 546 | old-pal <- dry vermouth 547 | paere-dansk <- dry champagne 548 | paere-dansk <- vodka 549 | paere-dansk <- lemon juice 550 | paere-dansk <- spiced pear liqueur 551 | paere-dansk <- orgeat 552 | pago-pago-cocktail <- gold rum 553 | pago-pago-cocktail <- lime juice 554 | pago-pago-cocktail <- green chartreuse 555 | pago-pago-cocktail <- creme de cacao 556 | pago-pago-cocktail <- pineapple chunk 557 | painkiller <- dark rum 558 | painkiller <- gold rum 559 | painkiller <- pineapple juice 560 | painkiller <- orange juice 561 | painkiller <- coconut cream 562 | pale-end-of-the-day <- blanc vermouth 563 | pale-end-of-the-day <- london dry gin 564 | pale-end-of-the-day <- pear liqueur 565 | palmetto <- gold rum or dark rum 566 | palmetto <- sweet vermouth 567 | palmetto <- orange bitters 568 | paper-plane <- bourbon 569 | paper-plane <- aperol 570 | paper-plane <- amaro nonino 571 | paper-plane <- lemon juice 572 | pearl-of-puebla <- mezcal 573 | pearl-of-puebla <- yellow chartreuse 574 | pearl-of-puebla <- lime juice 575 | pearl-of-puebla <- pastis or absinthe 576 | pearl-of-puebla <- agave syrup 577 | pearl-of-puebla <- oregano 578 | pendennis <- london dry gin 579 | pendennis <- apricot brandy 580 | pendennis <- peychaud's bitters 581 | pendennis <- lime juice 582 | penicillin <- blended scotch 583 | penicillin <- lemon juice 584 | penicillin <- honey syrup 585 | penicillin <- ginger 586 | penicillin <- islay single malt scotch 587 | pineapple-fizz <- light rum 588 | pineapple-fizz <- gold rum 589 | pineapple-fizz <- pineapple juice 590 | pineapple-fizz <- simple syrup 591 | pineapple-fizz <- angostura bitters 592 | pineapple-fizz <- soda water 593 | planter-s-punch <- dark rum 594 | planter-s-punch <- lime juice 595 | planter-s-punch <- simple syrup 596 | planter-s-punch <- allspice dram 597 | planter-s-punch <- angostura bitters 598 | prado <- blanco tequila 599 | prado <- lime juice 600 | prado <- maraschino liqueur 601 | prado <- egg white 602 | presbyterian <- soda water 603 | presbyterian <- rye or scotch 604 | presbyterian <- lemon juice 605 | presbyterian <- lime juice 606 | presbyterian <- ginger syrup 607 | pressure-drop <- old tom gin 608 | pressure-drop <- amaro nonino or sweet amaro 609 | pressure-drop <- dry vermouth 610 | pressure-drop <- pear brandy 611 | pressure-drop <- angostura bitters 612 | puerto-rican-racer <- aged rum 613 | puerto-rican-racer <- apple brandy 614 | puerto-rican-racer <- yellow chartreuse 615 | puerto-rican-racer <- grenadine 616 | puerto-rican-racer <- peychaud's bitters 617 | queens-park-swizzle <- dark rum 618 | queens-park-swizzle <- lime juice 619 | queens-park-swizzle <- simple syrup 620 | queens-park-swizzle <- mint 621 | queens-park-swizzle <- angostura bitters 622 | queens-park-swizzle <- peychaud's bitters 623 | ramos-gin-fizz <- london dry gin or new american gin 624 | ramos-gin-fizz <- simple syrup 625 | ramos-gin-fizz <- heavy cream 626 | ramos-gin-fizz <- lemon juice 627 | ramos-gin-fizz <- lime juice 628 | ramos-gin-fizz <- egg white 629 | ramos-gin-fizz <- orange flower water 630 | ramos-gin-fizz <- soda water 631 | red-hook <- rye 632 | red-hook <- maraschino liqueur 633 | red-hook <- sweet vermouth 634 | remember-the-maine <- rye 635 | remember-the-maine <- sweet vermouth 636 | remember-the-maine <- cherry heering 637 | remember-the-maine <- absinthe 638 | reveillon <- apple brandy 639 | reveillon <- pear brandy 640 | reveillon <- allspice dram 641 | reveillon <- sweet vermouth 642 | reveillon <- angostura bitters 643 | revolver <- bourbon 644 | revolver <- coffee liqueur 645 | revolver <- orange bitters 646 | rob-roy <- scotch 647 | rob-roy <- sweet vermouth 648 | rob-roy <- angostura bitters 649 | rum-flip <- gold rum 650 | rum-flip <- whole egg 651 | rum-flip <- simple syrup 652 | saladito <- mezcal 653 | saladito <- honey syrup 654 | saladito <- lime juice 655 | saladito <- cayenne 656 | saladito <- fine sea salt 657 | sawyer <- london dry gin 658 | sawyer <- lime juice 659 | sawyer <- simple syrup 660 | sawyer <- angostura bitters 661 | sawyer <- peychaud's bitters 662 | sawyer <- orange bitters 663 | sazerac <- rye 664 | sazerac <- peychaud's bitters 665 | sazerac <- simple syrup or sugar cube 666 | sazerac <- absinthe or herbsaint 667 | scofflaw <- rye 668 | scofflaw <- dry vermouth 669 | scofflaw <- lemon juice 670 | scofflaw <- grenadine 671 | scofflaw <- orange bitters 672 | scorched-earth <- mezcal 673 | scorched-earth <- zucca or cynar 674 | scorched-earth <- campari 675 | scorched-earth <- orange bitters 676 | scotch-coconut <- scotch 677 | scotch-coconut <- aged rum 678 | scotch-coconut <- curacao 679 | scotch-coconut <- demerara syrup 680 | scotch-coconut <- mole bitters 681 | scotch-coconut <- coconut water ice cube 682 | seelbach <- champagne 683 | seelbach <- peychaud's bitters 684 | seelbach <- angostura bitters 685 | seelbach <- bourbon 686 | seelbach <- orange liqueur 687 | sherpa <- bourbon 688 | sherpa <- allspice dram 689 | sherpa <- curacao 690 | sherpa <- orange bitters 691 | sherry-cobbler <- sherry 692 | sherry-cobbler <- lemon juice 693 | sherry-cobbler <- orange liqueur 694 | shoulder-season <- rye 695 | shoulder-season <- apple brandy 696 | shoulder-season <- angostura bitters 697 | shoulder-season <- walnut bitters 698 | shoulder-season <- maple syrup 699 | si-guey <- reposado tequila 700 | si-guey <- scotch 701 | si-guey <- curacao 702 | si-guey <- orange bitters 703 | sidecar <- brandy 704 | sidecar <- orange liqueur 705 | sidecar <- lemon juice 706 | sidecar <- sugar 707 | singapore-sling <- london dry gin 708 | singapore-sling <- benedictine 709 | singapore-sling <- cherry heering 710 | singapore-sling <- lime juice 711 | singapore-sling <- soda water 712 | sleepy-resurrection <- london dry gin 713 | sleepy-resurrection <- rhubarb puree 714 | sleepy-resurrection <- lemon juice 715 | sleepy-resurrection <- benedictine 716 | sleepy-resurrection <- lillet 717 | sleepy-resurrection <- soda water 718 | soul-clench <- pisco 719 | soul-clench <- dry vermouth 720 | soul-clench <- pamplemousse liqueur 721 | soul-clench <- elderflower liqueur 722 | soul-clench <- falernum 723 | southside <- london dry gin 724 | southside <- lemon juice 725 | southside <- simple syrup 726 | southside <- mint 727 | spanish-coffee <- navy-strength gold rum 728 | spanish-coffee <- coffee liqueur 729 | spanish-coffee <- orange liqueur 730 | spanish-coffee <- freshly brewed coffee 731 | spanish-coffee <- lime wedge 732 | spanish-coffee <- sugar 733 | spruce-goose <- new american gin 734 | spruce-goose <- honey syrup 735 | spruce-goose <- lime juice 736 | spruce-goose <- angostura bitters 737 | spruce-goose <- salt 738 | star <- apple brandy 739 | star <- sweet vermouth 740 | star <- simple syrup 741 | star <- angostura bitters 742 | stardust <- reposado tequila 743 | stardust <- creme de cacao 744 | stardust <- heavy cream 745 | stardust <- cinnamon syrup 746 | stardust <- absinthe 747 | stinger <- cognac 748 | stinger <- creme de menthe 749 | stinger <- absinthe or pastis 750 | stone-fence <- hard apple cider 751 | stone-fence <- bourbon 752 | stone-fence <- maple syrup 753 | suburban <- rye 754 | suburban <- dark rum 755 | suburban <- port 756 | suburban <- orange bitters 757 | suburban <- angostura bitters 758 | suissesse <- absinthe 759 | suissesse <- aquavit 760 | suissesse <- creme de cacao 761 | suissesse <- egg white 762 | suissesse <- peychaud's bitters 763 | sutter-s-mill <- bourbon 764 | sutter-s-mill <- pineapple wedges 765 | sutter-s-mill <- lemon juice 766 | sutter-s-mill <- honey syrup 767 | telekinesis <- blanco tequila 768 | telekinesis <- campari 769 | telekinesis <- sweet vermouth 770 | telekinesis <- yellow chartreuse 771 | test-pilot <- dark rum 772 | test-pilot <- light rum 773 | test-pilot <- falernum 774 | test-pilot <- orange liqueur 775 | test-pilot <- lime juice 776 | test-pilot <- angostura bitters 777 | test-pilot <- absinthe 778 | the-abbey <- london dry gin 779 | the-abbey <- lillet or cocchi americano 780 | the-abbey <- orange juice 781 | the-abbey <- orange bitters or angostura bitters 782 | the-blinker <- rye 783 | the-blinker <- grapefruit juice 784 | the-blinker <- raspberry syrup 785 | the-last-word <- london dry gin 786 | the-last-word <- green chartreuse 787 | the-last-word <- maraschino liqueur 788 | the-last-word <- lime juice 789 | the-long-hello <- champagne 790 | the-long-hello <- apple brandy 791 | the-long-hello <- elderflower liqueur 792 | the-long-hello <- whiskey barrel aged bitters 793 | the-pegu-club <- london dry gin 794 | the-pegu-club <- curacao or orange liqueur 795 | the-pegu-club <- lime juice 796 | the-pegu-club <- angostura bitters 797 | the-pegu-club <- orange bitters 798 | three-dots-a-dash <- dark rum 799 | three-dots-a-dash <- lime juice 800 | three-dots-a-dash <- orange juice 801 | three-dots-a-dash <- simple syrup 802 | three-dots-a-dash <- falernum 803 | three-dots-a-dash <- allspice dram 804 | three-dots-a-dash <- angostura bitters 805 | three-dots-a-dash <- pineapple chunk or pineapple frond 806 | ti-punch <- rhum agricole 807 | ti-punch <- lime peel 808 | ti-punch <- cane syrup or simple syrup 809 | tom-collins <- old tom gin 810 | tom-collins <- simple syrup 811 | tom-collins <- lemon juice 812 | tom-collins <- soda water 813 | toronto <- rye 814 | toronto <- fernet 815 | toronto <- simple syrup 816 | toronto <- angostura bitters 817 | trinidad-sour <- angostura bitters 818 | trinidad-sour <- orgeat 819 | trinidad-sour <- lemon juice 820 | trinidad-sour <- rye 821 | turf <- london dry gin or old tom gin 822 | turf <- dry vermouth 823 | turf <- maraschino liqueur 824 | turf <- absinthe 825 | turf <- orange bitters 826 | ty-cobbler <- reposado tequila 827 | ty-cobbler <- cynar 828 | ty-cobbler <- simple syrup 829 | ty-cobbler <- cherry 830 | ty-cobbler <- mole bitters 831 | vampire-blues <- bourbon 832 | vampire-blues <- sherry 833 | vampire-blues <- lemon juice 834 | vampire-blues <- pumpkin butter 835 | vampire-blues <- angostura bitters 836 | verte-chaud <- green chartreuse 837 | verte-chaud <- shaved chocolate 838 | verte-chaud <- heavy cream 839 | vesper <- gin 840 | vesper <- kina 841 | vesper <- orange bitters 842 | vieux-carre <- rye 843 | vieux-carre <- cognac 844 | vieux-carre <- sweet vermouth 845 | vieux-carre <- benedictine 846 | vieux-carre <- peychaud's bitters 847 | vieux-carre <- angostura bitters 848 | whiskey-sour <- bourbon 849 | whiskey-sour <- lemon juice 850 | whiskey-sour <- simple syrup 851 | whiskey-sour <- egg white 852 | white-negroni <- london dry gin 853 | white-negroni <- lillet 854 | white-negroni <- salers or suze 855 | white-negroni <- lemon peel 856 | white-russian <- vodka 857 | white-russian <- coffee liqueur 858 | white-russian <- half and half 859 | widows-kiss <- apple brandy 860 | widows-kiss <- yellow chartreuse 861 | widows-kiss <- benedictine 862 | widows-kiss <- angostura bitters 863 | zombie <- gold rum 864 | zombie <- dark rum 865 | zombie <- demerara rum 866 | zombie <- herbsaint or absinthe 867 | zombie <- falernum 868 | zombie <- donn's mix 869 | zombie <- grenadine 870 | zombie <- angostura bitters 871 | -------------------------------------------------------------------------------- /facts/unbuyable: -------------------------------------------------------------------------------- 1 | 1:1 diluted maple syrup 2 | coconut water ice cube 3 | egg white 4 | hand-whipped cream 5 | honey syrup 6 | lemon peel 7 | lemon twist 8 | lemon wedge 9 | lemon wheel 10 | lemon zest 11 | lime cordial 12 | lime peel 13 | lime wedge 14 | lime wheel 15 | lime zest 16 | orange peel 17 | orange wedge 18 | sage syrup 19 | whole egg 20 | -------------------------------------------------------------------------------- /generate-auto-facts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | cd facts 6 | 7 | sed recipes -En -e 's/^.+<- //' -e '/ or /{ s/(.+) or (.+)/\1 -> \0\n\2 -> \0/; p }' >auto-begets 8 | sed recipes -En -e 's/^.+<- //' -e '/ or /p' >auto-unbuyable 9 | -------------------------------------------------------------------------------- /list-ingredients: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | cd facts 6 | 7 | ( sed recipes -E -e 's/^.+<- //' 8 | sed begets -E -e 's/ -> .+$//' 9 | sed combinations -E -e 's/, /\n/g' 10 | ) \ 11 | | grep -vFf <(cat {,auto-}unbuyable) \ 12 | | sort -u 13 | -------------------------------------------------------------------------------- /mixologician.dl: -------------------------------------------------------------------------------- 1 | .type Ingredient <: symbol 2 | .type Recipe <: symbol 3 | 4 | .decl Has(x : Ingredient) 5 | .input Has(filename="bar") 6 | 7 | .decl Needs(drink : Recipe, ingredient : Ingredient) 8 | .input Needs(filename="recipes", delimiter=" <- ") 9 | 10 | .decl Begets(in : Ingredient, out : Ingredient) 11 | .input Begets(filename="begets", delimiter=" -> ") 12 | .input Begets(filename="auto-begets", delimiter=" -> ") 13 | 14 | .decl Composite(result : Ingredient, first : Ingredient, second : Ingredient) 15 | .input Composite(filename="combinations", delimiter=", ") 16 | 17 | .decl IsRecipe(x : Recipe) 18 | IsRecipe(x) :- Needs(x, _). 19 | 20 | .decl IsIngredient(x : Ingredient) 21 | IsIngredient(x) :- Needs(_, x). 22 | IsIngredient(x) :- Begets(x, _). 23 | IsIngredient(x) :- Begets(_, x). 24 | IsIngredient(x) :- Composite(x, _, _). 25 | IsIngredient(x) :- Composite(_, x, _). 26 | IsIngredient(x) :- Composite(_, _, x). 27 | 28 | .decl Unbuyable(x : Ingredient) 29 | .input Unbuyable(filename="unbuyable") 30 | .input Unbuyable(filename="auto-unbuyable") 31 | 32 | Begets(x, x) :- IsIngredient(x). 33 | Begets(x, z) :- Begets(x, y), Begets(y, z). 34 | 35 | Has(out) :- Has(in), Begets(in, out). 36 | 37 | Begets(x, result) :- Composite(result, first, second), Has(first), Begets(x, second). 38 | Begets(x, result) :- Composite(result, first, second), Has(second), Begets(x, first). 39 | 40 | .decl Missing(drink : Recipe, ingredient : Ingredient) 41 | Missing(drink, ingredient) :- Needs(drink, ingredient), !Has(ingredient). 42 | 43 | .decl Mixable(drink : Recipe) 44 | Mixable(drink) :- IsRecipe(drink), !Missing(drink, _). 45 | 46 | .decl MixableRecipe(drink : Recipe, ingredient : Ingredient) 47 | MixableRecipe(drink, recipe) :- Mixable(drink), Needs(drink, recipe). 48 | 49 | .decl Enables(missing : Ingredient, drink : Recipe) 50 | Enables(ingredient, drink) :- 51 | !Unbuyable(ingredient), 52 | Missing(drink, out), 53 | Begets(ingredient, out), 54 | count : { Missing(drink, _) } = 55 | count : { Begets(ingredient, product), Missing(drink, product) } 56 | . 57 | 58 | .output Mixable(filename="mixable") 59 | .output MixableRecipe(filename="mixable-recipes", delimiter=" <- ") 60 | .output Enables(filename="shopping-list", delimiter=" -> ") 61 | -------------------------------------------------------------------------------- /parse: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #!nix-shell -i bash -p pup 3 | 4 | set -euo pipefail 5 | 6 | cd tuxedono2 7 | 8 | for file in *-cocktail-recipe; do 9 | name=${file%-cocktail-recipe} 10 | pup -p -f "$file" '.recipe__recipe ul:first-of-type .ingredient text{}' \ 11 | | sed -En -e '/^$/d' -e ':start /^$/!{ H; n; b start }' -e 'x; s/\n//g; s/^\s+//; p' \ 12 | | sed -E \ 13 | -e 's/^.*$/\L\0/' \ 14 | -e 's/^-[0-9] *//' \ 15 | -e 's/ *to (fill|top)//' \ 16 | -e 's/,? *for (muddling|rim(ming the glass)?)//' \ 17 | -e 's/^(dash(es)?|pinch|slices|droplets|barspoon|(splash|sprigs|sprinkle) of|fill( with)?) *//' \ 18 | -e 's/^five-inch slice of *//' \ 19 | -e 's/^inch thick disk of lime peel with some flesh attached$/lime peel/' \ 20 | -e 's/^(long|(very )?cold|chilled|fresh|hot|large|wet) *//' \ 21 | -e 's/ , optional//' \ 22 | -e 's/cherries/cherry/' \ 23 | -e 's/pineapple chunk or frond/pineapple chunk or pineapple frond/' \ 24 | -e 's/pineapple chunks|cubed pineapple/pineapple chunk/' \ 25 | -e 's/simple syrup, rich/2:1 simple syrup/' \ 26 | -e 's/ *\([^)]+\)//' \ 27 | -e 's/zest of [0-9]+ (.+)/\1 zest/' \ 28 | -e 's/(1|one) sugar cube/sugar cube/' \ 29 | -e 's/china china amer/china-china amer/' \ 30 | -e 's/amaro nonino or similar/amaro nonino or sweet amaro/' \ 31 | -e 's/amaro averna or similar sweet amaro/amaro averna or sweet amaro/' \ 32 | -e 's/^egg$/whole egg/' \ 33 | -e 's/^mint leaves$/mint/' \ 34 | -e 's/1:1 simple syrup/simple syrup/' \ 35 | -e 's/^angostura$/angostura bitters/' \ 36 | -e 's/^negus mix$/oloroso sherry\ncognac\nlemon juice\nvanilla syrup\nsugar\nlemon zest\nnutmeg/' \ 37 | -e '/gar?nish/d' \ 38 | -e '/^water$/d' \ 39 | -e '/^crushed ice$/d' \ 40 | | xargs -n1 -d'\n' printf '%s <- %s\n' "$name" 41 | done 42 | -------------------------------------------------------------------------------- /results/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | with import {}; mkShell { 2 | nativeBuildInputs = [ souffle python39Packages.cram ]; 3 | } 4 | -------------------------------------------------------------------------------- /test.t: -------------------------------------------------------------------------------- 1 | Set up some helpers that we'll use. 2 | 3 | $ function runtest { 4 | > # souffle's output is a little noisy; do some light post-processing 5 | > # to make the tests easier to read 6 | > souffle "$TESTDIR"/mixologician.dl 7 | > if [[ -s mixable ]]; then 8 | > echo 'mixable:' $(sort mixable) 9 | > fi 10 | > if [[ -s shopping-list ]]; then 11 | > echo "shopping list:" 12 | > sort shopping-list 13 | > fi 14 | > } 15 | $ function empty_bar { cat /dev/null > bar; } 16 | $ function empty_recipe_book { cat /dev/null > recipes; } 17 | $ function buy { 18 | > for ingredient in "$@"; do 19 | > echo "$ingredient" >> bar; 20 | > done 21 | > } 22 | $ function sell { 23 | > # this function is... very fragile. 24 | > # it will break if an ingredient contains a single quote. 25 | > filter="grep -vF " 26 | > for ingredient in "$@"; do 27 | > filter=$filter"-e '$ingredient'" 28 | > done 29 | > # the eval is necessary to interpret the quotes correctly... look 30 | > # no one is more upset about this than i am 31 | > eval "$filter" bar > new_bar 32 | > mv new_bar bar 33 | > } 34 | $ function set_bar { empty_bar; buy "$@"; } 35 | $ function add_recipe { 36 | > drink=$1 37 | > shift 38 | > for ingredient in "$@"; do 39 | > echo "$drink <- $ingredient" >> recipes 40 | > done 41 | > } 42 | 43 | Now let's create some empty files that need to exist but that aren't relevant 44 | to us. 45 | 46 | $ cat /dev/null > auto-begets 47 | $ cat /dev/null > auto-unbuyable 48 | 49 | And set up some basic rules: 50 | 51 | $ echo "lime zest" > unbuyable 52 | $ echo "lime -> lime juice" > begets 53 | $ echo "lime -> lime zest" >> begets 54 | $ echo "reposado tequila -> tequila" >> begets 55 | $ echo "blanco tequila -> tequila" >> begets 56 | $ echo "lime cordial, sugar, lime zest" > combinations 57 | 58 | Let's start with the simplest thing we can: nothing in our bar, one trivial 59 | recipe in our book. 60 | 61 | $ empty_bar 62 | $ add_recipe "shot of vodka" vodka 63 | $ runtest 64 | shopping list: 65 | vodka -> shot of vodka 66 | 67 | Okay! Nothing is mixable, but we learn that if we buy vodka we can mix up a 68 | delicious shot: 69 | 70 | $ buy vodka 71 | $ runtest 72 | mixable: shot of vodka 73 | 74 | Great. Now let's try a real cocktail: 75 | 76 | $ empty_recipe_book 77 | $ add_recipe "vodka martini" vodka "dry vermouth" 78 | $ runtest 79 | shopping list: 80 | dry vermouth -> vodka martini 81 | 82 | Okay. Since we already have vodka, all we need is dry vermouth. Let's buy it: 83 | 84 | $ buy "dry vermouth" 85 | $ runtest 86 | mixable: vodka martini 87 | 88 | Now we can mix a martini, and there are no further things that we should buy. 89 | 90 | Now let's make sure that "subtyping" works. 91 | 92 | $ empty_bar 93 | $ add_recipe margarita tequila "lime juice" "orange liqueur" 94 | $ buy "lime juice" 95 | $ buy "orange liqueur" 96 | $ runtest 97 | shopping list: 98 | blanco tequila -> margarita 99 | reposado tequila -> margarita 100 | tequila -> margarita 101 | 102 | Great. We can't make a margarita yet, but if we buy either generic or specific 103 | tequila, we'll be able to. 104 | 105 | $ buy "reposado tequila" 106 | $ runtest 107 | mixable: margarita 108 | 109 | Since we bought reposado tequila, there's no longer any reason to buy blanco 110 | tequila (according to our recipe book), so it goes away. 111 | 112 | Now let's test multi-ingredient syrups. 113 | 114 | $ empty_bar 115 | $ add_recipe gimlet gin "lime juice" "lime cordial" 116 | $ buy gin 117 | $ buy "lime juice" 118 | 119 | In order to make lime cordial, I need both limes and sugar. My lime *juice* 120 | won't do, because I need the zest. That's two new ingredients, so neither will 121 | show up on my shopping list. 122 | 123 | $ runtest 124 | shopping list: 125 | lime cordial -> gimlet 126 | 127 | But it does tell me that I can just buy lime cordial directly (if, you know, I 128 | could find it in a store). 129 | 130 | But once I buy sugar... 131 | 132 | $ buy sugar 133 | $ runtest 134 | shopping list: 135 | lime -> gimlet 136 | lime cordial -> gimlet 137 | 138 | It lets me know that I could either buy lime cordial directly, or I could just 139 | buy limes. 140 | 141 | In fact, now that I have sugar, I can get rid of my lime juice, because all I'll 142 | need to make a gimlet is lime. 143 | 144 | $ sell "lime juice" 145 | $ runtest 146 | shopping list: 147 | lime -> gimlet 148 | 149 | Nice. Notice that I can no longer just buy lime cordial, as that won't be 150 | sufficient: I can't make lime juice out of lime cordial. 151 | 152 | Now I suspect that there's a bug in my current implementation, so let's make a 153 | silly-looking test where a recipe uses both an ingredient and a potential output 154 | of that ingredient: 155 | 156 | $ empty_bar 157 | $ add_recipe "sour limes" "lime" "lime juice" 158 | $ runtest 159 | shopping list: 160 | lime -> sour limes 161 | 162 | And this did detect the bug! But now I've fixed it, and everything is fine. 163 | 164 | But I think I might have another bug. Does the Has relation work through 165 | arbitrarily long Begets production chains? 166 | 167 | $ empty_bar 168 | $ empty_recipe_book 169 | $ echo 'really really specific scotch -> really specific scotch' >>begets 170 | $ echo 'really specific scotch -> specific scotch' >>begets 171 | $ echo 'specific scotch -> scotch' >>begets 172 | $ add_recipe "shot of scotch" scotch 173 | $ runtest 174 | shopping list: 175 | really really specific scotch -> shot of scotch 176 | really specific scotch -> shot of scotch 177 | scotch -> shot of scotch 178 | specific scotch -> shot of scotch 179 | 180 | It didn't, the first time I tried this. But now I've fixed it. 181 | 182 | Now let's make sure that the Unbuyable relation is filtering things out 183 | correctly. 184 | 185 | $ empty_recipe_book 186 | $ add_recipe gimlet gin "lime juice" "lime cordial" 187 | $ buy "lime" "gin" 188 | $ runtest 189 | shopping list: 190 | lime cordial -> gimlet 191 | sugar -> gimlet 192 | 193 | Okay, but I can't find lime cordial in a store, so that line is kind of just 194 | noise to me. 195 | 196 | $ echo "lime cordial" >> unbuyable 197 | $ runtest 198 | shopping list: 199 | sugar -> gimlet 200 | 201 | There we go. But just because I can't usually buy it doesn't mean I can't use 202 | it to mix the drink, if I happen to have some on hand: 203 | 204 | $ buy "lime cordial" 205 | $ runtest 206 | mixable: gimlet 207 | 208 | So we can see that the "Unbuyable" relation only affects output of the 209 | "Enables" relation. 210 | --------------------------------------------------------------------------------