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; }
private IList<Molecule> getIncrementalCombinations(IList<Tile> potentialReactionTiles, IList<Molecule> previousSet) { IList<Molecule> toReturn = new List<Molecule>(); // This thread needs to be more sleepy. To prevent lag. int throttleCount = 0; const int THROTTLE_AFTER_COUNT = 10; // Trick: if we are generating combos of five, and we just did twos, we have: // AB, AC, AD, BC, BD, BE, CD, CE // We now take each of these, and add whatever's after the last one // eg. for AB, we look at: C, D, E to make ABC, ABD, ABE! // BRILLIANT, I tell you!! foreach (Molecule m in previousSet) { Tile lastTile = m.AtomTiles[m.AtomTiles.Count - 1]; IList<Tile> validTiles = new List<Tile>(); for (int i = potentialReactionTiles.IndexOf(lastTile) + 1; i < potentialReactionTiles.Count; i++) { validTiles.Add(potentialReactionTiles[i]); } foreach (Tile t in validTiles) { Molecule newbie = new Molecule(); foreach (Tile at in m.AtomTiles) { newbie.AtomTiles.Add(at); } newbie.AtomTiles.Add(t); toReturn.Add(newbie); throttleCount++; throttleCount %= THROTTLE_AFTER_COUNT; if (throttleCount == THROTTLE_AFTER_COUNT - 1) { Thread.Sleep(10); } } } return toReturn; }
// 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>(); } }