public void Process(Move move) { if (ComplexMoves) { MakeMove(move); } else { ConvertToSimpleMoves(move); } }
public bool IsReversible(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]; 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; return oldOrderFrom != 0 && (!isSwap || oldOrderTo != 0); }
private void ConvertToSimpleMoves(Move move) { if (Diagnostics) { Utils.WriteLine("CTSM: {0}", move); } // First move to the holding piles. HoldingMoveStack.Clear(); for (int holdingNext = move.HoldingNext; holdingNext != -1; holdingNext = SupplementaryList[holdingNext].Next) { Move holdingMove = SupplementaryList[holdingNext]; int undoFromRow = Tableau[holdingMove.To].Count; MakeMoveUsingSpaces(holdingMove.From, holdingMove.FromRow, holdingMove.To); HoldingMoveStack.Push(new Move(holdingMove.To, undoFromRow, holdingMove.From == move.From ? move.To : move.From)); } if (move.Type == MoveType.CompositeSinglePile) { // Composite single pile move. MakeCompositeSinglePileMove(move.Next); } else if (move.Type == MoveType.Swap) { // Swap move. SwapUsingSpaces(move.From, move.FromRow, move.To, move.ToRow); } else { // Ordinary move. MakeMoveUsingSpaces(move.From, move.FromRow, move.To); } // Lastly move from the holding piles, if we still can. while (HoldingMoveStack.Count > 0) { TryMakeMoveUsingSpaces(HoldingMoveStack.Pop()); } }
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 bool TryMakeMoveUsingSpaces(Move move) { if (Diagnostics) { Utils.WriteLine("TTMMUS: {0}/{1} -> {2}", move.From, move.FromRow, move.To); } if (SimpleMoveIsValid(move)) { if (SafeMakeMoveUsingSpaces(move.From, move.FromRow, move.To) == null) { return true; } } if (Diagnostics) { Utils.WriteLine("*** failed to make move ***"); } return false; }
public void ProcessMove(Move move) { game.ProcessMove(move); }
public void PrintMove(Move move) { Utils.WriteLine(move); for (int next = move.Next; next != -1; next = SupplementaryList[next].Next) { Move nextMove = SupplementaryList[next]; Utils.WriteLine(" {0}", nextMove); } for (int holdingNext = move.HoldingNext; holdingNext != -1; holdingNext = SupplementaryList[holdingNext].Next) { Utils.WriteLine(" holding {0}", SupplementaryList[holdingNext]); } }
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 int GetOneRunDelta(int oldOrder, int newOrder, Move move) { bool fromFree = tableau.GetDownCount(move.From) == 0; bool toFree = tableau.GetDownCount(move.To) == 0; bool fromUpper = GetRunUp(move.From, move.FromRow) == move.FromRow; bool fromLower = move.HoldingNext == -1; bool toUpper = GetRunUp(move.To, move.ToRow) == move.ToRow; bool oldFrom = move.FromRow == 0 ? (fromFree && fromLower) : (fromFree && fromUpper && fromLower && oldOrder == 2); bool newFrom = fromFree && fromUpper; bool oldTo = toFree && toUpper; bool newTo = move.ToRow == 0 ? (toFree && fromLower) : (toFree && toUpper && fromLower && newOrder == 2); int oneRunDelta = (newFrom ? 1 : 0) - (oldFrom ? 1 : 0) + (newTo ? 1 : 0) - (oldTo ? 1 : 0); return oneRunDelta > 0 ? 1 : 0; }
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; }
public void ProcessCandidate(Move move) { if (IsViable(move)) { Candidates.Add(move); } }
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); }
private void AddSupplementaryMove(Move move, Pile pile, HoldingSet holdingSet, bool undoHolding) { // Add moves to the holding piles. for (int i = 0; i < holdingSet.Count; i++) { HoldingInfo holding = holdingSet[i]; Move holdingMove = new Move(MoveType.Basic, MoveFlags.Holding, move.From, -holding.Length, holding.To); WorkingTableau.Move(holdingMove); SupplementaryMoves.Add(holdingMove); } // Add the primary move. WorkingTableau.UncheckedMove(new Move(move.From, move.FromRow, move.To)); SupplementaryMoves.Add(move); if (undoHolding) { // Undo moves from the holding piles. for (int i = holdingSet.Count - 1; i >= 0; i--) { HoldingInfo holding = holdingSet[i]; Move holdingMove = new Move(MoveType.Basic, MoveFlags.UndoHolding, holding.To, -holding.Length, move.To); if (!WorkingTableau.TryMove(holdingMove)) { break; } SupplementaryMoves.Add(holdingMove); } } }
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 void MakeMove(Move move) { if (move.Next != -1) { for (int next = move.Next; next != -1; next = SupplementaryList[next].Next) { Move subMove = SupplementaryList[next]; MakeSingleMove(subMove); } return; } MakeSingleMove(move); }
private int CountUses(Move move) { if (move.FromRow == 0 || move.ToRow != FindTableau[move.To].Count) { // No exposed card, no uses. return 0; } int uses = 0; Pile fromPile = FindTableau[move.From]; Card fromCard = fromPile[move.FromRow]; Card exposedCard = fromPile[move.FromRow - 1]; if (!exposedCard.IsTargetFor(fromCard)) { // Check whether the exposed card will be useful. int numberOfSpaces = FindTableau.NumberOfSpaces - 1; int maxExtraSuits = ExtraSuits(numberOfSpaces); int fromSuits = RunFinder.CountSuits(move.From, move.FromRow); for (int nextFrom = 0; nextFrom < NumberOfPiles; nextFrom++) { if (nextFrom == move.From || nextFrom == move.To) { // Inappropriate column. continue; } Pile nextFromPile = FindTableau[nextFrom]; if (nextFromPile.Count == 0) { // Column is empty. continue; } int nextFromRow = nextFromPile.Count - RunFinder.GetRunUpAnySuit(nextFrom); if (!nextFromPile[nextFromRow].IsSourceFor(exposedCard)) { // Not the card we need. continue; } int extraSuits = RunFinder.CountSuits(nextFrom, nextFromRow) - 1; if (extraSuits <= maxExtraSuits) { // Card leads to a useful move. uses++; } // Check whether the exposed run will be useful. int upperFromRow = move.FromRow - RunFinder.GetRunUp(move.From, move.FromRow); if (upperFromRow != move.FromRow) { Card upperFromCard = fromPile[upperFromRow]; uses += FaceLists[(int)upperFromCard.Face + 1].Count; } } } return uses; }
private void MakeSingleMove(Move move) { // Record the move. if (TraceMoves) { Utils.WriteLine("Move {0}: {1}", Tableau.Moves.Count, move); } // Make the move. Tableau.Move(move); }
public void ProcessCandidate(Move move) { double score = ScoreCalculator.Calculate(move); if (score == Move.RejectScore) { return; } move.Score = score; Candidates.Add(move); }
private bool SimpleMoveIsValid(Move move) { return Tableau.IsValid(move); }
public void ProcessMove(Move move) { MoveProcessor.Process(move); }
public void PrintMove(Move move) { game.PrintMove(move); }