import random # ---------------------- # Data & Knowledge Base # ---------------------- bean_types = { "Arabica": {"acidity": "bright", "body": "medium", "flavor": ["fruity", "floral", "sweet"]}, "Robusta": {"acidity": "low", "body": "heavy", "flavor": ["nutty", "woody", "bitter"]}, "Liberica": {"acidity": "medium", "body": "full", "flavor": ["smoky", "spicy", "flowery"]}, "Excelsa": {"acidity": "high", "body": "light", "flavor": ["tart", "fruity", "complex"]} } roast_levels = { "Light": {"acidity": "very high", "body": "light", "flavor_strength": "mild", "notes": ["citrus", "berry", "tea-like"]}, "Medium": {"acidity": "balanced", "body": "medium", "flavor_strength": "rich", "notes": ["chocolate", "caramel", "nut"]}, "Medium-Dark": {"acidity": "low", "body": "full", "flavor_strength": "strong", "notes": ["roasted", "toast", "dark chocolate"]}, "Dark": {"acidity": "very low", "body": "heavy", "flavor_strength": "intense", "notes": ["smoky", "bitter", "spicy"]} } grind_sizes = { "Extra Fine": {"best_for": ["Espresso"], "extraction": "very fast", "risk": "bitter/over-extracted"}, "Fine": {"best_for": ["Espresso", "Moka Pot"], "extraction": "fast", "risk": "slightly bitter if too long"}, "Medium-Fine": {"best_for": ["Pour Over", "AeroPress"], "extraction": "balanced", "risk": "low"}, "Medium": {"best_for": ["Drip Coffee", "Chemex"], "extraction": "even", "risk": "low"}, "Coarse": {"best_for": ["French Press", "Cold Brew"], "extraction": "slow", "risk": "sour/under-extracted"}, "Extra Coarse": {"best_for": ["Cold Brew"], "extraction": "very slow", "risk": "watery"} } water_temp = { "80°C - 85°C": {"effect": "milder, less extraction", "best_for": "light roasts, fruity beans"}, "86°C - 92°C": {"effect": "balanced extraction", "best_for": "most medium roasts"}, "93°C - 96°C": {"effect": "high extraction, bold flavors", "best_for": "dark roasts, heavy beans"}, "97°C - 100°C": {"effect": "over-extraction risk, bitter", "best_for": "none — use carefully"} } brew_ratios = { "1:12": {"strength": "very strong", "body": "thick", "risk": "bitter"}, "1:15": {"strength": "strong", "body": "full", "risk": "balanced if brewed right"}, "1:18": {"strength": "balanced", "body": "medium", "risk": "ideal for most"}, "1:20": {"strength": "mild", "body": "light", "risk": "watery if too coarse"}, "1:22": {"strength": "very mild", "body": "thin", "risk": "weak flavor"} } brew_methods = { "Pour Over": {"clarity": "high", "acidity": "emphasized", "body": "clean"}, "French Press": {"clarity": "low", "acidity": "mellowed", "body": "heavy/oily"}, "Espresso": {"clarity": "intense", "acidity": "concentrated", "body": "very thick"}, "Cold Brew": {"clarity": "smooth", "acidity": "very low", "body": "silky/sweet"}, "AeroPress": {"clarity": "medium-high", "acidity": "adjustable", "body": "balanced"}, "Moka Pot": {"clarity": "low", "acidity": "bold", "body": "syrupy"} } extras = { "None": {"effect": "pure coffee taste"}, "Milk": {"effect": "softens acidity, adds creaminess, sweetens"}, "Sugar": {"effect": "masks bitterness, enhances sweetness"}, "Cinnamon": {"effect": "warm, spicy note, reduces sharpness"}, "Vanilla": {"effect": "sweet, aromatic, rounds out flavors"}, "Coconut": {"effect": "tropical, creamy, lightens body"} } # ---------------------- # Prediction Logic # ---------------------- def predict_taste(bean, roast, grind, temp, ratio, method, extra): # Base properties base = bean_types[bean] roast_props = roast_levels[roast] grind_props = grind_sizes[grind] temp_props = water_temp[temp] ratio_props = brew_ratios[ratio] method_props = brew_methods[method] extra_props = extras[extra] # Combine traits acidity = [] body = [] flavors = [] notes = [] quality = 10 # start perfect, deduct for mismatches # Acidity acidity.append(base["acidity"]) acidity.append(roast_props["acidity"]) acidity.append(method_props["acidity"]) # Body body.append(base["body"]) body.append(roast_props["body"]) body.append(ratio_props["body"]) body.append(method_props["body"]) # Flavors & Notes flavors.extend(base["flavor"]) flavors.extend(roast_props["notes"]) notes.append(temp_props["effect"]) notes.append(grind_props["extraction"]) # Check for good/poor matches if grind not in grind_props["best_for"]: quality -= 3 notes.append(f"⚠️ Grind size not ideal for {method} — {grind_props['risk']}") if temp == "97°C - 100°C" and roast != "Dark": quality -= 2 notes.append("⚠️ Too hot for this roast — risk of bitterness") if ratio == "1:12" and roast == "Light": quality -= 2 notes.append("⚠️ Too concentrated for light roast — may taste harsh") if extra != "None": notes.append(f"✨ Added {extra}: {extra_props['effect']}") # Final quality cap quality = max(1, min(10, quality)) # Summarize avg_acidity = max(set(acidity), key=acidity.count) avg_body = max(set(body), key=body.count) unique_flavors = list(set(flavors))[:4] result = { "acidity": avg_acidity, "body": avg_body, "flavor_profile": ", ".join(unique_flavors), "characteristics": " ".join(notes), "quality_score": quality, "taste_prediction": get_taste_description(avg_acidity, avg_body, unique_flavors, quality) } return result def get_taste_description(acidity, body, flavors, score): flavor_str = ", ".join(flavors) if score >= 9: return f"š Excellent! A perfectly balanced cup — {acidity} acidity, {body} body, with clear notes of {flavor_str}. Smooth, rich, and enjoyable." elif score >= 7: return f"✅ Very good. {acidity.capitalize()} acidity, {body} body, tasting of {flavor_str}. Well-made and pleasant." elif score >= 5: return f"š Average. {acidity} acidity, {body} body, hints of {flavor_str}. Drinkable but could be adjusted for better balance." else: return f"⚠️ Needs improvement. {acidity} acidity, {body} body, muted {flavor_str}. Slightly off-balance — try changing grind or temperature." # ---------------------- # Game Interface # ---------------------- def coffee_lab_game(): print("="*50) print("☕ COFFEE LAB SIMULATOR BOT ☕") print("Experiment with your brew and see how it tastes!") print("="*50) # User inputs bean = input(f"Bean type {list(bean_types.keys())}: ").strip().title() while bean not in bean_types: bean = input("Invalid choice. Try again: ").strip().title() roast = input(f"Roast level {list(roast_levels.keys())}: ").strip().title() while roast not in roast_levels: roast = input("Invalid choice. Try again: ").strip().title() grind = input(f"Grind size {list(grind_sizes.keys())}: ").strip().title() while grind not in grind_sizes: grind = input("Invalid choice. Try again: ").strip().title() temp = input(f"Water temperature {list(water_temp.keys())}: ").strip() while temp not in water_temp: temp = input("Invalid choice. Try again: ").strip() ratio = input(f"Brew ratio (coffee:water) {list(brew_ratios.keys())}: ").strip() while ratio not in brew_ratios: ratio = input("Invalid choice. Try again: ").strip() method = input(f"Brew method {list(brew_methods.keys())}: ").strip().title() while method not in brew_methods: method = input("Invalid choice. Try again: ").strip().title() extra = input(f"Add-in {list(extras.keys())}: ").strip().title() while extra not in extras: extra = input("Invalid choice. Try again: ").strip().title() # Simulate & Predict print("\nš Brewing your coffee...") result = predict_taste(bean, roast, grind, temp, ratio, method, extra) # Show Result print("\n" + "="*50) print("š TASTE PREDICTION RESULT") print("="*50) print(f"Acidity: {result['acidity']}") print(f"Body: {result['body']}") print(f"Flavor Profile: {result['flavor_profile']}") print(f"Notes: {result['characteristics']}") print(f"Quality Score: {result['quality_score']}/10") print("\nš¬ Final Verdict:") print(result['taste_prediction']) print("="*50) # Play again again = input("\nWant to experiment again? (yes/no): ").strip().lower() if again == "yes": coffee_lab_game() else: print("Thanks for playing! Keep experimenting to find your perfect cup. ☕") # Run the game if __name__ == "__main__": coffee_lab_game()
No comments:
Post a Comment