private double CalculateLastResortScore(Move move) { ScoreInfo score = new ScoreInfo(Coefficients, Group1); Pile fromPile = FindTableau[move.From]; Pile toPile = FindTableau[move.To]; Card fromCard = fromPile[move.FromRow]; bool wholePile = move.FromRow == 0; score.UsesSpace = true; score.DownCount = FindTableau.GetDownCount(move.From); score.TurnsOverCard = wholePile && score.DownCount != 0; score.FaceValue = (int)fromCard.Face; score.IsKing = fromCard.Face == Face.King; score.Uses = CountUses(move); if (wholePile) { // Only move an entire pile if there // are more cards to be turned over. if (!score.TurnsOverCard) { return(Move.RejectScore); } } else if (fromPile[move.FromRow - 1].IsTargetFor(fromCard)) { // No point in splitting consecutive cards // unless they are part of a multi-move // sequence. return(Move.RejectScore); } return(score.LastResortScore); }
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); }
private double CalculateCompositeSinglePileScore(Move move) { ScoreInfo score = new ScoreInfo(Coefficients, Group0); score.Order = move.ToRow; score.FaceValue = 0; score.NetRunLength = 0; score.DownCount = FindTableau.GetDownCount(move.From); score.TurnsOverCard = move.Flags.TurnsOverCard(); score.CreatesSpace = move.Flags.CreatesSpace(); score.UsesSpace = move.Flags.UsesSpace(); score.Discards = move.Flags.Discards(); score.IsCompositeSinglePile = true; score.NoSpaces = FindTableau.NumberOfSpaces == 0; score.OneRunDelta = 0; if (score.UsesSpace) { // XXX: should calculate uses, is king, etc. score.Coefficient0 = Group1; return(score.LastResortScore); } return(score.Score); }
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 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 double Calculate(Move move) { if (move.IsEmpty) { return(Move.RejectScore); } ScoreInfo score = new ScoreInfo(Coefficients, Group0); int from = move.From; int fromRow = move.FromRow; int to = move.To; int toRow = move.ToRow; if (move.Type == MoveType.CompositeSinglePile) { return(CalculateCompositeSinglePileScore(move)); } Pile fromPile = FindTableau[from]; Pile toPile = FindTableau[to]; if (toPile.Count == 0) { return(CalculateLastResortScore(move)); } bool isSwap = move.Type == MoveType.Swap; Card fromParent = fromRow != 0 ? fromPile[fromRow - 1] : Card.Empty; Card fromChild = fromPile[fromRow]; Card toParent = toRow != 0 ? toPile[toRow - 1] : Card.Empty; Card toChild = toRow != toPile.Count ? toPile[toRow] : Card.Empty; int oldOrderFrom = GetOrder(fromParent, fromChild); int newOrderFrom = GetOrder(toParent, fromChild); int oldOrderTo = isSwap ? GetOrder(toParent, toChild) : 0; int newOrderTo = isSwap ? GetOrder(fromParent, toChild) : 0; score.Order = newOrderFrom - oldOrderFrom + newOrderTo - oldOrderTo; if (score.Order < 0) { return(Move.RejectScore); } score.Reversible = oldOrderFrom != 0 && (!isSwap || oldOrderTo != 0); score.Uses = CountUses(move); score.OneRunDelta = !isSwap?RunFinder.GetOneRunDelta(oldOrderFrom, newOrderFrom, move) : 0; int faceFrom = (int)fromChild.Face; int faceTo = isSwap ? (int)toChild.Face : 0; score.FaceValue = Math.Max(faceFrom, faceTo); bool wholePile = fromRow == 0 && toRow == toPile.Count; int netRunLengthFrom = RunFinder.GetNetRunLength(newOrderFrom, from, fromRow, to, toRow); int netRunLengthTo = isSwap ? RunFinder.GetNetRunLength(newOrderTo, to, toRow, from, fromRow) : 0; score.NetRunLength = netRunLengthFrom + netRunLengthTo; #if true int newRunLengthFrom = RunFinder.GetNewRunLength(newOrderFrom, from, fromRow, to, toRow); int newRunLengthTo = isSwap ? RunFinder.GetNewRunLength(newOrderTo, to, toRow, from, fromRow) : 0; score.Discards = newRunLengthFrom == 13 || newRunLengthTo == 13; #endif score.DownCount = FindTableau.GetDownCount(from); score.TurnsOverCard = wholePile && score.DownCount != 0; score.CreatesSpace = wholePile && score.DownCount == 0; score.NoSpaces = FindTableau.NumberOfSpaces == 0; if (score.Order == 0 && score.NetRunLength < 0) { return(Move.RejectScore); } int delta = 0; if (score.Order == 0 && score.NetRunLength == 0) { if (!isSwap && oldOrderFrom == 1 && newOrderFrom == 1) { delta = RunFinder.GetRunDelta(from, fromRow, to, toRow); } if (delta <= 0) { return(Move.RejectScore); } } score.IsCompositeSinglePile = false; return(score.Score); }
public bool IsViable(Move move) { int from = move.From; int fromRow = move.FromRow; int to = move.To; int toRow = move.ToRow; Pile fromPile = FindTableau[from]; Pile toPile = FindTableau[to]; if (toPile.Count == 0) { if (fromPile.Count == 0 && FindTableau.GetDownCount(from) == 0) { return(false); } else if (fromRow != 0 && fromPile[fromRow - 1].IsTargetFor(fromPile[fromRow])) { return(false); } return(true); } bool isSwap = move.Type == MoveType.Swap; Card fromParent = fromRow != 0 ? fromPile[fromRow - 1] : Card.Empty; Card fromChild = fromPile[fromRow]; Card toParent = toRow != 0 ? toPile[toRow - 1] : Card.Empty; Card toChild = toRow != toPile.Count ? toPile[toRow] : Card.Empty; int oldOrderFrom = GetOrder(fromParent, fromChild); int newOrderFrom = GetOrder(toParent, fromChild); int oldOrderTo = isSwap ? GetOrder(toParent, toChild) : 0; int newOrderTo = isSwap ? GetOrder(fromParent, toChild) : 0; int order = newOrderFrom - oldOrderFrom + newOrderTo - oldOrderTo; if (order < 0) { return(false); } int netRunLengthFrom = RunFinder.GetNetRunLength(newOrderFrom, from, fromRow, to, toRow); int netRunLengthTo = isSwap ? RunFinder.GetNetRunLength(newOrderTo, to, toRow, from, fromRow) : 0; int netRunLength = netRunLengthFrom + netRunLengthTo; if (order == 0 && netRunLength < 0) { return(false); } int delta = 0; if (order == 0 && netRunLength == 0) { if (!isSwap && oldOrderFrom == 1 && newOrderFrom == 1) { delta = RunFinder.GetRunDelta(from, fromRow, to, toRow); } if (delta <= 0) { return(false); } } return(true); }