public Card(string[] createParams)
        {
            expansion = (ExpansionSet)Enum.Parse(typeof(ExpansionSet), createParams[0]);
            strName = createParams[1];

            structureType = (StructureType)Enum.Parse(typeof(StructureType), createParams[2]);

            if (structureType != StructureType.WonderStage)
            {
                age = int.Parse(createParams[3]);
                for (int i = 0, j = 6; i < numAvailableByNumPlayers.Length; ++i, ++j)
                    numAvailableByNumPlayers[i] = int.Parse(createParams[j]);
                wonderStage = 0;
            }
            else
            {
                age = 0;
                wonderStage = int.Parse(createParams[22]);
            }

            Id = CardNameFromStringName(strName, wonderStage);

            description = createParams[4];
            iconName = createParams[5];

            cost = new Cost(createParams[11]);

            // Structure cost
            /*
            int.TryParse(createParams[11], out cost.coin);
            int.TryParse(createParams[12], out cost.wood);
            int.TryParse(createParams[13], out cost.stone);
            int.TryParse(createParams[14], out cost.clay);
            int.TryParse(createParams[15], out cost.ore);
            int.TryParse(createParams[16], out cost.cloth);
            int.TryParse(createParams[17], out cost.glass);
            int.TryParse(createParams[18], out cost.papyrus);
            */

            // build chains (Cards that can be built for free in the following age)
            chain[0] = createParams[12];
            chain[1] = createParams[13];

            if (createParams[14] != string.Empty)
            {
                var effectType = (Effect.Type)Enum.Parse(typeof(Effect.Type), createParams[14]);

                switch (effectType)
                {
                    case Effect.Type.Military:
                        effect = new MilitaryEffect(int.Parse(createParams[15]));
                        break;

                    case Effect.Type.Resource:
                        effect = new ResourceEffect(structureType == StructureType.RawMaterial || structureType == StructureType.Goods,
                            createParams[16]);
                        break;

                    case Effect.Type.Science:
                        effect = new ScienceEffect(createParams[17]);
                        break;

                    case Effect.Type.Commerce:
                        effect = new CommercialDiscountEffect();
                        break;

                    case Effect.Type.CoinsPoints:
                        CoinsAndPointsEffect.CardsConsidered cardsConsidered = (CoinsAndPointsEffect.CardsConsidered)
                            Enum.Parse(typeof(CoinsAndPointsEffect.CardsConsidered), createParams[18]);

                        StructureType classConsidered =
                            (StructureType)Enum.Parse(typeof(StructureType), createParams[19]);

                        int coinsGranted = 0;
                        int.TryParse(createParams[20], out coinsGranted);

                        int pointsAwarded = 0;
                        int.TryParse(createParams[21], out pointsAwarded);

                        effect = new CoinsAndPointsEffect(cardsConsidered, classConsidered, coinsGranted, pointsAwarded);
                        break;

                    case Effect.Type.ScienceWild:
                        effect = new ScienceWildEffect();
                        break;

                    case Effect.Type.PlayACardForFreeOncePerAge:
                        effect = new PlayACardForFreeOncePerAgeEffect();
                        break;

                    // From the Leaders expansion pack
                    case Effect.Type.FreeLeaders:                    // Roma (A) board effect: Maecenas effect
                        effect = new FreeLeadersEffect();
                        break;

                    case Effect.Type.StructureDiscount:
                        effect = new StructureDiscountEffect((StructureType)Enum.Parse(typeof(StructureType), createParams[23]));
                        break;

                    // From the Cities expansion pack
                    case Effect.Type.CopyScienceSymbolFromNeighbor:
                        effect = new CopyScienceSymbolFromNeighborEffect();
                        break;

                    case Effect.Type.LossOfCoins:
                        LossOfCoinsEffect.LossCounter lc = (LossOfCoinsEffect.LossCounter)Enum.Parse(typeof(LossOfCoinsEffect.LossCounter), createParams[19]);
                        effect = new LossOfCoinsEffect(lc, int.Parse(createParams[20]), int.Parse(createParams[21]));
                        break;

                    case Effect.Type.Diplomacy:
                        effect = new DiplomacyEffect(int.Parse(createParams[21]));
                        break;

                    default:
                        throw new Exception(string.Format("No effect class for this effect: {0}", effectType.ToString()));
                }
            }
        }
        static void Verify2(Cost cost, List<ResourceEffect> cityResources, List<ResourceEffect> leftResources, List<ResourceEffect> rightResources,
            ResourceManager.CommercePreferences pref, ResourceManager.CommerceEffects commerceEffects, CommerceOptions expectedResult)
        {
            ResourceManager resMan = new ResourceManager();

            cityResources.ForEach(x =>
            {
                resMan.add(x);
            });

            resMan.SetCommerceEffect(commerceEffects);

            CommerceOptions co = resMan.CanAfford(cost, leftResources, rightResources, pref);

            Verify(co.bAreResourceRequirementsMet == expectedResult.bAreResourceRequirementsMet);
            Verify(co.bankCoins == expectedResult.bankCoins);
            Verify(co.leftCoins == expectedResult.leftCoins);
            Verify(co.rightCoins == expectedResult.rightCoins);
        }
        /// <summary>
        /// The entry point to the reduction algorithm
        /// </summary>
        /// <param name="cost">The cost in coins and resources of the structure</param>
        /// <param name="leftResources">Resources available for my city to purchase (Browns/Greys only)</param>
        /// <param name="rightResources">Resources available for my city to purchase (Browns/Greys only)</param>
        /// <param name="pref">Flags indicating how the search should be performed (Buy from Left or Right), find LowestCost, etc.</param>
        /// <returns></returns>
        public CommerceOptions CanAfford(Cost cost, List<ResourceEffect> leftResources, List<ResourceEffect> rightResources,
            CommercePreferences pref = CommercePreferences.LowestCost | CommercePreferences.BuyFromLeftNeighbor)
        {
            CommerceOptions commOptions = new CommerceOptions();
            ReduceState rs = new ReduceState();

            rs.myResources = resources;
            rs.leftResources = leftResources;
            rs.rightResources = rightResources;
            rs.leftResourcesAvailable = leftResources.Count();
            rs.rightResourcesAvailable = rightResources.Count();
            rs.currentResourceStack = new Stack<ResourceUsed>();
            rs.marketEffects = this.marketEffects;
            rs.pref = pref;
            rs.lowestCostResourceStack = new List<ResourceUsed>();
            ResourceCost lowCost = new ResourceCost();

            rs.wildResource = pref.HasFlag(CommercePreferences.OneResourceDiscount) ? SpecialTrait.Unused : SpecialTrait.Unavailable;
            rs.bilkis = marketEffects.HasFlag(CommerceEffects.Bilkis) ? SpecialTrait.Unused : SpecialTrait.Unavailable;

            if (rs.wildResource == SpecialTrait.Unused || rs.bilkis == SpecialTrait.Unused)
                rs.wildResourceEffect = new ResourceEffect(false, "WSBOCGP");

            rs.secretWarehouse = marketEffects.HasFlag(CommerceEffects.SecretWarehouse) ? SpecialTrait.Unused : SpecialTrait.Unavailable;
            rs.nBlackMarketIndex = 0;
            rs.nBlackMarketAvailable = 0;

            if (marketEffects.HasFlag(CommerceEffects.BlackMarket1))
                ++rs.nBlackMarketAvailable;

            if (marketEffects.HasFlag(CommerceEffects.BlackMarket2))
                ++rs.nBlackMarketAvailable;

            if (rs.nBlackMarketAvailable > 0)
            {
                string strBlackMarket = "WSBOCGP";

                foreach (ResourceEffect re in resources)
                {
                    // The Black Market only excludes resources produced by this
                    // city's brown or grey structures, which only have 1 or 2 resource types
                    // So this excludes the Caravansery, Forum, and any Wonder stages.
                    if (re.resourceTypes.Length <= 2)
                    {
                        foreach (char c in re.resourceTypes)
                        {
                            int index = strBlackMarket.IndexOf(c);

                            if (index >= 0)
                                strBlackMarket = strBlackMarket.Remove(index, 1);
                        }
                    }
                }

                rs.blackMarketResource = new ResourceEffect(false, strBlackMarket);
            }

            if (cost.resources != string.Empty)
            {
                // kick off a recursive reduction of the resource cost.  Paths that completely eliminate the cost
                // are returned in the requiredResourcesLists.
                ReduceRecursively(rs, ref lowCost, cost.resources);

                commOptions.bAreResourceRequirementsMet = rs.lowestCostResourceStack.Count != 0;
            }
            else
            {
                commOptions.bAreResourceRequirementsMet = true;
            }

            if (commOptions.bAreResourceRequirementsMet)
            {
                commOptions.bankCoins = lowCost.bank + cost.coin;
                commOptions.leftCoins = lowCost.left;
                commOptions.rightCoins = lowCost.right;
            }

            return commOptions;
        }
        /// <summary>
        /// Set the coordinator and handle CommerceInformation, which contains all necessary UI data, from GameManager
        /// </summary>
        public NewCommerce(Coordinator coordinator, Card cardToBuild, bool isWonderStage, /* List<Card> cardList, */ /*string cardName, int wonderStage,*/ NameValueCollection qscoll)
        {
            //intialise all the UI components in the xaml file (labels, etc.) to avoid null pointer
            InitializeComponent();

            this.coordinator = coordinator;

            leftName = "Left Neighbor";
            middleName = "Player";
            rightName = "Right Neighbor";

            this.cardToBuild = cardToBuild;
            this.isStage = isWonderStage;

            if (isStage)
            {
                string strWonderName = qscoll["WonderStageCard"];

                cardToBuild = coordinator.FindCard(strWonderName);
            }

            cardCost = cardToBuild.cost;

            string strLeaderDiscounts = qscoll["LeaderDiscountCards"];

            if (strLeaderDiscounts != string.Empty)
            {
                foreach (string strCardId in strLeaderDiscounts.Split(','))
                {
                    Card leaderDiscountCard = coordinator.FindCard(strCardId);

                    if (((StructureDiscountEffect)leaderDiscountCard.effect).discountedStructureType == cardToBuild.structureType)
                    {
                        leaderDiscountCardId = leaderDiscountCard.Id.ToString();
                    }
                }
            }

            leftRawMarket = qscoll["hasWestTradingPost"] != null;
            rightRawMarket = qscoll["hasEastTradingPost"] != null;
            marketplace = qscoll["hasMarketplace"] != null;
            leftDock = qscoll["hasClandestineDockWest"] != null;
            rightDock = qscoll["hasClandestineDockEast"] != null;
            hasSecretWarehouse = qscoll["hasSecretWarehouse"] != null;

            string strBlackMarkets = qscoll["nBlackMarket"];
            if (strBlackMarkets != null)
            {
                nBlackMarkets = int.Parse(strBlackMarkets);
            }
            else
            {
                nBlackMarkets = 0;
            }

            PLAYER_COIN = int.Parse(qscoll["coin"]);

            CreateDag(middleDag, qscoll["PlayerResources"]);
            CreateDag(leftDag, qscoll["LeftResources"]);
            CreateDag(rightDag, qscoll["RightResources"]);

            //set the name labels
            leftNameLabel.Content = leftName;
            middleNameLabel.Content = middleName;
            rightNameLabel.Content = rightName;

            //set the player's total coins
            playerCoinsLabel.Content = PLAYER_COIN;

            bankCoinsLabel.Content = cardCost.coin;

            //set the market images
            leftRawImage.Source = FindResource(leftRawMarket ? "1r" : "2r") as BitmapImage;
            rightRawImage.Source = FindResource(rightRawMarket ? "1r" : "2r") as BitmapImage;
            leftManuImage.Source = rightManuImage.Source = FindResource(marketplace ? "1m" : "2m") as BitmapImage;

            if (leftDock)
                clandestineDockWestImage.Source = FindResource("Icons/Clandestine_Dock_West") as BitmapImage;

            if (rightDock)
                clandestineDockEastImage.Source = FindResource("Icons/Clandestine_Dock_East") as BitmapImage;

            if (leaderDiscountCardId != null)
            {
                middleDag.add(new ResourceEffect(false, "WSBOCGP"));
            }

            if (nBlackMarkets != 0)
            {
                string strBlackMarketResources = "WSBOCGP";

                foreach (ResourceEffect re in middleDag.getResourceList(false))
                {
                    // The Black Market only excludes resources produced by this
                    // city's brown or grey structures, which only have 1 or 2 resource types
                    // So this excludes the Caravansery, Forum, and any Wonder stages.
                    if (re.resourceTypes.Length <= 2)
                    {
                        foreach (char c in re.resourceTypes)
                        {
                            int index = strBlackMarketResources.IndexOf(c);

                            if (index >= 0)
                                strBlackMarketResources = strBlackMarketResources.Remove(index, 1);
                        }
                    }
                }

                if (strBlackMarketResources != string.Empty)
                {
                    for (int i = 0; i < nBlackMarkets; i++)
                        middleDag.add(new ResourceEffect(false, strBlackMarketResources));
                }
            }

            hasBilkis = qscoll["Bilkis"] != null;

            if (hasBilkis)
            {
                imgBilkisPower.Visibility = Visibility.Visible;

                // Add Bilkis' choice
                middleDag.add(new ResourceEffect(false, "WSBOCGP"));
            }

            if (hasSecretWarehouse)
            {
                SecretWarehouseImage.Source = FindResource("Icons/Secret_Warehouse") as BitmapImage;
            }

            //generate mutable elements (DAG buttons, Price representations, currentResources, etc.)
            reset();
        }
        /// <summary>
        /// Construct the labels at the cost panel, given a cost
        /// </summary>
        /// <param name="cost"></param>
        private void generateCostPanelAndUpdateSubtotal(Cost cost)
        {
            costPanel.Children.Clear();
            Label[] costLabels = new Label[resourcesNeeded];

            //fill the labels with the appropriate image
            for (int i = 0; i < cost.resources.Length; ++i)
            {
                BitmapImage iconImage = null;

                iconImage = GetButtonIcon(cost.resources[i]);

                costLabels[i] = new Label();

                costLabels[i].Background = new ImageBrush(iconImage);
                costLabels[i].Width = ICON_WIDTH;
                costLabels[i].Height = ICON_WIDTH;

                //add the labels to costPanel
                costPanel.Children.Add(costLabels[i]);
            }

            int coinCost = cost.coin;

            if (usedBilkis) ++coinCost;

            //update the subtotals
            leftSubtotalLabel.Content = leftcoin;
            rightSubtotalLabel.Content = rightcoin;
            bankCoinsLabel.Content = coinCost;
            subTotalLabel.Content = coinCost + leftcoin + rightcoin;
        }
        private Cost eliminate(Cost c, string currResourceString)
        {
            string strResources = c.resources;

            foreach (char ch in currResourceString)
            {
                int i = strResources.IndexOf(ch);

                if (i != -1)
                    strResources = strResources.Remove(i, 1);
            }

            return new Cost(strResources);
        }