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 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 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); }