public void Find() { // Check for swaps. int maxExtraSuits = ExtraSuits(FindTableau.NumberOfSpaces); for (int from = 0; from < NumberOfPiles; from++) { Pile fromPile = FindTableau[from]; int splitRow = fromPile.Count - RunFinder.GetRunUpAnySuit(from); int extraSuits = 0; HoldingStack holdingStack = HoldingStacks[from]; for (int fromRow = fromPile.Count - 1; fromRow >= splitRow; fromRow--) { if (fromRow < fromPile.Count - 1) { if (fromPile[fromRow].Suit != fromPile[fromRow + 1].Suit) { // This is a cross-suit run. extraSuits++; if (extraSuits > maxExtraSuits + holdingStack.Suits) { break; } } } Check(from, fromRow, extraSuits, maxExtraSuits); } } }
private void FindUncoveringMoves(int maxExtraSuits) { // Find all uncovering moves. UncoveringMoves.Clear(); for (int from = 0; from < NumberOfPiles; from++) { Pile fromPile = FindTableau[from]; int fromRow = fromPile.Count - RunFinder.GetRunUpAnySuit(from); if (fromRow == 0) { continue; } int fromSuits = RunFinder.CountSuits(from, fromRow); Card fromCard = fromPile[fromRow]; PileList faceList = FaceLists[(int)fromCard.Face + 1]; for (int i = 0; i < faceList.Count; i++) { HoldingStack.Clear(); int to = faceList[i]; if (fromSuits - 1 > maxExtraSuits) { int holdingSuits = FindHolding(FindTableau, HoldingStack, false, fromPile, from, fromRow, fromPile.Count, to, maxExtraSuits); if (fromSuits - 1 > maxExtraSuits + holdingSuits) { break; } } Pile toPile = FindTableau[to]; Card toCard = toPile[toPile.Count - 1]; int order = GetOrder(toCard, fromCard); UncoveringMoves.Add(new Move(from, fromRow, to, order, AddHolding(HoldingStack.Set))); } } }
private void SetVariation() { Tableau.Variation = Variation; NumberOfPiles = Variation.NumberOfPiles; NumberOfSuits = Variation.NumberOfSuits; HoldingStacks = new HoldingStack[NumberOfPiles]; for (int column = 0; column < NumberOfPiles; column++) { HoldingStacks[column] = new HoldingStack(); } }
private bool TryAddOneRunMove(int oneRun, int target) { Pile oneRunPile = WorkingTableau[oneRun]; if (oneRunPile.Count == 0) { return(false); } Card oneRunRootCard = oneRunPile[0]; // Check whether the one run pile matches the target pile. Card targetCard = WorkingTableau.GetCard(target); if (!oneRunRootCard.IsSourceFor(targetCard)) { return(false); } // Check whether we can make the move. int oneRunSuits = oneRunPile.CountSuits(); int oneRunMaxExtraSuits = ExtraSuits(GetNumberOfSpacesLeft()); HoldingStack.Clear(); if (oneRunSuits - 1 > oneRunMaxExtraSuits) { oneRunSuits -= FindHolding(WorkingTableau, HoldingStack, false, oneRunPile, oneRun, 0, oneRunPile.Count, target, oneRunMaxExtraSuits); if (oneRunSuits - 1 > oneRunMaxExtraSuits) { // Not enough spaces and/or holding piles. return(false); } } // Handle the messy cases. if (Offload.IsEmpty || Offload.SinglePile && FindTableau.GetDownCount(oneRun) == 0) { // Save working state. SaveWorkingState(); // Found a home for the one run pile. AddSupplementaryMove(new Move(oneRun, 0, target), oneRunPile, HoldingStack.Set, true); if (AddMove(order + GetOrder(targetCard, oneRunRootCard))) { return(true); } // Restore working state. RestoreWorkingState(); } return(false); }
public CompositeSinglePileMoveFinder(Game game) : base(game) { UncoveringMoves = new MoveList(); OneRunPiles = new PileList(); Used = new PileList(); Roots = new PileList(); WorkingTableau = new Tableau(); HoldingStack = new HoldingStack(); SupplementaryMoves = new MoveList(); MoveStack = new FastList <Move>(); }
public CompositeSinglePileMoveFinder(Game game) : base(game) { UncoveringMoves = new MoveList(); OneRunPiles = new PileList(); Used = new PileList(); Roots = new PileList(); WorkingTableau = new Tableau(); HoldingStack = new HoldingStack(); SupplementaryMoves = new MoveList(); MoveStack = new FastList<Move>(); }
public int FindHolding(IGetCard map, HoldingStack holdingStack, bool inclusive, Pile fromPile, int from, int fromStart, int fromEnd, int to, int maxExtraSuits) { holdingStack.StartingRow = fromEnd; int firstRow = fromStart + (inclusive ? 0 : 1); int lastRow = fromEnd - fromPile.GetRunUp(fromEnd); int extraSuits = 0; for (int fromRow = lastRow; fromRow >= firstRow; fromRow--) { if (fromRow < lastRow && fromPile[fromRow].Suit != fromPile[fromRow + 1].Suit) { extraSuits++; } if (extraSuits > maxExtraSuits) { return(holdingStack.Suits); } Card fromCard = fromPile[fromRow]; for (int column = 0; column < NumberOfPiles; column++) { if (column == from || column == to) { continue; } if (fromCard.IsSourceFor(map.GetCard(column))) { int holdingSuits = extraSuits; if (fromRow == fromStart || fromCard.Suit != fromPile[fromRow - 1].Suit) { holdingSuits++; } if (holdingSuits > holdingStack.Suits) { int length = holdingStack.FromRow - fromRow; holdingStack.Push(new HoldingInfo(from, fromRow, column, holdingSuits, length)); } } } } return(holdingStack.Suits); }
private void Check(int from, int fromRow, int extraSuits, int maxExtraSuits) { if (fromRow == 0 && FindTableau.GetDownCount(from) != 0) { // Would turn over a card. return; } Pile fromPile = FindTableau[from]; Card fromCard = fromPile[fromRow]; Card fromCardParent = Card.Empty; bool inSequence = true; if (fromRow != 0) { fromCardParent = fromPile[fromRow - 1]; inSequence = fromCardParent.IsTargetFor(fromCard); } HoldingStack fromHoldingStack = HoldingStacks[from]; for (int to = 0; to < NumberOfPiles; to++) { Pile toPile = FindTableau[to]; if (to == from || toPile.Count == 0) { continue; } int splitRow = toPile.Count - RunFinder.GetRunUpAnySuit(to); int toRow = -1; if (inSequence) { // Try to find from counterpart in the first to run. toRow = splitRow + (int)(toPile[splitRow].Face - fromCard.Face); if (toRow < splitRow || toRow >= toPile.Count) { // Sequence doesn't contain our counterpart. continue; } } else { // Try to swap with both runs out of sequence. toRow = splitRow; if (fromRow != 0 && !fromCardParent.IsTargetFor(toPile[toRow])) { // Cards don't match. continue; } } if (toRow == 0) { if (fromRow == 0) { // No point in swap both entire piles. continue; } if (FindTableau.GetDownCount(to) != 0) { // Would turn over a card. continue; } } else if (!toPile[toRow - 1].IsTargetFor(fromCard)) { // Cards don't match. continue; } int toSuits = RunFinder.CountSuits(to, toRow); if (extraSuits + toSuits <= maxExtraSuits) { // Swap with no holding piles. Algorithm.ProcessCandidate(new Move(MoveType.Swap, from, fromRow, to, toRow)); continue; } HoldingStack toHoldingStack = HoldingStacks[to]; if (extraSuits + toSuits > maxExtraSuits + fromHoldingStack.Suits + toHoldingStack.Suits) { // Not enough spaces. continue; } Used.Clear(); Used.Add(from); Used.Add(to); int fromHoldingCount = 0; int toHoldingCount = 0; int fromHoldingSuits = 0; int toHoldingSuits = 0; while (true) { if (fromHoldingCount < fromHoldingStack.Count && fromHoldingStack[fromHoldingCount].FromRow >= fromRow && !Used.Contains(fromHoldingStack[fromHoldingCount].To)) { Used.Add(fromHoldingStack[fromHoldingCount].To); fromHoldingSuits = fromHoldingStack[fromHoldingCount].Suits; fromHoldingCount++; } else if (toHoldingCount < toHoldingStack.Count && toHoldingStack[toHoldingCount].FromRow >= toRow && !Used.Contains(toHoldingStack[toHoldingCount].To)) { Used.Add(toHoldingStack[toHoldingCount].To); toHoldingSuits = toHoldingStack[toHoldingCount].Suits; toHoldingCount++; } else { // Out of options. break; } if (extraSuits + toSuits > maxExtraSuits + fromHoldingSuits + toHoldingSuits) { // Not enough spaces. continue; } // We've found a legal swap. Debug.Assert(toRow == 0 || toPile[toRow - 1].IsTargetFor(fromCard)); Debug.Assert(fromRow == 0 || fromCardParent.IsTargetFor(toPile[toRow])); HoldingSet fromHoldingSet = new HoldingSet(fromHoldingStack, fromHoldingCount); HoldingSet toHoldingSet = new HoldingSet(toHoldingStack, toHoldingCount); Algorithm.ProcessCandidate(new Move(MoveType.Swap, from, fromRow, to, toRow, AddHolding(fromHoldingSet, toHoldingSet))); break; } } }
public void Find() { Candidates.Clear(); SupplementaryList.Clear(); int numberOfSpaces = FindTableau.NumberOfSpaces; int maxExtraSuits = ExtraSuits(numberOfSpaces); int maxExtraSuitsToSpace = ExtraSuits(numberOfSpaces - 1); for (int from = 0; from < NumberOfPiles; from++) { HoldingStack holdingStack = HoldingStacks[from]; Pile fromPile = FindTableau[from]; holdingStack.Clear(); holdingStack.StartingRow = fromPile.Count; int extraSuits = 0; for (int fromRow = fromPile.Count - 1; fromRow >= 0; fromRow--) { Card fromCard = fromPile[fromRow]; if (fromCard.IsEmpty) { break; } if (fromRow < fromPile.Count - 1) { Card previousCard = fromPile[fromRow + 1]; if (!previousCard.IsSourceFor(fromCard)) { break; } if (fromCard.Suit != previousCard.Suit) { // This is a cross-suit run. extraSuits++; if (extraSuits > maxExtraSuits + holdingStack.Suits) { break; } } } // Add moves to other piles. if (fromCard.Face < Face.King) { PileList piles = FaceLists[(int)fromCard.Face + 1]; for (int i = 0; i < piles.Count; i++) { for (int count = 0; count <= holdingStack.Count; count++) { HoldingSet holdingSet = new HoldingSet(holdingStack, count); if (extraSuits > maxExtraSuits + holdingSet.Suits) { continue; } int to = piles[i]; if (from == to || holdingSet.Contains(from)) { continue; } // We've found a legal move. Pile toPile = FindTableau[to]; Algorithm.ProcessCandidate(new Move(from, fromRow, to, toPile.Count, AddHolding(holdingSet))); // Update the holding pile move. int holdingSuits = extraSuits; if (fromRow > 0 && (!fromPile[fromRow - 1].IsTargetFor(fromCard) || fromCard.Suit != fromPile[fromRow - 1].Suit)) { holdingSuits++; } if (holdingSuits > holdingStack.Suits) { int length = holdingStack.FromRow - fromRow; holdingStack.Push(new HoldingInfo(from, fromRow, to, holdingSuits, length)); } break; } } } // Add moves to an space. for (int i = 0; i < FindTableau.NumberOfSpaces; i++) { int to = FindTableau.Spaces[i]; if (fromRow == 0) { // No point in moving from a full pile // from one open position to another unless // there are more cards to turn over. if (FindTableau.GetDownCount(from) == 0) { continue; } } else { // No point in moving anything less than // as much as possible to an space. Card nextCard = fromPile[fromRow - 1]; if (fromCard.Suit == nextCard.Suit) { if (nextCard.IsTargetFor(fromCard)) { continue; } } } for (int count = 0; count <= holdingStack.Count; count++) { HoldingSet holdingSet = new HoldingSet(holdingStack, count); if (holdingSet.FromRow == fromRow) { // No cards left to move. continue; } if (extraSuits > maxExtraSuitsToSpace + holdingSet.Suits) { // Not enough spaces. continue; } // We've found a legal move. Pile toPile = FindTableau[to]; Algorithm.ProcessCandidate(new Move(from, fromRow, to, toPile.Count, AddHolding(holdingSet))); break; } // Only need to check the first space // since all spaces are the same // except for undealt cards. break; } } } }
private void CheckOffload(int to) { if (Offload.IsEmpty) { // No offload to check. return; } Pile offloadPile = WorkingTableau[Offload.To]; if (offloadPile.Count == 0) { // A discard emptied the offload pile. Offload = OffloadInfo.Empty; return; } Card offloadRootCard = offloadPile[0]; int offloadSuits = offloadPile.CountSuits(); int offloadMaxExtraSuits = ExtraSuits(GetNumberOfSpacesLeft()); MoveType offloadType = Offload.SinglePile ? MoveType.Basic : MoveType.Reload; // Check whether offload matches from pile. Card fromCard = WorkingTableau.GetCard(from); if (offloadRootCard.IsSourceFor(fromCard)) { // Check whether we can make the move. bool canMove = true; HoldingStack.Clear(); if (Offload.SinglePile && offloadSuits - 1 > offloadMaxExtraSuits) { // Not enough spaces. offloadSuits -= FindHolding(WorkingTableau, HoldingStack, false, offloadPile, Offload.To, 0, offloadPile.Count, from, offloadMaxExtraSuits); if (offloadSuits - 1 > offloadMaxExtraSuits) { // Not enough spaces and/or holding piles. canMove = false; } } if (canMove) { // Save working state. SaveWorkingState(); // Offload matches from pile. AddSupplementaryMove(new Move(offloadType, Offload.To, 0, from), offloadPile, HoldingStack.Set, true); // Add the intermediate move. AddMove(order + GetOrder(fromCard, offloadRootCard)); // Restore working state. RestoreWorkingState(); } } // Check whether offoad matches to pile. Card toCard = WorkingTableau.GetCard(to); if (offloadRootCard.IsSourceFor(toCard)) { // Check whether we can make the move. bool canMove = true; HoldingStack.Clear(); if (Offload.SinglePile && offloadSuits - 1 > offloadMaxExtraSuits) { // Not enough spaces. offloadSuits -= FindHolding(WorkingTableau, HoldingStack, false, offloadPile, Offload.To, 0, offloadPile.Count, to, offloadMaxExtraSuits); if (offloadSuits - 1 > offloadMaxExtraSuits) { // Not enough spaces and/or holding piles. canMove = false; } } if (canMove) { // Record the order improvement. order += GetOrder(toCard, offloadRootCard); // Found a home for the offload. AddSupplementaryMove(new Move(offloadType, Offload.To, 0, to), offloadPile, HoldingStack.Set, true); // Update the state. Offload = OffloadInfo.Empty; } } }
private void CheckOne(Move uncoveringMove) { // Prepare data structures. order = 0; int runs = Roots.Count - 1; Offload = OffloadInfo.Empty; SupplementaryMoves.Clear(); // Initialize the pile map. WorkingTableau.Clear(); WorkingTableau.CopyUpPiles(FindTableau); WorkingTableau.BlockDownPiles(FindTableau); if (!uncoveringMove.IsEmpty) { // Update the map for the uncovering move but don't // include its order contribution so we don't make // the uncovering move unless it is really necessary. MoveStack.Clear(); for (int next = uncoveringMove.HoldingNext; next != -1; next = SupplementaryList[next].Next) { Move holdingMove = SupplementaryList[next]; Move forwardMove = new Move(MoveType.Basic, MoveFlags.Holding, holdingMove.From, holdingMove.FromRow, holdingMove.To); SupplementaryMoves.Add(forwardMove); WorkingTableau.Move(forwardMove); MoveStack.Push(new Move(MoveType.Basic, MoveFlags.UndoHolding, holdingMove.To, -holdingMove.ToRow, uncoveringMove.To)); } SupplementaryMoves.Add(uncoveringMove); WorkingTableau.Move(uncoveringMove); while (MoveStack.Count > 0) { Move holdingMove = MoveStack.Pop(); if (!WorkingTableau.IsValid(holdingMove)) { break; } SupplementaryMoves.Add(holdingMove); WorkingTableau.Move(holdingMove); } } // Check all the roots. int offloads = 0; for (int n = 1; n < Roots.Count; n++) { int rootRow = Roots[n]; Card rootCard = fromPile[rootRow]; int runLength = Roots[n - 1] - Roots[n]; int suits = fromPile.CountSuits(rootRow, rootRow + runLength); int maxExtraSuits = ExtraSuits(GetNumberOfSpacesLeft()); bool suitsMatch = false; HoldingStack.Clear(); // Try to find the best matching target. int to = -1; for (int i = 0; i < NumberOfPiles; i++) { if (i == from) { continue; } Card card = WorkingTableau.GetCard(i); if (card.IsTargetFor(rootCard)) { if (!Offload.IsEmpty && to == Offload.To) { to = -1; suitsMatch = false; } if (!suitsMatch && card.Suit == rootCard.Suit) { to = i; suitsMatch = true; } else if (to == -1) { to = i; } } } MoveType type = MoveType.Basic; bool isOffload = false; if (to != -1) { // Check for inverting. if (!Offload.IsEmpty && to == Offload.To) { if (!Offload.SinglePile) { // Not enough spaces to invert. return; } } // Try to move this run. if (suits - 1 > maxExtraSuits) { // Try using holding piles. suits -= FindHolding(WorkingTableau, HoldingStack, false, fromPile, from, rootRow, rootRow + runLength, to, maxExtraSuits); if (suits - 1 > maxExtraSuits) { // Not enough spaces. return; } } // Record the order improvement. order += GetOrder(true, suitsMatch); } else { if (!Offload.IsEmpty) { // Already have an offload. return; } // It doesn't make sense to offload the last root. if (rootRow == 0) { if (runs - 1 >= 2) { AddMove(order); } return; } // Check for partial offload. if (offloads > 0) { AddMove(order); } // Try to offload this run. if (GetNumberOfSpacesLeft() == 0) { // Not enough spaces. return; } to = WorkingTableau.Spaces[0]; int maxExtraSuitsOnePile = ExtraSuits(GetNumberOfSpacesLeft() - 1) + 1; if (suits > maxExtraSuitsOnePile) { // Try using holding piles. suits -= FindHolding(WorkingTableau, HoldingStack, false, fromPile, from, rootRow, rootRow + runLength, to, maxExtraSuits); if (suits > maxExtraSuits) { // Still not enough spaces. return; } } int numberOfSpacesUsed = SpacesUsed(GetNumberOfSpacesLeft(), suits); Offload = new OffloadInfo(to, numberOfSpacesUsed); type = Offload.SinglePile ? MoveType.Basic : MoveType.Unload; isOffload = true; offloads++; } // Do the move and the holding moves. HoldingSet holdingSet = HoldingStack.Set; bool undoHolding = !isOffload; AddSupplementaryMove(new Move(type, from, rootRow, to), fromPile, holdingSet, undoHolding); // Check whether the offload matches the new from or to piles. if (!isOffload) { CheckOffload(to); } // Check whether any of the one run piles now match // the new from or to piles. for (int i = 0; i < OneRunPiles.Count; i++) { if (CheckOneRun(to, OneRunPiles[i])) { // Found an emptying move. return; } } } // Check for unload that needs to be reloaded. if (!Offload.IsEmpty && !Offload.SinglePile) { if (FindTableau.GetDownCount(from) != 0) { // Can't reload. return; } else { // Reload the offload onto the now empty space. SupplementaryMoves.Add(new Move(MoveType.Reload, Offload.To, 0, from, 0)); } } // Add the move. AddMove(order); }
public int FindHolding(IGetCard map, HoldingStack holdingStack, bool inclusive, Pile fromPile, int from, int fromStart, int fromEnd, int to, int maxExtraSuits) { holdingStack.StartingRow = fromEnd; int firstRow = fromStart + (inclusive ? 0 : 1); int lastRow = fromEnd - fromPile.GetRunUp(fromEnd); int extraSuits = 0; for (int fromRow = lastRow; fromRow >= firstRow; fromRow--) { if (fromRow < lastRow && fromPile[fromRow].Suit != fromPile[fromRow + 1].Suit) { extraSuits++; } if (extraSuits > maxExtraSuits) { return holdingStack.Suits; } Card fromCard = fromPile[fromRow]; for (int column = 0; column < NumberOfPiles; column++) { if (column == from || column == to) { continue; } if (fromCard.IsSourceFor(map.GetCard(column))) { int holdingSuits = extraSuits; if (fromRow == fromStart || fromCard.Suit != fromPile[fromRow - 1].Suit) { holdingSuits++; } if (holdingSuits > holdingStack.Suits) { int length = holdingStack.FromRow - fromRow; holdingStack.Push(new HoldingInfo(from, fromRow, column, holdingSuits, length)); } } } } return holdingStack.Suits; }
public int FindHolding(IGetCard map, HoldingStack holdingStack, bool inclusive, Pile fromPile, int from, int fromStart, int fromEnd, int to, int maxExtraSuits) { return game.FindHolding(map, holdingStack, inclusive, fromPile, from, fromStart, fromEnd, to, maxExtraSuits); }
public int FindHolding(IGetCard map, HoldingStack holdingStack, bool inclusive, Pile fromPile, int from, int fromStart, int fromEnd, int to, int maxExtraSuits) { return(game.FindHolding(map, holdingStack, inclusive, fromPile, from, fromStart, fromEnd, to, maxExtraSuits)); }