ScienceScore FindBestScienceWildcard(int nWildScienceSymbols, ScienceSymbols copiedSymbols, int groupMultiplier)
        {
            ScienceScore tmpResult = new ScienceScore();
            ScienceScore bestResult = tmpResult;

            int maxScienceScore = 0;

            // this player's science cards:
            int nCompass = playedStructure.Where(x => x.effect is ScienceEffect && ((ScienceEffect)x.effect).symbol == ScienceEffect.Symbol.Compass).Count();
            int nGear = playedStructure.Where(x => x.effect is ScienceEffect && ((ScienceEffect)x.effect).symbol == ScienceEffect.Symbol.Gear).Count();
            int nTablet = playedStructure.Where(x => x.effect is ScienceEffect && ((ScienceEffect)x.effect).symbol == ScienceEffect.Symbol.Tablet).Count();

            // now add wild cards and symbols copied from neighbors by mask effect cards.

            // if wild cards are in play, we choose the best combination of wilds to get the maximum overall
            // score, with Aristotle's bonus factored in.  In some cases, Aristotle's effect will mean it's
            // more beneficial to use the wild card(s) to make more groups rather than like symbols.
            // For example: 1/2/5+1 wild.  Without Aristotle, the maximum score is 1/2/6 = 48.
            // But with Aristotle's bonus, it's better to use the wild card to make a 2nd set instead
            // 1/2/6 = 51 with Aristotle but if you use the wild to make 2/2/5, that group is worth 53 points.

            for (int nWildTablets = 0; nWildTablets <= Math.Min(nWildScienceSymbols, copiedSymbols.nTablet); ++nWildTablets)
            {
                for (int nWildCompasses = 0; nWildCompasses <= Math.Min(nWildScienceSymbols - nWildTablets, copiedSymbols.nCompass); ++nWildCompasses)
                {
                    int mWildGears = Math.Min(nWildScienceSymbols - (nWildTablets + nWildCompasses), copiedSymbols.nGear);
                    int score = CalculateScienceGroupScore(nTablet + nWildTablets, nCompass + nWildCompasses, nGear + mWildGears, groupMultiplier, out tmpResult);
                    if (score > maxScienceScore)
                    {
                        maxScienceScore = score;
                        bestResult = tmpResult;
                    }
                }
            }

            return bestResult;
        }
        int CalculateScienceGroupScore(int nCompass, int nGear, int nTablet, int groupMultiplier, out ScienceScore ss)
        {
            ss.sym.nCompass = nCompass;
            ss.sym.nGear = nGear;
            ss.sym.nTablet = nTablet;
            ss.groupMultiplier = groupMultiplier;

            // Compute output values
            ss.baseScore = ss.sym.nCompass * ss.sym.nCompass + ss.sym.nGear * ss.sym.nGear + ss.sym.nTablet * ss.sym.nTablet;
            ss.nGroups = Math.Min(Math.Min(ss.sym.nCompass, ss.sym.nGear), ss.sym.nTablet);
            ss.TotalPoints = ss.baseScore + ss.nGroups * ss.groupMultiplier;

            return ss.TotalPoints;
        }