/// <summary>
        /// Judges a set of recipes based on a scale and its efficiency with regards to the current pantry.  The lower the score, the better.
        /// </summary>
        double GetScore(RecipeNode[] currentSet, byte scale)
        {
            double wasted = 0; //Add 1.0 for ingredients that don't exist in pantry, add percentage of leftover otherwise
             float avgRating = 0; //Average rating for all recipes in the set (0-4)
             float tagPoints = 0; //Point for each tag that's one of our favorites
             float tagTotal = 0; //Total number of tags in all recipes
             float ingPoints = 0; //Point for each ing that's one of our favorites
             float ingTotal = 0; //Total number of ingrediets in all recipes

             for (var i = 0; i < currentSet.Length; i++)
             {
            var recipe = currentSet[i];
            var ingredients = (IngredientUsage[]) recipe.Ingredients;

            //Add points for any favorite tags this recipe uses

            tagTotal += recipe.Tags.Length;
            ingTotal += ingredients.Length;

            for (var t = 0; t < recipe.Tags.Length; t++) //TODO: Use bitmasks for storing recipe tags and fav tags, then count bits
            {
               if (favTags[t])
               {
                  tagPoints++;
               }
            }

            byte realRating; //Real rating is the user's rating, else the public rating, else 3.
            if (!ratings.TryGetValue(recipe, out realRating))
            {
               realRating = (recipe.Rating == 0) ? (byte) 3 : recipe.Rating; //if recipe has no ratings, use average rating of 3.
            }
            avgRating += (realRating - 1);

            for (var j = 0; j < ingredients.Length; j++)
            {
               var ingredient = ingredients[j];

               //Add points for any favorite ingredients this recipe uses
               var ingKey = ingredient.Ingredient.Key;

               for (var k = 0; k < favIngs.Length; k++) //For loop is actually faster than 5 "if" statements
               {
                  if (favIngs[k] == ingKey)
                  {
                     ingPoints++;
                     break;
                  }
               }

               IngredientUsage curUsage;
               var fContains = totals.TryGetValue(ingredient.Ingredient, out curUsage);
               if (!fContains)
               {
                  curUsage = new IngredientUsage();
                  curUsage.Amt = ingredient.Amt;
                  totals.Add(ingredient.Ingredient, curUsage);
               }
               else
               {
                  curUsage.Amt += ingredient.Amt;
               }
            }
             }

             if (profile.Pantry != null) //If profile has a pantry, figure out how much of it is wasted
             {
            //For each pantry ingredient that we're not using, punish the score by MISSING_ING_PUNISH amount.
            var pEnum = pantryAmounts.GetEnumerator();
            while (pEnum.MoveNext())
            {
               if (!totals.ContainsKey(pEnum.Current.Key))
               {
                  wasted += MISSING_ING_PUNISH;
               }
            }

            var e = totals.GetEnumerator();
            while (e.MoveNext())
            {
               var curKey = e.Current.Key;

               float? have;
               if (pantryAmounts.TryGetValue(curKey, out have)) //We have this in our pantry
               {
                  if (!have.HasValue) //We have this in our pantry, but no amount is specified - So we "act" like we have whatever we need
                  {
                     continue;
                  }

                  if (!e.Current.Value.Amt.HasValue) //This recipe doesn't specify an amount - So we "act" like we use half of what we have
                  {
                     wasted += EMPTY_RECIPE_AMOUNT;
                     continue;
                  }

                  var need = e.Current.Value.Amt.Value;
                  var ratio = 1 - ((have.Value - need)/have.Value); //Percentage of how much you're using of what you have
                  if (ratio > 1) //If you need more than you have, add the excess ratio to the waste but don't go over the punishment for not having the ingredient at all
                  {
                     wasted += Math.Min(ratio, NEW_ING_PUNISH);
                  }
                  else
                  {
                     wasted += (1 - ratio);
                  }
               }
               else
               {
                  wasted += NEW_ING_PUNISH; //For each ingredient this meal set needs that we don't have, increment by NEW_ING_PUNISH
               }
            }
             }

             double worstScore, trendScore, efficiencyScore;

             if (profile.Pantry == null) //No pantry, efficiency is defined by the overlap of ingredients across recipes
             {
            efficiencyScore = totals.Keys.Count/ingTotal;
             }
             else //Efficiency is defined by how efficient the pantry ingredients are utilized
             {
            worstScore = ((totals.Keys.Count*NEW_ING_PUNISH) + (profile.Pantry.Length*MISSING_ING_PUNISH)); //Worst possible efficiency score
            efficiencyScore = (wasted/worstScore);
             }

             avgRating /= currentSet.Length;
             trendScore = 1 - ((((avgRating/4)*4) + (tagPoints/tagTotal) + (ingPoints/ingTotal))/6);

             totals.Clear();

             if (scale == 1)
            return efficiencyScore;
             else if (scale == 2)
            return (efficiencyScore + efficiencyScore + trendScore)/3;
             else if (scale == 3)
            return (efficiencyScore + trendScore)/2;
             else if (scale == 4)
            return (efficiencyScore + trendScore + trendScore)/3;
             else if (scale == 5)
            return trendScore;

             return 0;
        }
        /// <summary>
        /// Judges a set of recipes based on a scale and its efficiency with regards to the current pantry.  The lower the score, the better.
        /// </summary>
        private double GetScore(RecipeNode[] currentSet, byte scale)
        {
            double wasted = 0; // Add 1.0 for ingredients that don't exist in pantry, add percentage of leftover otherwise
            float averageRating = 0; // Average rating for all recipes in the set (0-4)
            float tagPoints = 0; // Point for each tag that's one of our favorites
            float tagTotal = 0; // Total number of tags in all recipes
            float ingredientPoints = 0; // Point for each ingredient that's one of our favorites
            float ingredientTotal = 0; // Total number of ingrediets in all recipes

            for (var i = 0; i < currentSet.Length; i++)
            {
                var recipe = currentSet[i];
                var ingredients = (IngredientUsage[])recipe.Ingredients;

                // Add points for any favorite tags this recipe uses
                tagTotal += recipe.Tags.Length;
                ingredientTotal += ingredients.Length;

                // TODO: Use bitmasks for storing recipe tags and fav tags, then count bits
                for (int tag = 0; tag < recipe.Tags.Length; tag++)
                {
                    if (this.favoriteTags[tag])
                    {
                        tagPoints++;
                    }
                }

                // Real rating is the user's rating, else the public rating, else 3.
                byte realRating;
                if (!this.ratings.TryGetValue(recipe, out realRating))
                {
                    // if recipe has no ratings, use average rating of 3.
                    realRating = (recipe.Rating == 0) ? (byte)3 : recipe.Rating;
                }

                averageRating += realRating - 1;

                for (var j = 0; j < ingredients.Length; j++)
                {
                    var ingredient = ingredients[j];

                    // Add points for any favorite ingredients this recipe uses
                    var ingredientKey = ingredient.Ingredient.Key;

                    for (var key = 0; key < this.favoriteIngredients.Length; key++)
                    {
                        if (this.favoriteIngredients[key] == ingredientKey)
                        {
                            ingredientPoints++;
                            break;
                        }
                    }

                    IngredientUsage currentUsage;
                    bool fContains = this.totals.TryGetValue(ingredient.Ingredient, out currentUsage);
                    if (!fContains)
                    {
                        currentUsage = new IngredientUsage();
                        currentUsage.Amount = ingredient.Amount;
                        this.totals.Add(ingredient.Ingredient, currentUsage);
                    }
                    else
                    {
                        currentUsage.Amount += ingredient.Amount;
                    }
                }
            }

            // If profile has a pantry, figure out how much of it is wasted
            if (this.profile.Pantry != null)
            {
                // For each pantry ingredient that we're not using, punish the score by MissingIngredientPunish amount.
                var pantryEnumerator = this.pantryAmounts.GetEnumerator();
                while (pantryEnumerator.MoveNext())
                {
                    if (!this.totals.ContainsKey(pantryEnumerator.Current.Key))
                    {
                        wasted += MissingIngredientPunish;
                    }
                }

                var enumerator = this.totals.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    var currentKey = enumerator.Current.Key;

                    float? haveAmount;

                    // We have this in our pantry
                    if (this.pantryAmounts.TryGetValue(currentKey, out haveAmount))
                    {
                        // We have this in our pantry, but no amount is specified - So we "act" like we have whatever we need
                        if (!haveAmount.HasValue)
                        {
                            continue;
                        }

                        // This recipe doesn't specify an amount - So we "act" like we use half of what we have
                        if (!enumerator.Current.Value.Amount.HasValue)
                        {
                            wasted += EmptyRecipeAmount;
                            continue;
                        }

                        float needAmount = enumerator.Current.Value.Amount.Value;

                        // Percentage of how much you're using of what you have
                        float ratio = 1 - ((haveAmount.Value - needAmount) / haveAmount.Value);

                        // If you need more than you have, add the excess ratio to the waste but don't go over the punishment for not having the ingredient at all
                        if (ratio > 1)
                        {
                            wasted += Math.Min(ratio, NewIngredientPunish);
                        }
                        else
                        {
                            wasted += 1 - ratio;
                        }
                    }
                    else
                    {
                        // For each ingredient this meal set needs that we don't have, increment by NewIngredientPunish
                        wasted += NewIngredientPunish;
                    }
                }
            }

            double worstScore;
            double trendScore;
            double efficiencyScore;

            // No pantry, efficiency is defined by the overlap of ingredients across recipes
            if (this.profile.Pantry == null)
            {
                efficiencyScore = this.totals.Keys.Count / ingredientTotal;
            }
            else
            {
                // Efficiency is defined by how efficient the pantry ingredients are utilized
                worstScore =
                    this.totals.Keys.Count * NewIngredientPunish +
                    this.profile.Pantry.Length * MissingIngredientPunish; // Worst possible efficiency score
                efficiencyScore = wasted / worstScore;
            }

            averageRating /= currentSet.Length;
            trendScore = 1 - ((averageRating + tagPoints / tagTotal + ingredientPoints / ingredientTotal) / 6);

            this.totals.Clear();

            switch (scale)
            {
                case 1:
                    return efficiencyScore;
                case 2:
                    return (efficiencyScore + efficiencyScore + trendScore) / 3;
                case 3:
                    return (efficiencyScore + trendScore) / 2;
                case 4:
                    return (efficiencyScore + trendScore + trendScore) / 3;
                case 5:
                    return trendScore;
                default:
                    return 0;
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Judges a set of recipes based on a scale and its efficiency with regards to the current pantry.  The lower the score, the better.
        /// </summary>
        public double GetScore(RecipeNode[] currentSet, byte scale)
        {
            double wasted = 0; // Add 1.0 for ingredients that don't exist in pantry, add percentage of leftover otherwise
            float avgRating = 0; // Average rating for all recipes in the set (0-4)
            float tagPoints = 0; // Point for each tag that's one of our favorites
            float tagTotal = 0; // Total number of tags in all recipes
            float ingPoints = 0; // Point for each ing that's one of our favorites
            float ingTotal = 0; // Total number of ingrediets in all recipes

            for (var i = 0; i < currentSet.Length; i++)
            {
                var recipe = currentSet[i];
                var ingredients = (IngredientUsage[])recipe.Ingredients;

                // Add points for any favorite tags this recipe uses
                tagTotal += recipe.Tags.Length;
                ingTotal += ingredients.Length;

                for (var t = 0; t < recipe.Tags.Length; t++)
                {
                    if (this.favTags[t])
                    {
                        tagPoints++;
                    }
                }

                byte realRating; // Real rating is the user's rating, else the public rating, else 3.
                if (!this.ratings.TryGetValue(recipe, out realRating))
                {
                    realRating = (recipe.Rating == 0) ? (byte)3 : recipe.Rating; // if recipe has no ratings, use average rating of 3.
                }
                avgRating += realRating - 1;

                foreach (var ingredient in ingredients)
                {
                    // Add points for any favorite ingredients this recipe uses
                    var ingKey = ingredient.Ingredient.Key;

                    if (this.favIngs.Any(t => t == ingKey))
                    {
                        ingPoints++;
                    }

                    IngredientUsage curUsage;
                    var fContains = this.totals.TryGetValue(ingredient.Ingredient, out curUsage);
                    if (!fContains)
                    {
                        curUsage = new IngredientUsage();
                        curUsage.Amt = ingredient.Amt;
                        this.totals.Add(ingredient.Ingredient, curUsage);
                    }
                    else
                    {
                        curUsage.Amt += ingredient.Amt;
                    }
                }
            }

            // If profile has a pantry, figure out how much of it is wasted
            if (this.profile.Pantry != null)
            {
                // For each pantry ingredient that we're not using, punish the score by MISSING_ING_PUNISH amount.
                var pEnum = this.pantryAmounts.GetEnumerator();
                while (pEnum.MoveNext())
                {
                    if (!this.totals.ContainsKey(pEnum.Current.Key))
                    {
                        wasted += MissingIngPunish;
                    }
                }

                var e = this.totals.GetEnumerator();
                while (e.MoveNext())
                {
                    var curKey = e.Current.Key;

                    float? have;

                    // We have this in our pantry
                    if (this.pantryAmounts.TryGetValue(curKey, out have))
                    {
                        // We have this in our pantry, but no amount is specified - So we "act" like we have whatever we need
                        if (!have.HasValue)
                        {
                            continue;
                        }

                        // This recipe doesn't specify an amount - So we "act" like we use half of what we have
                        if (!e.Current.Value.Amt.HasValue)
                        {
                            wasted += EmptyRecipeAmount;
                            continue;
                        }

                        var need = e.Current.Value.Amt.Value;
                        var ratio = 1 - ((have.Value - need) / have.Value); // Percentage of how much you're using of what you have
                        // If you need more than you have, add the excess ratio to the waste but don't go over the punishment for not having the ingredient at all
                        if (ratio > 1)
                        {
                            wasted += Math.Min(ratio, NewIngPunish);
                        }
                        else
                        {
                            wasted += 1 - ratio;
                        }
                    }
                    else
                    {
                        wasted += NewIngPunish; // For each ingredient this meal set needs that we don't have, increment by NEW_ING_PUNISH
                    }
                }
            }

            double efficiencyScore;

            // No pantry, efficiency is defined by the overlap of ingredients across recipes
            if (this.profile.Pantry == null)
            {
                efficiencyScore = totals.Keys.Count / ingTotal;
            }
            else
            {
                // Efficiency is defined by how efficient the pantry ingredients are utilized
                double worstScore = (this.totals.Keys.Count * NewIngPunish) + (this.profile.Pantry.Length * MissingIngPunish);
                efficiencyScore = wasted / worstScore;
            }

            avgRating /= currentSet.Length;
            double trendScore = 1 - ((((avgRating / 4) * 4) + (tagPoints / tagTotal) + (ingPoints / ingTotal)) / 6);

            this.totals.Clear();

            switch (scale)
            {
                case 1:
                    return efficiencyScore;
                case 2:
                    return (efficiencyScore + efficiencyScore + trendScore) / 3;
                case 3:
                    return (efficiencyScore + trendScore) / 2;
                case 4:
                    return (efficiencyScore + trendScore + trendScore) / 3;
                case 5:
                    return trendScore;
            }

            return 0;
        }
Esempio n. 4
0
        /// <summary>
        /// Judges a set of recipes based on a scale and its efficiency with regards to the current pantry.  The lower the score, the better.
        /// </summary>
        public double GetScore(RecipeNode[] currentSet, byte scale)
        {
            double wasted    = 0; // Add 1.0 for ingredients that don't exist in pantry, add percentage of leftover otherwise
            float  avgRating = 0; // Average rating for all recipes in the set (0-4)
            float  tagPoints = 0; // Point for each tag that's one of our favorites
            float  tagTotal  = 0; // Total number of tags in all recipes
            float  ingPoints = 0; // Point for each ing that's one of our favorites
            float  ingTotal  = 0; // Total number of ingrediets in all recipes

            for (var i = 0; i < currentSet.Length; i++)
            {
                var recipe      = currentSet[i];
                var ingredients = (IngredientUsage[])recipe.Ingredients;

                // Add points for any favorite tags this recipe uses
                tagTotal += recipe.Tags.Length;
                ingTotal += ingredients.Length;

                for (var t = 0; t < recipe.Tags.Length; t++)
                {
                    if (this.favTags[t])
                    {
                        tagPoints++;
                    }
                }

                byte realRating; // Real rating is the user's rating, else the public rating, else 3.
                if (!this.ratings.TryGetValue(recipe, out realRating))
                {
                    realRating = (recipe.Rating == 0) ? (byte)3 : recipe.Rating; // if recipe has no ratings, use average rating of 3.
                }
                avgRating += realRating - 1;

                foreach (var ingredient in ingredients)
                {
                    // Add points for any favorite ingredients this recipe uses
                    var ingKey = ingredient.Ingredient.Key;

                    if (this.favIngs.Any(t => t == ingKey))
                    {
                        ingPoints++;
                    }

                    IngredientUsage curUsage;
                    var             fContains = this.totals.TryGetValue(ingredient.Ingredient, out curUsage);
                    if (!fContains)
                    {
                        curUsage     = new IngredientUsage();
                        curUsage.Amt = ingredient.Amt;
                        this.totals.Add(ingredient.Ingredient, curUsage);
                    }
                    else
                    {
                        curUsage.Amt += ingredient.Amt;
                    }
                }
            }

            // If profile has a pantry, figure out how much of it is wasted
            if (this.profile.Pantry != null)
            {
                // For each pantry ingredient that we're not using, punish the score by MISSING_ING_PUNISH amount.
                var pEnum = this.pantryAmounts.GetEnumerator();
                while (pEnum.MoveNext())
                {
                    if (!this.totals.ContainsKey(pEnum.Current.Key))
                    {
                        wasted += MissingIngPunish;
                    }
                }

                var e = this.totals.GetEnumerator();
                while (e.MoveNext())
                {
                    var curKey = e.Current.Key;

                    float?have;

                    // We have this in our pantry
                    if (this.pantryAmounts.TryGetValue(curKey, out have))
                    {
                        // We have this in our pantry, but no amount is specified - So we "act" like we have whatever we need
                        if (!have.HasValue)
                        {
                            continue;
                        }

                        // This recipe doesn't specify an amount - So we "act" like we use half of what we have
                        if (!e.Current.Value.Amt.HasValue)
                        {
                            wasted += EmptyRecipeAmount;
                            continue;
                        }

                        var need  = e.Current.Value.Amt.Value;
                        var ratio = 1 - ((have.Value - need) / have.Value); // Percentage of how much you're using of what you have
                        // If you need more than you have, add the excess ratio to the waste but don't go over the punishment for not having the ingredient at all
                        if (ratio > 1)
                        {
                            wasted += Math.Min(ratio, NewIngPunish);
                        }
                        else
                        {
                            wasted += 1 - ratio;
                        }
                    }
                    else
                    {
                        wasted += NewIngPunish; // For each ingredient this meal set needs that we don't have, increment by NEW_ING_PUNISH
                    }
                }
            }

            double efficiencyScore;

            // No pantry, efficiency is defined by the overlap of ingredients across recipes
            if (this.profile.Pantry == null)
            {
                efficiencyScore = totals.Keys.Count / ingTotal;
            }
            else
            {
                // Efficiency is defined by how efficient the pantry ingredients are utilized
                double worstScore = (this.totals.Keys.Count * NewIngPunish) + (this.profile.Pantry.Length * MissingIngPunish);
                efficiencyScore = wasted / worstScore;
            }

            avgRating /= currentSet.Length;
            double trendScore = 1 - ((((avgRating / 4) * 4) + (tagPoints / tagTotal) + (ingPoints / ingTotal)) / 6);

            this.totals.Clear();

            switch (scale)
            {
            case 1:
                return(efficiencyScore);

            case 2:
                return((efficiencyScore + efficiencyScore + trendScore) / 3);

            case 3:
                return((efficiencyScore + trendScore) / 2);

            case 4:
                return((efficiencyScore + trendScore + trendScore) / 3);

            case 5:
                return(trendScore);
            }

            return(0);
        }