[Challenge] BB Gets Drunk
-
Partially for fun, partially to test my skills, and partially to get other members to solve my problems for me, I’ve decided to post a little programming challenge. If it goes well, maybe we’ll do more, or maybe I’ll post them with my solutions to show off various concepts and (hopefully) get feedback on where things could be improved.
I hope you guys will give these a chance and that you enjoy working on them.
BB Get’s Drunk
Scuzz’s birthday is coming up, and we’ve decided to all get together and get drunk!
We know we need:
12 cans of cider
2 bottles vodka
1 bottle whiskey
6 beers
4 bottles red wine
2 bottles white wineWe also know that
Scuzz has 6 cans of cider, 1 bottle gin, and 2 bottles of red wine
Schamper has 2 cans of cider, and 1 bottle of white wine
Jelly has 3 bottles red wine and 1 bottle vodka
That freeloader Sly only has protein shakes
Almost has 6 beers and 1 bottle red wine
Choob has 1 bottle whiskey and 12 cans of ciderWhat we want to do is satisfy our drinking list as best as possible. We also want to keep track of who’s going to bring what drinks to avoid accidentally bringing too much (bear with me, I know this situation isn’t perfect) as well as keep track of what we’re missing.
E.g. One solution would be:
Scuzz brings 6 cans of cider and 2 bottles red wine
Schamper brings 2 cans of cider and 1 bottle white wine
Jelly brings 2 bottles red wine and 1 bottle vodka
Almost brings 6 beers
Choob brings 1 bottle whiskey and 4 cans cider
We’re missing 1 bottle white wine and 1 bottle vodka.This is a simplified variation on a real problem I came across. The current solution is really bad - It took me half an hour to analyze 40 lines to figure out how it did what it was supposed to do. I’m sure there’s a better way to do it, and I’m trying to find it, but figured you guys might be interested in this as well.
The current structure of the solution is something like this:
class Alcohol { public string name; public int quantity; } class AlcoholInventoryItem { public string person; public Alcohol alcohol; } public AlcoholInentoryItem[] GetWhoWillBringWhat(Alcohol[] wanted, AlcoholInventoryItem[] have);
Feel free to use this as a starting point or do it with a completely different structure (I know this one is pretty bad, that’s why I’m trying to come up with other solutions)
Anyway, Let me know if you need clarification, and good luck!
-
Here’s my first solution
public class Alcohol { public string Name; public int Quantity; public Alcohol(string name, int quantity) { Name = name; Quantity = quantity; } } public class AlcoholInventoryItem { public string Person; public Alcohol Alcohol; public AlcoholInventoryItem(String person, String alcoholName, int quantity) { Person = person; this.Alcohol = new Alcohol(alcoholName, quantity); } } public static IEnumerable<AlcoholInventoryItem> WhoIsBringingWhat(IEnumerable<Alcohol> wanted, IEnumerable<AlcoholInventoryItem> have) { var whoIsBringingWhat = new List<AlcoholInventoryItem>(); // Iterate through everything we want foreach (var currentAlcohol in wanted) { // Get all inventory items that are for this type of alcohol IEnumerable<AlcoholInventoryItem> currentAlcoholInventory = have.Where(item => item.Alcohol.Name == currentAlcohol.Name); // Get how many we have unsatisfied int numStillWanted = currentAlcohol.Quantity; // Iterate through our inventory foreach (var currentAlcoholInventoryItem in currentAlcoholInventory) { // If we still want some, this person will bring some if (numStillWanted > 0) { // Bring as much as we want or as much as you have - which ever is less var bringing = new AlcoholInventoryItem(currentAlcoholInventoryItem.Person, currentAlcoholInventoryItem.Alcohol.Name, Math.Min(currentAlcoholInventoryItem.Alcohol.Quantity, numStillWanted)); // Figure out how much we still need numStillWanted -= bringing.Alcohol.Quantity; // Mark who is bringing this and how much whoIsBringingWhat.Add(bringing); } } // We've gone through all our inventory - are we still missing some? if (numStillWanted > 0) { whoIsBringingWhat.Add(new AlcoholInventoryItem(null, currentAlcohol.Name, numStillWanted)); } } return whoIsBringingWhat; }
Yay C#.
The outerloop iterates through what we want, the inner loop iterates through what we have. As it’s iterating through the inner loop, it tries to get each person to bring as much as they can up to the number we want. Then at the end, if we still want more, we add it.
I feel like this could be simplified more. For example, I’m not a fan of the fact that I have a
if (numStillWanted > 0)
outside of the inner loop. I think there’s probably a more functional way, but I haven’t found it yet. -
Really minor improvement, but in order to remove that extra
if (numStillWanted > 0)
we could consider any alcohol that we don’t have as inventory that belongs to no one.To make this work in the code, we just add a line outside the outer loop:
have = have.concat(wanted.select(cur => new AlcoholInventoryItem(null, cur.Name, cur.Quantity));
With this, we’re assured that we will never be left with
numStillWanted > 0
after the inner loop because in the case where no one has any of the alcohol we want, our inventory consists of alcohol that belongs to no one that has exactly the quantity we want.