summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rw-r--r--Untitled.ipynb114
-rw-r--r--index.html360
-rw-r--r--recipes-fix.json2
-rw-r--r--recipes.js1304
-rw-r--r--script.js240
-rw-r--r--server.js39
7 files changed, 1996 insertions, 69 deletions
diff --git a/README.md b/README.md
index 804bf16..01d5122 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,7 @@
recipes.json from https://github.com/teijo/iba-cocktails.
+
+
+- check lp scores against python
+- roseacrucis
+- use strict
+- greedy + greedyscore
diff --git a/Untitled.ipynb b/Untitled.ipynb
index eca45d1..40be1aa 100644
--- a/Untitled.ipynb
+++ b/Untitled.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 185,
+ "execution_count": 208,
"metadata": {},
"outputs": [
{
@@ -69,43 +69,21 @@
},
{
"cell_type": "code",
- "execution_count": 186,
+ "execution_count": null,
"metadata": {},
"outputs": [],
- "source": [
- "makeStepR = lambda step, r: 'r' + str(step) + '_' + str(r)\n",
- "makeStepY = lambda step, y: 'y' + str(step) + '_' + str(y)\n",
- "\n",
- "f = open('lp.2', 'w')\n",
- "f.write('Minimize\\n')\n",
- "for step in range(n_ingredients):\n",
- " for r, recipe in enumerate(recipes):\n",
- " f.write(' - ' + makeStepR(step, r))\n",
- "f.write('\\nSubject To\\n')\n",
- "for step in range(n_ingredients):\n",
- " f.write(' + '.join(makeStepY(step, y) for y in range(n_ingredients)) + ' = ' + str(step + 1) + '\\n')\n",
- "for y in range(n_ingredients):\n",
- " for step in range(n_ingredients - 1):\n",
- " f.write(makeStepY(step+1, y) + ' - ' + makeStepY(step, y) + ' >= 0\\n')\n",
- "for r, recipe in enumerate(recipes):\n",
- " for step in range(n_ingredients):\n",
- " tot = ' - '.join(makeStepY(step, y) for y in recipe)\n",
- " f.write(makeStepR(step, r) + ' - ' + tot + ' >= -' + str(len(recipe) - 1) + '\\n')\n",
- " for y in recipe:\n",
- " f.write(makeStepR(step, r) + ' - ' + makeStepY(step, y) + ' <= 0\\n')\n",
- "f.write('Binary\\n')\n",
- "f.write(' '.join(makeStepY(step, y) \n",
- " for step in range(n_ingredients) for y in range(n_ingredients)))\n",
- "f.write(' ')\n",
- "f.write(' '.join(makeStepR(step, r) \n",
- " for step in range(n_ingredients) for r in range(len(recipes))))\n",
- "f.write('\\nEnd\\n')\n",
- "f.close()"
- ]
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
},
{
"cell_type": "code",
- "execution_count": 164,
+ "execution_count": 192,
"metadata": {},
"outputs": [
{
@@ -114,7 +92,7 @@
"(50, 50)"
]
},
- "execution_count": 164,
+ "execution_count": 192,
"metadata": {},
"output_type": "execute_result"
}
@@ -152,7 +130,7 @@
},
{
"cell_type": "code",
- "execution_count": 166,
+ "execution_count": 193,
"metadata": {},
"outputs": [
{
@@ -166,47 +144,47 @@
"5. Gin | Long Island Iced Tea, Gin Fizz, John Collins, White Lady\n",
"6. Cognac | Horse's Neck, Sidecar, Between the Sheets\n",
"7. Galliano | Yellow Bird, Golden Dream, Harvey Wallbanger\n",
- "8. Cherry liqueur | Aviation, Hemingway Special\n",
- "9. Grenadine | Bacardi, Tequila Sunrise, Mary Pickford\n",
+ "8. Grenadine | Bacardi, Tequila Sunrise\n",
+ "9. Cherry liqueur | Aviation, Mary Pickford, Hemingway Special\n",
"10. Bourbon | Whiskey Sour, Mint Julep\n",
"11. Champagne | Mimosa, French 75\n",
"12. Angostura bitters | Old Fashioned, Champagne Cocktail\n",
- "13. DiSaronno | God Mother, French Connection\n",
- "14. Dark rum | Planter's Punch, Dark 'n' Stormy\n",
- "15. Dry vermouth | Dirty Martini, Dry Martini\n",
+ "13. Dry vermouth | Dirty Martini, Dry Martini\n",
+ "14. DiSaronno | God Mother, French Connection\n",
+ "15. Dark rum | Planter's Punch, Dark 'n' Stormy\n",
"16. Coffee liqueur | Espresso Martini, Black Russian\n",
- "17. Créme de Cacao | Alexander\n",
- "18. Créme de Menthe | Grasshopper, Stinger\n",
- "19. Irish whiskey | Irish Coffee\n",
- "20. Peach bitters | Derby\n",
- "21. Créme de Cassis | Russian Spring Punch\n",
+ "17. Créme de Menthe | Stinger\n",
+ "18. Créme de Cacao | Grasshopper, Alexander\n",
+ "19. Pisco | Pisco Sour\n",
+ "20. Créme de Cassis | Russian Spring Punch\n",
+ "21. Dry white wine | Kir\n",
"22. Cachaca | Caipirinha\n",
- "23. Pisco | Pisco Sour\n",
- "24. Blackberry liqueur | Bramble\n",
- "25. Peach schnapps | Sex on the Beach\n",
- "26. Prosecco | Barracuda\n",
- "27. Red Port | Porto Flip\n",
+ "23. Peach bitters | Derby\n",
+ "24. Peach schnapps | Sex on the Beach\n",
+ "25. Absinthe | Monkey Gland\n",
+ "26. Apricot brandy | Paradise\n",
+ "27. Prosecco | Barracuda\n",
"28. Raspberry syrup | Clover Club\n",
- "29. Dry White Wine | Kir\n",
- "30. Sweet vermouth | \n",
- "31. Campari | Negroni, Americano\n",
- "32. Absinthe | Monkey Gland\n",
- "33. Peychaud's bitters | Sazerac\n",
- "34. Aperol | Spritz Veneziano\n",
- "35. Scotch | God Father\n",
- "36. Peach puree | Bellini\n",
- "37. Orange Bitters | Casino\n",
- "38. Lillet Blonde | Vesper\n",
+ "29. Campari | \n",
+ "30. Sweet vermouth | Negroni, Americano\n",
+ "31. Rye | Manhattan\n",
+ "32. Red Port | Porto Flip\n",
+ "33. Blackberry liqueur | Bramble\n",
+ "34. Irish whiskey | Irish Coffee\n",
+ "35. Peach puree | Bellini\n",
+ "36. Orgeat | Mai-tai\n",
+ "37. Scotch | God Father\n",
+ "38. Orange bitters | Casino\n",
"39. Drambuie | Rusty Nail\n",
- "40. DOM Bénédictine | Singapore Sling\n",
- "41. Cream liqueur | B52\n",
- "42. Apricot brandy | Paradise\n",
- "43. Calvados | Angel Face\n",
- "44. Raspberry liqueur | French Martini\n",
- "45. Rye | Manhattan\n",
+ "40. Peychaud's bitters | Sazerac\n",
+ "41. Calvados | Angel Face\n",
+ "42. Aperol | Spritz Veneziano\n",
+ "43. DOM Bénédictine | Singapore Sling\n",
+ "44. Cream liqueur | B52\n",
+ "45. Lillet Blonde | Vesper\n",
"46. Maraschino | Tuxedo\n",
- "47. Orgeat | Mai-tai\n",
- "48. Orange flower water | Ramos Fizz\n",
+ "47. Orange flower water | Ramos Fizz\n",
+ "48. Raspberry liqueur | French Martini\n",
"49. Strawberry syrup | \n",
"50. Kirsch | Rose\n"
]
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e19bb22
--- /dev/null
+++ b/index.html
@@ -0,0 +1,360 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Well-Ordered</title>
+ <meta charset="utf-8">
+ <style>
+body {
+ background: #fdf3f3;
+ color: DarkSlateGrey;
+ font-family: EBGaramond, serif;
+ font-size: large;
+}
+
+a {
+ color: #1a97bf;
+}
+
+a:hover {
+ color: #075d77;
+}
+
+.container {
+ margin: 1em;
+ max-width: 40em;
+}
+
+.row {
+ margin-bottom: 1em;
+}
+
+.title {
+ letter-spacing: -0.5px;
+}
+
+div {
+ margin-bottom: 10px;
+}
+
+
+form {
+ border: 1px DarkSlateGrey solid;
+ padding: 10px;
+ padding-left: 25px;
+}
+
+.sep {
+ margin-left: 6px;
+ margin-right: 6px;
+}
+
+.nonbreaking {
+ white-space: nowrap;
+}
+ .ingredient {
+ display: inline-block;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ }
+ button {
+ margin-right: 10px;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <h3>Well-Ordered</h3>
+ <noscript>Sorry, JavaScript is required to run Well-Ordered.</noscript>
+ <div id="form">
+ <form action="/" method="none">
+ <p>
+ Select any ingredients already owned or of negligible cost.
+ </p>
+ <div id="checkboxes">
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient0" x-ingredient="0">
+ <label for="ingredient0">Absinthe</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient1" x-ingredient="1">
+ <label for="ingredient1">Angostura bitters</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient2" x-ingredient="2">
+ <label for="ingredient2">Aperol</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient3" x-ingredient="3">
+ <label for="ingredient3">Apricot brandy</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient4" x-ingredient="4">
+ <label for="ingredient4">Blackberry liqueur</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient5" x-ingredient="5">
+ <label for="ingredient5">Bourbon</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient6" x-ingredient="6">
+ <label for="ingredient6">Cachaca</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient7" x-ingredient="7">
+ <label for="ingredient7">Calvados</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient8" x-ingredient="8">
+ <label for="ingredient8">Campari</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient9" x-ingredient="9">
+ <label for="ingredient9">Champagne</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient10" x-ingredient="10">
+ <label for="ingredient10">Cherry liqueur</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient11" x-ingredient="11" checked>
+ <label for="ingredient11">Coconut milk</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient12" x-ingredient="12">
+ <label for="ingredient12">Coffee liqueur</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient13" x-ingredient="13">
+ <label for="ingredient13">Cognac</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient14" x-ingredient="14" checked>
+ <label for="ingredient14">Cola</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient15" x-ingredient="15" checked>
+ <label for="ingredient15">Cranberry juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient16" x-ingredient="16" checked>
+ <label for="ingredient16">Cream</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient17" x-ingredient="17">
+ <label for="ingredient17">Cream liqueur</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient18" x-ingredient="18">
+ <label for="ingredient18">Créme de Cacao</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient19" x-ingredient="19">
+ <label for="ingredient19">Créme de Cassis</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient20" x-ingredient="20">
+ <label for="ingredient20">Créme de Menthe</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient21" x-ingredient="21">
+ <label for="ingredient21">DOM Bénédictine</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient22" x-ingredient="22">
+ <label for="ingredient22">Dark rum</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient23" x-ingredient="23">
+ <label for="ingredient23">DiSaronno</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient24" x-ingredient="24">
+ <label for="ingredient24">Drambuie</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient25" x-ingredient="25">
+ <label for="ingredient25">Dry vermouth</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient26" x-ingredient="26">
+ <label for="ingredient26">Dry white wine</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient27" x-ingredient="27" checked>
+ <label for="ingredient27">Egg yolk</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient28" x-ingredient="28">
+ <label for="ingredient28">Galliano</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient29" x-ingredient="29">
+ <label for="ingredient29">Gin</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient30" x-ingredient="30" checked>
+ <label for="ingredient30">Ginger ale</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient31" x-ingredient="31" checked>
+ <label for="ingredient31">Ginger beer</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient32" x-ingredient="32" checked>
+ <label for="ingredient32">Grapefruit juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient33" x-ingredient="33">
+ <label for="ingredient33">Grenadine</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient34" x-ingredient="34" checked>
+ <label for="ingredient34">Hot coffee</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient35" x-ingredient="35">
+ <label for="ingredient35">Irish whiskey</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient36" x-ingredient="36">
+ <label for="ingredient36">Kirsch</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient37" x-ingredient="37" checked>
+ <label for="ingredient37">Lemon juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient38" x-ingredient="38">
+ <label for="ingredient38">Lillet Blonde</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient39" x-ingredient="39" checked>
+ <label for="ingredient39">Lime juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient40" x-ingredient="40">
+ <label for="ingredient40">Maraschino</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient41" x-ingredient="41" checked>
+ <label for="ingredient41">Olive juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient42" x-ingredient="42">
+ <label for="ingredient42">Orange bitters</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient43" x-ingredient="43">
+ <label for="ingredient43">Orange flower water</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient44" x-ingredient="44" checked>
+ <label for="ingredient44">Orange juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient45" x-ingredient="45">
+ <label for="ingredient45">Orgeat</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient46" x-ingredient="46">
+ <label for="ingredient46">Peach bitters</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient47" x-ingredient="47">
+ <label for="ingredient47">Peach puree</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient48" x-ingredient="48">
+ <label for="ingredient48">Peach schnapps</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient49" x-ingredient="49">
+ <label for="ingredient49">Peychaud's bitters</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient50" x-ingredient="50" checked>
+ <label for="ingredient50">Pineapple juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient51" x-ingredient="51">
+ <label for="ingredient51">Pisco</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient52" x-ingredient="52">
+ <label for="ingredient52">Prosecco</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient53" x-ingredient="53">
+ <label for="ingredient53">Raspberry liqueur</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient54" x-ingredient="54">
+ <label for="ingredient54">Raspberry syrup</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient55" x-ingredient="55">
+ <label for="ingredient55">Red Port</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient56" x-ingredient="56">
+ <label for="ingredient56">Rye</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient57" x-ingredient="57">
+ <label for="ingredient57">Scotch</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient58" x-ingredient="58" checked>
+ <label for="ingredient58">Soda water</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient59" x-ingredient="59">
+ <label for="ingredient59">Strawberry syrup</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient60" x-ingredient="60">
+ <label for="ingredient60">Sweet vermouth</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient61" x-ingredient="61" checked>
+ <label for="ingredient61">Syrup</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient62" x-ingredient="62">
+ <label for="ingredient62">Tequila</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient63" x-ingredient="63" checked>
+ <label for="ingredient63">Tomato juice</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient64" x-ingredient="64">
+ <label for="ingredient64">Triple Sec</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient65" x-ingredient="65">
+ <label for="ingredient65">Vodka</label>
+ </div>
+ <div class="ingredient">
+ <input type="checkbox" id="ingredient66" x-ingredient="66">
+ <label for="ingredient66">White rum</label>
+ </div>
+ </div>
+ <div>
+ <label for="limit">Time limit in seconds:</label>
+ <input id="limit" name="limit" type="number" value="120" min="10"></input>
+ </div>
+ <div>
+ <button type="submit" id="optimal">Compute optimal (slow)</button>
+ <button type="button" id="greedy">Compute greedy (fast)</button>
+ </div>
+ </form>
+ </div>
+ <div id="waiting"></div>
+ <div id="solution"></div>
+ </div>
+ <script src="./node_modules/glpk.js/glpk.js"></script>
+ <script src="./recipes.js"></script>
+ <script src="./script.js"></script>
+ </body>
+</html>
diff --git a/recipes-fix.json b/recipes-fix.json
index e4779d0..fedad5c 100644
--- a/recipes-fix.json
+++ b/recipes-fix.json
@@ -125,7 +125,7 @@
"ingredient": "Cognac" },
{ "unit": "cl",
"amount": 12,
- "ingredient": "Ginger Ale" },
+ "ingredient": "Ginger ale" },
{ "special": "Dash of Angostura bitters (optional)" }
],
"garnish": "Lemon twist",
diff --git a/recipes.js b/recipes.js
new file mode 100644
index 0000000..e9b5620
--- /dev/null
+++ b/recipes.js
@@ -0,0 +1,1304 @@
+allRecipes = [
+ { "name": "Vesper",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 0.75,
+ "ingredient": "Lillet Blonde" }
+ ],
+ "garnish": "Lemon twist",
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "Bacardi",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "White rum",
+ "label": "Bacardi White Rum" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Lime juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Grenadine",
+ "label": "Grenadine" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "Negroni",
+ "glass": "old-fashioned",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Campari" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Sweet vermouth",
+ "label": "Sweet red vermouth" }
+ ],
+ "garnish": "Half an orange slice",
+ "preparation": "Build into old-fashioned glass filled with ice. Stir gently." },
+ { "name": "Rose",
+ "glass": "martini",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Kirsch" },
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Dry vermouth",
+ "label": "Dry vermouth" },
+ { "ingredient": "Strawberry syrup" },
+ { "special": "3 dashes Strawberry syrup" }
+ ],
+ "preparation": "Stir all ingredients with ice and strain into a cocktail glass." },
+ { "name": "Old Fashioned",
+ "glass": "old-fashioned",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Bourbon",
+ "label": "Bourbon or rye whiskey" },
+ { "ingredient": "Angostura bitters" },
+ { "special": "2 dashes Angostura Bitters" },
+ { "special": "1 sugar cube" },
+ { "special": "Few dashes plain water" }
+ ],
+ "garnish": "Orange slice and cherry",
+ "preparation": "Place sugar cube in old-fashioned glass and saturate with bitters, add a dash of plain water. Muddle until dissolve. Fill the glass with ice cubes and add whisky." },
+]/*
+ { "name": "Tuxedo",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Gin",
+ "label": "Old Tom Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Dry vermouth",
+ "label": "Dry vermouth" },
+ { "ingredient": "Maraschino" },
+ { "ingredient": "Absinthe" },
+ { "ingredient": "Orange bitters" },
+ { "special": "1/2 bar spoon Maraschino" },
+ { "special": "1/4 bar spoon Absinthe" },
+ { "special": "3 dashes Orange Bitters" }
+ ],
+ "garnish": "Cherry and lemon twist",
+ "preparation": "Stir all ingredients with ice and strain into cocktail glass." },
+ { "name": "Mojito",
+ "glass": "collins",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "White rum",
+ "label": "White Cuban Rum" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Lime juice" },
+ { "special": "6 Mint sprigs" },
+ { "special": "2 teaspoons white sugar" },
+ { "special": "Soda water" }
+ ],
+ "garnish": "Mint leaves and lemon slice",
+ "preparation": "Muddle mint sprigs with sugar and lime juice. Add splash of soda water and fill glass with cracked ice. Pour rum and top with soda water. Serve with straw." },
+ { "name": "Horse's Neck",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 12,
+ "ingredient": "Ginger ale" },
+ { "special": "Dash of Angostura bitters (optional)" }
+ ],
+ "garnish": "Lemon twist",
+ "preparation": "Build into highball glass with ice cubes. Stir gently. If required, add dashes of Angostura bitters." },
+ { "name": "Planter's Punch",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Dark rum" },
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Orange juice" },
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Pineapple juice" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Grenadine",
+ "label": "Grenadine" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" },
+ { "ingredient": "Angostura bitters" },
+ { "special": "3 to 4 dashes Angostura bitters" }
+ ],
+ "garnish": "Pineapple slice and a cherry",
+ "preparation": "Pour all ingredients, except the bitters, into shaker filled with ice. Shake. Pour into large glass, filled with ice. Add Angostura bitters, “on top”." },
+ { "name": "Sea Breeze",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 12,
+ "ingredient": "Cranberry juice" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Grapefruit juice" }
+ ],
+ "garnish": "Lime wedge",
+ "preparation": "Build all ingredients in a rock glass filled with ice." },
+ { "name": "Pisco Sour",
+ "glass": "old-fashioned",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Pisco" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" },
+ { "special": "1 raw egg white (small egg)" }
+ ],
+ "preparation": "Shake and strain into a chilled champagne flute. Dash some Angostura bitters on top." },
+ { "name": "Long Island Iced Tea",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Tequila" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 2.5,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 3.0,
+ "ingredient": "Syrup",
+ "label": "Gomme syrup" },
+ { "special": "1 dash of Cola" }
+ ],
+ "garnish": "Lemon twist",
+ "preparation": "Add all ingredients into highball glass filled with ice. Stir gently. Serve with straw." },
+ { "name": "Clover Club",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Raspberry syrup",
+ "label": "Raspberry syrup" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" },
+ { "special": "Few drops of Egg White" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into cocktail glass." },
+ { "name": "Angel Face",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Apricot brandy" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Calvados" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into a cocktail glass." },
+ { "name": "Mimosa",
+ "glass": "champagne-flute",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 7.5,
+ "ingredient": "Champagne" },
+ { "unit": "cl",
+ "amount": 7.5,
+ "ingredient": "Orange juice" }
+ ],
+ "garnish": "Optional orange twist",
+ "preparation": "Pour orange juice into flute and gently pour Champagne. Stir gently. Note: Buck's Fizz is a very similar cocktail but made of two parts champagne to one part orange juice." },
+ { "name": "Whiskey Sour",
+ "glass": "old-fashioned",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Bourbon",
+ "label": "Bourbon whiskey" },
+ { "unit": "cl",
+ "amount": 3.0,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" }
+ ],
+ "garnish": "Half an orange slice and cherry",
+ "preparation": "Dash egg white (Optional: if used shake little harder to foam up the egg white). Pour all ingredients into cocktail shaker filled with ice. Shake. Strain into cocktail glass. If served ‘On the rocks’, strain ingredients into old-fashioned glass filled with ice." },
+ { "name": "Screwdriver",
+ "glass": "highball",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 10,
+ "ingredient": "Orange juice" }
+ ],
+ "garnish": "Orange slice",
+ "preparation": "Build into a highball glass filled with ice. Stir gently." },
+ { "name": "Cuba Libre",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 12,
+ "ingredient": "Cola" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Lime juice" }
+ ],
+ "garnish": "Lime wedge",
+ "preparation": "Build all ingredients in a highball glass filled with ice." },
+ { "name": "Manhattan",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Rye",
+ "label": "Rye whiskey" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Sweet vermouth",
+ "label": "Red vermouth" },
+ { "ingredient": "Angostura bitters" },
+ { "special": "1 dash Angostura Bitters" }
+ ],
+ "garnish": "Cherry",
+ "preparation": "Stir in mixing glass with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "Porto Flip",
+ "glass": "martini",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Red Port" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Egg yolk" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into cocktail glass. Sprinkle with fresh ground nutmeg." },
+ { "name": "Gin Fizz",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" },
+ { "unit": "cl",
+ "amount": 8,
+ "ingredient": "Soda water" }
+ ],
+ "garnish": "Lemon slice",
+ "preparation": "Shake all ingredients with ice cubes, except soda water. Pour into tumbler. Top with soda water." },
+ { "name": "Espresso Martini",
+ "glass": "martini",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Coffee liqueur",
+ "label": "Kahlúa" },
+ { "special": "Sugar syrup (according to individual preference of sweetness)" },
+ { "special": "1 short strong Espresso" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "Margarita",
+ "glass": "margarita",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Tequila" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Triple Sec",
+ "label": "Cointreau" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into cocktail glass rimmed with salt (note:Fruit Margarita - blend selected fruit with the above recipe)." },
+ { "name": "French 75",
+ "glass": "champagne-tulip",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" },
+ { "special": "2 dashes Sugar syrup" },
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Champagne" }
+ ],
+ "preparation": "Shake with ice cubes, except for champagne. Strain into a champagne flute. Top up with champagne. Stir gently." },
+ { "name": "Yellow Bird",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Galliano" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "Pina Colada",
+ "glass": "hurricane",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Pineapple juice" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Coconut milk" }
+ ],
+ "garnish": "Pineapple slice and a cherry",
+ "preparation": "Blend all the ingredients with ice in a electric blender, pour into a large goblet or Hurricane glass and serve with straws." },
+ { "name": "Aviation",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Cherry liqueur",
+ "label": "Maraschino" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "Bellini",
+ "glass": "champagne-flute",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 10,
+ "ingredient": "Prosecco" },
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Peach puree" }
+ ],
+ "preparation": "Pour peach puree into chilled glass and add sparkling wine. Stir gently. Variations: Puccini (fresh mandarin juice), Rossini (fresh strawberry puree), Tintoretto (fresh pomegranate juice)" },
+ { "name": "Grasshopper",
+ "glass": "martini",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Créme de Cacao",
+ "label": "White Créme de Cacao" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Créme de Menthe",
+ "label": "Green Créme de Menthe" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Cream" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "Tequila Sunrise",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Tequila" },
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Orange juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Grenadine",
+ "label": "Grenadine" }
+ ],
+ "garnish": "Orange slice and a cherry",
+ "preparation": "Build tequila and orange juice into highball with ice cubes. Add a splash of grenadine to create sunrise effect. Do not stir." },
+ { "name": "Daiquiri",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 2.5,
+ "ingredient": "Lime juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Syrup",
+ "label": "Simple syrup" }
+ ],
+ "preparation": "Shake and strain into a cocktail glass." },
+ { "name": "Rusty Nail",
+ "glass": "old-fashioned",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Scotch",
+ "label": "Scotch whisky" },
+ { "unit": "cl",
+ "amount": 2.5,
+ "ingredient": "Drambuie" }
+ ],
+ "garnish": "Lemon twist",
+ "preparation": "Build into old-fashioned glass filled with ice. Stir gently." },
+ { "name": "B52",
+ "glass": "shot",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Coffee liqueur",
+ "label": "Kahlúa" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Cream liqueur",
+ "label": "Baileys Irish Cream" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Triple Sec",
+ "label": "Grand Marnier" }
+ ],
+ "preparation": "Layer ingredients one at a time starting with Kahlúa, followed by Baileys Irish Cream and top with Grand Marnier. Flame the Grand Marnier, serve while the flame is still on, accompanied with a straw on side plate." },
+ { "name": "Stinger",
+ "glass": "martini",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Créme de Menthe",
+ "label": "White Créme de Menthe" }
+ ],
+ "preparation": "Stir in mixing glass with ice cubes. Strain into a cocktail glass." },
+ { "name": "Golden Dream",
+ "glass": "martini",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Galliano" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Orange juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Cream" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "God Mother",
+ "glass": "old-fashioned",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "DiSaronno" }
+ ],
+ "preparation": "Build into old fashioned glass filled with ice cubes. Stir gently." },
+ { "name": "Spritz Veneziano",
+ "glass": "old-fashioned",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Prosecco" },
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Aperol" },
+ { "special": "Splash of Soda water" }
+ ],
+ "garnish": "Half an orange slice",
+ "preparation": "Build into an old-fashioned glass filled with ice. Top with a splash of soda water." },
+ { "name": "Bramble",
+ "glass": "old-fashioned",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Blackberry liqueur" }
+ ],
+ "garnish": "Lemon slice and two blackberries",
+ "preparation": "Build over crushed ice, in a rock glass. Stir, then pour the blackberry liqueur over the top of the drink in a circular fashion." },
+ { "name": "Alexander",
+ "glass": "martini",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Créme de Cacao",
+ "label": "Brown Créme de Cacao" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Cream" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass. Sprinkle with fresh ground nutmeg." },
+ { "name": "Lemon Drop Martini",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 2.5,
+ "ingredient": "Vodka",
+ "label": "Citron Vodka" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" }
+ ],
+ "garnish": "Lemon slice",
+ "preparation": "Shake and strain into a chilled cocktail glass rimmed with sugar." },
+ { "name": "French Martini",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Raspberry liqueur" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Pineapple juice" }
+ ],
+ "preparation": "Stir in mixing glass with ice cubes. Strain into chilled cocktail glass. Squeeze oil from lemon peel onto the drink." },
+ { "name": "Black Russian",
+ "glass": "old-fashioned",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Coffee liqueur" }
+ ],
+ "preparation": "Build into old fashioned glass filled with ice cubes. Stir gently. Note: for White Russian, float fresh cream on the top and stir gently." },
+ { "name": "Bloody Mary",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Tomato juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" },
+ { "special": "2 to 3 dashes of Worcestershire Sauce" },
+ { "special": "Tabasco" },
+ { "special": "Celery salt" },
+ { "special": "Pepper" }
+ ],
+ "garnish": "Celery and optionally lemon wedge",
+ "preparation": "Stir gently, pour all ingredients into highball glass." },
+ { "name": "Mai-tai",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Dark rum" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Triple Sec",
+ "label": "Orange Curaçao" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Orgeat",
+ "label": "Orgeat syrup" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Lime juice" }
+ ],
+ "garnish": "Pineapple spear, mint leaves and lime wedge",
+ "preparation": "Shake and strain into highball glass. Serve with straw." },
+ { "name": "Barracuda",
+ "glass": "margarita",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Dark rum",
+ "label": "Gold rum" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Galliano" },
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Pineapple juice" },
+ { "ingredient": "Prosecco" },
+ { "special": "1 dash Lime juice" },
+ { "special": "Top with Prosecco" }
+ ] },
+ { "name": "Sex on the Beach",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Peach schnapps" },
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Cranberry juice" },
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Orange juice" }
+ ],
+ "garnish": "Orange slice",
+ "preparation": "Build all ingredients in a highball glass filled with ice." },
+ { "name": "Monkey Gland",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Orange juice" },
+ { "ingredient": "Absinthe" },
+ { "ingredient": "Grenadine" },
+ { "special": "2 drops Absinthe" },
+ { "special": "2 drops Grenadine" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "Derby",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Gin" },
+ { "ingredient": "Peach bitters" },
+ { "special": "2 drops Peach Bitters" },
+ { "special": "2 Fresh mint leaves" }
+ ],
+ "garnish": "Mint leaves",
+ "preparation": "Stir in mixing glass with ice cubes. Strain into a cocktail glass." },
+ { "name": "Sidecar",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Lemon juice" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into cocktail glass." },
+ { "name": "Irish Coffee",
+ "glass": "hot-drink",
+ "category": "Hot Drink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Irish whiskey",
+ "label": "Irish whiskey" },
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Hot coffee" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Cream" },
+ { "special": "1 teaspoon of brown sugar" }
+ ],
+ "preparation": "Warm the Irish whiskey over a burner. Pour into the glass (for hot drink) hot coffee, and add a teaspoon of sugar. Float Cream on top." },
+ { "name": "Sazerac",
+ "glass": "old-fashioned",
+ "category": "After Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Absinthe" },
+ { "ingredient": "Peychaud's bitters" },
+ { "special": "1 sugar cube" },
+ { "special": "2 dashes Peychaud’s bitters" }
+ ],
+ "garnish": "Lemon twist",
+ "preparation": "Rinse a chilled old-fashioned glass with the absinthe, add crushed ice and set it aside. Stir the remaining ingredients over ice and set it aside. Discard the ice and any excess absinthe from the prepared glass, and strain the drink into the glass. Note: The original recipe changed after the American Civil War, rye whiskey substituted cognac as it became hard to obtain." },
+ { "name": "Americano",
+ "glass": "old-fashioned",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Campari" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Sweet vermouth",
+ "label": "Red vermouth" },
+ { "special": "A splash of soda water" }
+ ],
+ "garnish": "Half an orange slice",
+ "preparation": "Build into old fashioned glass filled with ice cubes. Add a splash of soda water." },
+ { "name": "Singapore Sling",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Cherry liqueur" },
+ { "unit": "cl",
+ "amount": 0.75,
+ "ingredient": "Triple Sec",
+ "label": "Cointreau" },
+ { "unit": "cl",
+ "amount": 0.75,
+ "ingredient": "DOM Bénédictine" },
+ { "unit": "cl",
+ "amount": 12.0,
+ "ingredient": "Pineapple juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Grenadine",
+ "label": "Grenadine" },
+ { "ingredient": "Angostura bitters" },
+ { "special": "1 dash Angostura bitters" }
+ ],
+ "garnish": "Pineapple slice and a cherry",
+ "preparation": "Shake with ice cubes. Strain into highball glass." },
+ { "name": "French Connection",
+ "glass": "old-fashioned",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "DiSaronno" }
+ ],
+ "preparation": "Build into old fashioned glass filled with ice cubes. Stir gently." },
+ { "name": "Moscow Mule",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 12,
+ "ingredient": "Ginger beer" },
+ { "unit": "cl",
+ "amount": 0.5,
+ "ingredient": "Lime juice" },
+ { "special": "1 slice lime in a highball glass" }
+ ],
+ "garnish": "Lime slice",
+ "preparation": "Combine the vodka and ginger beer. Add lime juice." },
+ { "name": "John Collins",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" },
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Soda water" }
+ ],
+ "garnish": "Lemon slice and a cherry",
+ "preparation": "Build into highball glass filled with ice. Stir gently. Add a dash of Angostura bitters. (Note: Use Old Tom Gin for Tom Collins)" },
+ { "name": "Kir",
+ "glass": "white-wine",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Dry white wine" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Créme de Cassis",
+ "label": "Créme de Cassis" }
+ ],
+ "preparation": "Pour Créme de Cassis into glass, top up with white wine. For Kir Royal: Use champagne instead of white wine." },
+ { "name": "Mint Julep",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Bourbon",
+ "label": "Bourbon whiskey" },
+ { "special": "4 fresh mint sprigs" },
+ { "special": "1 teaspoon powdered sugar" },
+ { "special": "2 teaspoons water" }
+ ],
+ "garnish": "Mint sprig",
+ "preparation": "In a highball glass gently muddle the mint, sugar and water. Fill the glass with cracked ice, add Bourbon and stir well until the glass is frost." },
+ { "name": "Tommy's Margarita",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Tequila" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" },
+ { "special": "2 bar spoons of Agave nectar" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "Paradise",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Apricot brandy" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Orange juice" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "Dirty Martini",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Dry vermouth",
+ "label": "Dry vermouth" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Olive juice" }
+ ],
+ "garnish": "Green olive",
+ "preparation": "Stir in mixing glass with ice cubes. Strain into chilled martini glass." },
+ { "name": "Champagne Cocktail",
+ "glass": "champagne-flute",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Champagne" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Cognac" },
+ { "ingredient": "Angostura bitters" },
+ { "special": "2 dashes Angostura Bitters" },
+ { "special": "1 sugar cube" }
+ ],
+ "garnish": "Orange slice and a cherry",
+ "preparation": "Add dash of Angostura bitter onto sugar cube and drop it into champagne flute. Add cognac followed by pouring gently chilled champagne." },
+ { "name": "Mary Pickford",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Cherry liqueur",
+ "label": "Maraschino" },
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Pineapple juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Grenadine",
+ "label": "Grenadine" }
+ ],
+ "preparation": "Shake and strain into a chilled large cocktail glass." },
+ { "name": "Hemingway Special",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Grapefruit juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Cherry liqueur",
+ "label": "Maraschino" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into a double cocktail glass." },
+ { "name": "Dark 'n' Stormy",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Dark rum" },
+ { "unit": "cl",
+ "amount": 10,
+ "ingredient": "Ginger beer" }
+ ],
+ "garnish": "Lime wedge",
+ "preparation": "Build into highball glass filled with ice. Add rum first and top it with ginger beer." },
+ { "name": "Ramos Fizz",
+ "glass": "highball",
+ "category": "Longdrink",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" },
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Cream" },
+ { "ingredient": "Orange flower water" },
+ { "special": "1 Egg white" },
+ { "special": "3 dashes Orange flower water" },
+ { "special": "2 drops Vanilla extract" },
+ { "special": "Soda water" }
+ ],
+ "preparation": "Pour all ingredients (except soda) in a mixing glass, dry shake (no ice) for two minutes, add ice and hard shake for another minute. Strain into a highball glass without ice, top with soda." },
+ { "name": "Russian Spring Punch",
+ "glass": "highball",
+ "category": "Sparkling Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 2.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 2.5,
+ "ingredient": "Lemon juice" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Créme de Cassis",
+ "label": "Créme de Cassis" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Syrup",
+ "label": "Sugar syrup" }
+ ],
+ "garnish": "Lemon slice and a blackberry",
+ "preparation": "Shake the ingredients and pour into highball glass. Top with Sparkling wine." },
+ { "name": "God Father",
+ "glass": "old-fashioned",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "Scotch",
+ "label": "Scotch whisky" },
+ { "unit": "cl",
+ "amount": 3.5,
+ "ingredient": "DiSaronno" }
+ ],
+ "preparation": "Build into old fashioned glass filled with ice cubes. Stir gently." },
+ { "name": "Cosmopolitan",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Vodka",
+ "label": "Citron Vodka" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Triple Sec",
+ "label": "Cointreau" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Lime juice" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Cranberry juice" }
+ ],
+ "garnish": "Lime slice",
+ "preparation": "Shake with ice cubes. Strain into a large cocktail glass." },
+ { "name": "Dry Martini",
+ "glass": "martini",
+ "category": "Before Dinner Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 6,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Dry vermouth",
+ "label": "Dry vermouth" }
+ ],
+ "preparation": "Stir in mixing glass with ice cubes. Strain into chilled martini glass. Squeeze oil from lemon peel onto the drink, or garnish with olive." },
+ { "name": "Between the Sheets",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "White rum" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Cognac" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Lemon juice" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "Casino",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Gin",
+ "label": "Old Tom Gin" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Cherry liqueur",
+ "label": "Maraschino" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Orange bitters" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Lemon juice" }
+ ],
+ "garnish": "Lemon twist and a cherry",
+ "preparation": "Shake with ice cubes. Strain into chilled cocktail glass." },
+ { "name": "Caipirinha",
+ "glass": "old-fashioned",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Cachaca" },
+ { "special": "half fresh lime cut into 4 wedges" },
+ { "special": "2 teaspoon sugar" }
+ ],
+ "preparation": "Place lime and sugar in old fashion glass and muddle. Fill glass with ice and Cachaca (note:Caipiroska- use Vodka instead of Cachaca)." },
+ { "name": "Vampiro",
+ "glass": "highball",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 5,
+ "ingredient": "Tequila",
+ "label": "Silver Tequila" },
+ { "unit": "cl",
+ "amount": 7,
+ "ingredient": "Tomato juice" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Orange juice" },
+ { "unit": "cl",
+ "amount": 1,
+ "ingredient": "Lime juice" },
+ { "special": "1 teaspoon clear honey" },
+ { "special": "Half slice onion finely chopped" },
+ { "special": "Few slices fresh red hot chili peppers" },
+ { "special": "Few drops Worcestershire sauce" },
+ { "special": "Salt" }
+ ],
+ "garnish": "Lime wedge and a green or red chili",
+ "preparation": "Shake with ice cubes. Strain into a highball glass, filled with ice." },
+ { "name": "Kamikaze",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Lime juice" }
+ ],
+ "preparation": "Shake and strain into a chilled cocktail glass." },
+ { "name": "White Lady",
+ "glass": "martini",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4,
+ "ingredient": "Gin" },
+ { "unit": "cl",
+ "amount": 3,
+ "ingredient": "Triple Sec" },
+ { "unit": "cl",
+ "amount": 2,
+ "ingredient": "Lemon juice" }
+ ],
+ "preparation": "Shake with ice cubes. Strain into large cocktail glass." },
+ { "name": "Harvey Wallbanger",
+ "glass": "highball",
+ "category": "All Day Cocktail",
+ "ingredients": [
+ { "unit": "cl",
+ "amount": 4.5,
+ "ingredient": "Vodka" },
+ { "unit": "cl",
+ "amount": 1.5,
+ "ingredient": "Galliano" },
+ { "unit": "cl",
+ "amount": 9,
+ "ingredient": "Orange juice" }
+ ],
+ "garnish": "Orance slice and a cherry",
+ "preparation": "Build vodka and orange juice into a highball glass filled with ice. Stir gently and float Galliano on top." }
+]
+*/
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..ba26dac
--- /dev/null
+++ b/script.js
@@ -0,0 +1,240 @@
+"use strict";
+
+var ingredientsm = {}
+var allIngredients = []
+for (let recipe of allRecipes) {
+ for (let ingredient of recipe.ingredients) {
+ if (ingredient.special) {
+ continue
+ }
+ if (!ingredientsm[ingredient.ingredient]) {
+ allIngredients.push(ingredient.ingredient)
+ ingredientsm[ingredient.ingredient] = 1
+ }
+ }
+}
+allIngredients.sort()
+var ingredientsLookup = {}
+for (let [idx, ingredient] of allIngredients.entries()) {
+ ingredientsLookup[ingredient] = idx
+}
+
+var globret = undefined;
+
+window.addEventListener('load', function(evt) {
+ glpkPromise.then(function(glpk) {
+
+ const worker = new Worker('./node_modules/glpk.js/glpk-worker.js')
+ worker.onerror = (err) => { console.log(err) }
+ worker.onmessage = (evt) => {
+ if (evt.data.initialized) {}
+ if (evt.data.result) {
+ hide(document.getElementById('waiting'))
+ show(document.getElementById('solution'))
+ renderSolution(glpk, evt.data, globret)
+ }
+ }
+
+ document.getElementById('optimal').addEventListener('click', function(evt) {
+ evt.preventDefault()
+ window.globret = constructILP(glpk)
+ solveILP(worker, globret)
+ })
+ })
+})
+
+
+function renderSolution(glpk, sol, ret) {
+ let ingredients = ret.ingredients
+ let recipes = JSON.parse(JSON.stringify(ret.recipes))
+ let res = []
+ let batches = []
+ let origbatch = []
+ for (let [r, recipe] of recipes.entries()) {
+ if (recipe.length == 0) {
+ origbatch.push(r)
+ }
+ }
+ for (let [varname, on] of Object.entries(sol.result.vars)) {
+ if (!on) { continue }
+ if (!varname.startsWith('y')) { continue }
+ let y = Number.parseInt(varname.split('_')[1])
+ if (res.indexOf(y) != -1) { continue }
+ res.push(y)
+ let batch = []
+ for (let [r, recipe] of recipes.entries()) {
+ if (recipe.length == 1 & recipe[0] == y) {
+ batch.push(r)
+ }
+ }
+ for (let [r, recipe] of recipes.entries()) {
+ recipes[r] = recipe.filter(it => it != y)
+ }
+ batches.push(batch)
+ }
+ let ol = document.createElement('ol')
+
+ let li = document.createElement('li')
+ li.appendChild(document.createTextNode('On-hand'))
+ if (origbatch.length) {
+ let ul = document.createElement('ul')
+ drinks = origbatch.map(b => allRecipes[b].name).join(', ')
+ let sli = document.createElement('li')
+ sli.appendChild(document.createTextNode(drinks))
+ ul.appendChild(sli)
+ li.appendChild(ul)
+ }
+ ol.appendChild(li)
+
+ for (let [idx, y] of res.entries()) {
+ let batch = batches[idx]
+ let li = document.createElement('li')
+ li.appendChild(document.createTextNode(ingredients[y]))
+ if (batch.length) {
+ let ul = document.createElement('ul')
+ let drinks = batch.map(b => allRecipes[b].name).join(', ')
+ let sli = document.createElement('li')
+ sli.appendChild(document.createTextNode(drinks))
+ ul.appendChild(sli)
+ li.appendChild(ul)
+ }
+ ol.appendChild(li)
+ }
+ document.getElementById('solution').innerHTML = ''
+ let desc = document.createElement('p')
+ if (sol.result.status == glpk.GLP_UNDEF) {
+ desc.appendChild(document.createTextNode('Error: Failed to find feasible solution. Try increasing time limit.'))
+ } else if (sol.result.status == glpk.GLP_FEAS) {
+ desc.appendChild(document.createTextNode(`Found feasible solution of score ${-sol.result.z}.`))
+ } else if (sol.result.status == glpk.GLP_INFEAS) {
+ desc.appendChild(document.createTextNode('Error: Did not find feasible solution.'))
+ } else if (sol.result.status == glpk.GLP_NOFEAS) {
+ desc.appendChild(document.createTextNode('Error: Problem has no feasible solution.'))
+ } else if (sol.result.status == glpk.GLP_OPT) {
+ desc.appendChild(document.createTextNode(`Found optimal solution of score ${-sol.result.z}.`))
+ } else if (sol.result.status == glpk.GLP_UNBND) {
+ desc.appendChild(document.createTextNode('Error: Problem is unbounded.'))
+ }
+ document.getElementById('solution').appendChild(desc)
+ if (sol.result.status == glpk.GLP_FEAS || sol.result.status == glpk.GLP_OPT) {
+ document.getElementById('solution').appendChild(ol)
+ }
+}
+
+let hide = e => e.style.display = 'none';
+let show = e => e.style.display = 'inline-block';
+
+function solveILP(worker, ret) {
+ let seconds = Number.parseInt(document.getElementById('limit').value)
+ document.getElementById('waiting').innerHTML = seconds + ' seconds...'
+ hide(document.getElementById('solution'))
+ show(document.getElementById('waiting'))
+ let incrSeconds = () => {
+ document.getElementById('waiting').innerHTML = seconds + ' seconds...'
+ seconds--
+ }
+ setInterval(incrSeconds, 1000)
+ worker.postMessage(ret.lp)
+}
+
+function constructILP(glpk) {
+ let skip = Object.values(document.querySelector('form')).reduce((obj, field) => {
+ if (field.getAttribute('x-ingredient') && field.checked) {
+ obj[field.getAttribute('x-ingredient')] = 1
+ }
+ return obj
+ }, {})
+
+ let ingredientsset = {}
+ let recipes = []
+
+ for (let recipe of allRecipes) {
+ for (let ingredient of recipe.ingredients) {
+ if (ingredient.special) { continue }
+ if (skip[ingredientsLookup[ingredient.ingredient]]) { continue }
+ ingredientsset[ingredient.ingredient] = 1
+ }
+ }
+ let ingredients = Object.keys(ingredientsset)
+
+ for (let recipe of allRecipes) {
+ let x = []
+ for (let ingredient of recipe.ingredients) {
+ if (ingredient.special) { continue }
+ if (skip[ingredientsLookup[ingredient.ingredient]]) { continue }
+ x.push(ingredients.indexOf(ingredient.ingredient))
+ }
+ recipes.push(x)
+ }
+ let n_ingredients = ingredients.length
+
+ let makeStepR = (step, r) => `r${step}_${r}`
+ let makeStepY = (step, y) => `y${step}_${y}`
+
+ let jsonvars = []
+ for (let step = 0; step < n_ingredients; step++) {
+ for (let [r, recipe] of recipes.entries()) {
+ jsonvars.push({name: makeStepR(step, r), coef: -1})
+ }
+ }
+ let jsonobjective = {
+ direction: glpk.GLP_MIN,
+ vars: jsonvars,
+ }
+ let jsonconstraints = []
+ for (let step = 0; step < n_ingredients; step++) {
+ let vars = []
+ for (let y = 0; y < n_ingredients; y++) {
+ vars.push({name: makeStepY(step, y), coef: 1})
+ }
+ jsonconstraints.push({vars, bnds: {type: glpk.GLP_FX, lb: step+1}})
+ }
+ for (let y = 0; y < n_ingredients; y++) {
+ for (let step = 0; step < n_ingredients - 1; step++) {
+ jsonconstraints.push({
+ vars: [{name: makeStepY(step+1, y), coef: 1}, {name: makeStepY(step, y), coef: -1}],
+ bnds: {type: glpk.GLP_LO, lb: 0}
+ })
+ }
+ }
+ for (let [r, recipe] of recipes.entries()) {
+ for (let step = 0; step < n_ingredients; step++) {
+ let total = []
+ for (let y of recipe) {
+ total.push({name: makeStepY(step, y), coef: -1})
+ }
+ total.push({name: makeStepR(step, r), coef: 1})
+ jsonconstraints.push({
+ vars: total,
+ bnds: {type: glpk.GLP_LO, lb: (-recipe.length + 1)}
+ })
+ for (let y of recipe) {
+ jsonconstraints.push({
+ vars: [{name: makeStepR(step, r), coef: 1}, {name: makeStepY(step, y), coef: -1}],
+ bnds: {type: glpk.GLP_UP, ub: 0}
+ })
+ }
+ }
+ }
+ let binaries = []
+ for (let y = 0; y < n_ingredients; y++) {
+ for (let step = 0; step < n_ingredients; step++) {
+ binaries.push(makeStepY(step, y))
+ }
+ }
+ for (let r = 0; r < recipes.length; r++) {
+ for (let step = 0; step < n_ingredients; step++) {
+ binaries.push(makeStepR(step, r))
+ }
+ }
+ let lp = {
+ objective: jsonobjective,
+ subjectTo: jsonconstraints,
+ binaries,
+ options: {
+ msglev: 2,
+ tmlim: Number.parseInt(document.getElementById('limit').value),
+ }
+ }
+ return {ingredients, lp, recipes}
+}
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..c1058c7
--- /dev/null
+++ b/server.js
@@ -0,0 +1,39 @@
+const http = require('http')
+const fs = require('fs')
+
+const PORT = 8080
+
+http
+ .createServer((request, response) => {
+ fs.readFile(`.${request.url}`, (err, data) => {
+ if (err) {
+ response.writeHeader(404, {
+ 'Content-Type': 'text/plain'
+ })
+ response.write('404 Not Found')
+ response.end()
+ return
+ }
+
+ if (request.url.endsWith('.html')) {
+ response.writeHeader(200, {
+ 'Content-Type': 'text/html'
+ })
+ }
+
+ if (request.url.endsWith('.js')) {
+ response.writeHeader(200, {
+ 'Content-Type': 'application/javascript'
+ })
+ }
+ if (request.url.endsWith('.wasm')) {
+ response.writeHeader(200, {
+ 'Content-Type': 'application/wasm'
+ })
+ }
+
+ response.write(data)
+ response.end()
+ })
+ })
+ .listen(PORT)