예제 #1
0
        private IList<Molecule> getCombinationsOfTwo(IList<Tile> potentialReactionTiles, Tile userClickedTile)
        {
            IList<Molecule> toReturn = new List<Molecule>();
            Molecule combo;

            for (int i = 0; i < potentialReactionTiles.Count; i++)
            {
                for (int j = i + 1; j < potentialReactionTiles.Count; j++)
                {
                    combo = new Molecule();
                    combo.AtomTiles.Add(potentialReactionTiles[i]);
                    combo.AtomTiles.Add(potentialReactionTiles[j]);

                    // Bug-fix and optimization: only molecules with the user-clicked tile are legit.
                    if (combo.AtomTiles.Contains(userClickedTile))
                    {
                        toReturn.Add(combo);
                    }
                }
            }

            return toReturn;
        }
예제 #2
0
 private IList<Molecule> calculateReactionsUsingBruteForceCombinations(IList<Tile> potentialReactionTiles, Tile userClickedTile)
 {
     IList<Molecule> toReturn = new List<Molecule>();
     // Generate all possible combinations of (2 .. N) molecules.
     // Since the internal function handles all the combinationing, we just
     // call it with the biggest atom count, and it gets all the combinations of [2 .. N]
     IList<Molecule> combos = getCombinationsOf(potentialReactionTiles.Count, potentialReactionTiles, userClickedTile);
     return combos;
 }
예제 #3
0
        private IList<Molecule> getCombinationsOf(int i, IList<Tile> potentialReactionTiles, Tile userClickedTile)
        {
            // Is there a better way? Ionno.
            // Let's return the FIRST molecule we find that matches. Instead of returning
            // like 2000 potential molecules.
            IList<Molecule> twos = new List<Molecule>();
            IList<Molecule> threes = new List<Molecule>();
            IList<Molecule> fours = new List<Molecule>();
            IList<Molecule> fives = new List<Molecule>();
            IList<Molecule> sixes = new List<Molecule>();
            IList<Molecule> sevens = new List<Molecule>();
            IList<Molecule> eights = new List<Molecule>();
            IList<Molecule> nines = new List<Molecule>();

            // Our "working set" of "do we have anything?"
            IList<Molecule> temp = new List<Molecule>();

            if (i >= 2)
            {
                twos = getCombinationsOfTwo(potentialReactionTiles, userClickedTile);
                temp = twos.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 3)
            {
                threes = getIncrementalCombinations(potentialReactionTiles, twos);
                temp = threes.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 4)
            {
                // Optimize: dump un-needed stuff
                twos.Clear();
                fours = getIncrementalCombinations(potentialReactionTiles, threes);
                temp = fours.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 5)
            {
                threes.Clear();
                fives = getIncrementalCombinations(potentialReactionTiles, fours);
                temp = fives.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 6)
            {
                fours.Clear();
                sixes = getIncrementalCombinations(potentialReactionTiles, fives);
                temp = sixes.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 7)
            {
                fives.Clear();
                sevens = getIncrementalCombinations(potentialReactionTiles, sixes);
                temp = sevens.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 8)
            {
                sixes.Clear();
                eights = getIncrementalCombinations(potentialReactionTiles, sevens);
                temp = eights.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            if (i >= 9)
            {
                sevens.Clear();
                nines = getIncrementalCombinations(potentialReactionTiles, eights);
                temp = nines.Where(m => m.NetCharge == 0).ToList();
                if (temp.Count > 0)
                {
                    temp = weedOutNonConnectedMolecules(temp);
                    if (temp.Count > 0)
                    {
                        return temp;
                    }
                }
            }

            // Didn't find it yet and return? Sucks to be you!
            return new List<Molecule>();
        }
예제 #4
0
        // Our real, core algorithm. Let's do it!
        internal IList<Tile> GetTilesInReaction(Tile newAtomTile)
        {
            // get all adjacent atoms. True = base case
            IList<Tile> potentialReactionTiles = new List<Tile>();
            this.getPotentialReactionTiles(newAtomTile, potentialReactionTiles);

            // It's fast, and it's great, but fails in wierd cases.
            // Like B-Fl-Fl-Fl, where you place the Fl closest to the B last.
            // If we carry over connected atoms, this case works, but other cases bork.
            // Like this case:
            // B Fl
            // B Fl
            //   Fl

            IList<Molecule> potentialMolecules = this.calculateReactionsUsingBestFirstSearch(potentialReactionTiles, newAtomTile, new List<Tile>(), new List<Tile>());

            // If best-first didn't find anything, revert to brute-froce.
            if (potentialMolecules.Count == 0)
            {
                potentialMolecules = this.calculateReactionsUsingBruteForceCombinations(potentialReactionTiles, newAtomTile);
            }

            // All of the following calls are not necessary in the brute-force case.
            // But the performance is trivial, so, do it anyway to simplify the code.
            // Weed out net != 0
            potentialMolecules = potentialMolecules.Where(m => m.NetCharge == 0).ToList();

            // Fix for bug where you can form |-shaped HCl around a Beryllium atom
            potentialMolecules = weedOutNonConnectedMolecules(potentialMolecules);

            // Sort by electronegativity, get the top result
            Molecule formedMolecule = getMostElectronegative(potentialMolecules);

            IList<Tile> toReturn = new List<Tile>();
            if (formedMolecule != null)
            {
                foreach (Tile t in formedMolecule.AtomTiles)
                {
                    toReturn.Add(t);
                }
            }

            return toReturn;
        }
예제 #5
0
        // It's complicated. We use two collections: one that looks at ALL the atoms we see, ever,
        // and one that looks at only the atoms we've seen so far -- the latter back-pedals when
        // we run out of adjacencies, and the former perseveres. This covers all kinds of cases,
        // like (* indicates last atom placed):
        // B Fl
        // B Fl
        //   Fl*
        // ... which is covered by back-pedalling only, and like:
        // B Cl* Cl Cl
        // ... which is covered by looking at the total only.
        private IList<Molecule> calculateReactionsUsingBestFirstSearch(IList<Tile> potentialReactionTiles, Tile centerTile, IList<Tile> foundSoFarInPath, IList<Tile>foundSoFarInTotal)
        {
            // These IFs cover a tricky case where we search exhaustively and don't find
            // a molecule, then when backtracking, backtrack into something we've seen
            // already. We shouldn't add a tile twice, and this fixes the bug that does that
            // (visible in puzzle one -- it adds the bottom-most Fl twice (6, 3).)
            if (!foundSoFarInPath.Contains(centerTile))
            {
                foundSoFarInPath.Add(centerTile);
            }

            if (!foundSoFarInTotal.Contains(centerTile))
            {
                foundSoFarInTotal.Add(centerTile);
            }

            Molecule m1 = new Molecule();
            foreach (Tile t in foundSoFarInPath)
            {
                m1.AtomTiles.Add(t);
            }

            if (m1.NetCharge == 0)
            {
                // Base case: we're net zero!
                List<Molecule> toReturn = new List<Molecule>();
                toReturn.Add(m1);
                return toReturn;
            }

            Molecule m2 = new Molecule();
            foreach (Tile t in foundSoFarInTotal)
            {
                m2.AtomTiles.Add(t);
            }

            if (m2.NetCharge == 0)
            {
                // Base case: we're net zero!
                List<Molecule> toReturn = new List<Molecule>();
                toReturn.Add(m2);
                return toReturn;
            }

            // Recursive case: keep looking.
            // Find adjacent molecules
            IList<Tile> adjacents = potentialReactionTiles.Where(t => TowerUtils.DistanceBetweenPoints(t.X, t.Y, centerTile.X, centerTile.Y) == 1).ToList();

            // Exclude tiles we already saw. Use current path, for back-pedalling.
            adjacents = adjacents.Where(t => !foundSoFarInPath.Contains(t) && !foundSoFarInTotal.Contains(t)).ToList();

            // Sort by |my valence + their valence|;
            adjacents = adjacents.OrderBy(t => t, new OrderTilesByElectronegativityWithAtomComparer(centerTile.Atom)).ToList();

            // Base case: no adjacents.
            if (adjacents.Count == 0)
            {
                return new List<Molecule>();
            }
            else
            {
                // Take the best, and move on
                foreach (Tile adj in adjacents)
                {
                    // Copy foundSoFar so we can return up the stack and keep looking on
                    // a different path. Without screwing up our "found so far" data.
                    IList<Tile> foundDupe = new List<Tile>();
                    foreach (Tile t in foundSoFarInPath)
                    {
                        foundDupe.Add(t);
                    }

                    IList<Molecule> toReturn = this.calculateReactionsUsingBestFirstSearch(potentialReactionTiles, adj, foundDupe, foundSoFarInTotal);
                    if (toReturn.Count > 0)
                    {
                        return toReturn;
                    }
                }

                // Found nothing.
                return new List<Molecule>();
            }
        }
예제 #6
0
        private bool tileFollowsDifficultyBalancingAlgorithm(Tile current, Atom proposedAtom)
        {
            IList<Tile> adjacents = this.getNonEmptyAdjacentTiles(current);

            if (adjacents.Count == 0)
            {
                return true;
            }
            else
            {
                IList<Tile> oppositeValence;

                if (proposedAtom.IonCharge > 0)
                {
                    oppositeValence = adjacents.Where(t => t.Atom.IonCharge < 0).ToList();
                }
                else
                {
                    oppositeValence = adjacents.Where(t => t.Atom.IonCharge > 0).ToList();
                }

                // Fail if 50% or more of atoms are opposite valence
                if (oppositeValence.Count >= 0.5 * adjacents.Count)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
예제 #7
0
        /// <summary>
        /// Get potential reaction tiles. These are non-empty tiles
        /// that are non-diagonally adjacent to centerTile.
        /// This also includes tiles that are adjacent to adjacent tiles.
        /// </summary>
        /// <param name="centerTile"></param>
        /// <returns></returns>
        private void getPotentialReactionTiles(Tile centerTile, IList<Tile> toReturn)
        {
            if (!toReturn.Contains(centerTile))
            {
                toReturn.Add(centerTile);
            }

            IList<Tile> temp = getNonEmptyAdjacentTiles(centerTile);

            foreach (Tile t in temp.Where(t => !toReturn.Contains(t)))
            {
                // Take adjacencies to all our atoms.
                getPotentialReactionTiles(t, toReturn);
            }
        }
예제 #8
0
        private IList<Tile> getNonEmptyAdjacentTiles(Tile centerTile)
        {
            IList<Tile> toReturn = new List<Tile>();

            if (centerTile.X > 0)
            {
                // left
                toReturn.Add(this._board[centerTile.X - 1, centerTile.Y]);
            }
            if (centerTile.X < BOARD_WIDTH - 1)
            {
                // right
                toReturn.Add(this._board[centerTile.X + 1, centerTile.Y]);
            }

            if (centerTile.Y > 0)
            {
                toReturn.Add(this._board[centerTile.X, centerTile.Y - 1]);
            }
            if (centerTile.Y < BOARD_HEIGHT - 1)
            {
                toReturn.Add(this._board[centerTile.X, centerTile.Y + 1]);
            }

            return toReturn.Where(t => !t.IsEmpty()).ToList();
        }
예제 #9
0
 private void fadeTileIn(Tile t)
 {
     // Be yellow. Fade in.
     t.AtomSprite.ColorOperation = ColorOperation.Add;
     t.AtomSprite.Red = 1;
     t.AtomSprite.Green = 1;
     t.AtomSprite.RedRate = -1;
     t.AtomSprite.GreenRate = -1;
     this._fadingInTiles.Add(t);
 }
예제 #10
0
        private void drawTile(Tile t)
        {
            if (!t.IsEmpty())
            {
                Atom a = t.Atom;
                Sprite s = this.AddSprite("Content/Atoms/" + a.Element + ".png");
                s.X = (t.X * 50) - CENTER_OF_BOARD_X;
                s.Y = (t.Y * -50) + CENTER_OF_BOARD_Y;

                Text e = this.AddText(a.Element);
                e.Scale = 16;
                e.HorizontalAlignment = FlatRedBall.Graphics.HorizontalAlignment.Center;
                e.X = s.X;
                e.Y = s.Y;

                // Do some clean-up. Just incase.
                if (t.AtomSprite != null)
                {
                    this.RemoveResource(t.AtomSprite);
                }

                if (t.AtomText != null)
                {
                    this.RemoveResource(t.AtomText);
                }

                t.AtomSprite = s;
                t.AtomText = e;
            }
        }