private void MakeMove(Node node, int i) { Move move = node.Moves[i]; bool toEmpty = move.Type == MoveType.Basic && WorkingTableau[move.To].Count == 0; MoveStack.Clear(); for (int next = move.HoldingNext; next != -1; next = node.SupplementaryList[next].Next) { Move holdingMove = node.SupplementaryList[next]; WorkingTableau.Move(new Move(MoveType.Basic, MoveFlags.Holding, holdingMove.From, holdingMove.FromRow, holdingMove.To)); int undoTo = holdingMove.From == move.From ? move.To : move.From; MoveStack.Push(new Move(MoveType.Basic, MoveFlags.UndoHolding, holdingMove.To, -holdingMove.ToRow, undoTo)); } WorkingTableau.Move(new Move(move.Type, move.From, move.FromRow, move.To, move.ToRow)); if (!toEmpty) { while (MoveStack.Count > 0) { Move holdingMove = MoveStack.Pop(); if (!WorkingTableau.IsValid(holdingMove)) { break; } WorkingTableau.Move(holdingMove); } } }
private void DepthFirstSearch(int depth) { if (depth == 0) { return; } Algorithm.FindMoves(WorkingTableau); Node node = new Node(new MoveList(Candidates), new MoveList(SupplementaryList)); for (int i = 0; i < node.Moves.Count; i++) { int checkPoint = WorkingTableau.CheckPoint; MakeMove(node, i); bool continueSearch = ProcessNode(); if (NodesSearched >= MaxNodes) { WorkingTableau.Revert(checkPoint); return; } if (continueSearch) { DepthFirstSearch(depth - 1); } WorkingTableau.Revert(checkPoint); if (NodesSearched >= MaxNodes) { return; } } }
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 void RestoreWorkingState() { // Restore state of the working tableau prior to intermediate move. WorkingTableau.Revert(tableauCheckPoint); // Restore state of supplementary moves prior to intermediate move. while (SupplementaryMoves.Count > supplementaryMovesCount) { SupplementaryMoves.RemoveAt(SupplementaryMoves.Count - 1); } }
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 void BreadthFirstSearch(ref Node parent) { if (parent.Moves == null) { Algorithm.FindMoves(WorkingTableau); parent.Moves = new AllocatedList <Move>(MoveAllocator, Candidates); parent.SupplementaryList = new AllocatedList <Move>(MoveAllocator, SupplementaryList); parent.Nodes = new AllocatedList <Node>(NodeAllocator, parent.Moves.Count, parent.Moves.Count); for (int i = 0; i < parent.Moves.Count; i++) { int checkPoint = WorkingTableau.CheckPoint; MakeMove(parent, i); if (ProcessNode()) { parent.Nodes[i] = new Node(true); } WorkingTableau.Revert(checkPoint); if (NodesSearched >= MaxNodes) { return; } } } else { for (int i = 0; i < parent.Moves.Count; i++) { Node child = parent.Nodes[i]; if (child.ContinueSearch) { int checkPoint = WorkingTableau.CheckPoint; MakeMove(parent, i); BreadthFirstSearch(ref child); WorkingTableau.Revert(checkPoint); if (NodesSearched >= MaxNodes) { return; } } parent.Nodes[i] = child; } } }
private bool ProcessNode() { int hashKey = WorkingTableau.GetUpPilesHashKey(); if (TranspositionTable.Contains(hashKey)) { return(false); } TranspositionTable.Add(hashKey); NodesSearched++; double score = CalculateSearchScore(); if (score > Score) { Score = score; Moves.Copy(WorkingTableau.Moves); } return(true); }
public MoveList SearchMoves() { WorkingTableau.Variation = Variation; WorkingTableau.Clear(); WorkingTableau.CopyUpPiles(Tableau); WorkingTableau.BlockDownPiles(Tableau); MoveAllocator.Clear(); NodeAllocator.Clear(); if (UseDepthFirst) { StartSearch(); DepthFirstSearch(MaxDepth); if (NodesSearched >= MaxNodes) { int maxNodesSearched = 0; for (int depth = 1; depth < MaxDepth; depth++) { StartSearch(); DepthFirstSearch(depth); if (NodesSearched == maxNodesSearched) { break; } maxNodesSearched = NodesSearched; if (maxNodesSearched >= MaxNodes) { break; } } } } else { StartSearch(); Node root = new Node(); while (true) { int lastNodesSearched = NodesSearched; BreadthFirstSearch(ref root); if (NodesSearched == lastNodesSearched || NodesSearched >= MaxNodes) { break; } } } if (TraceSearch) { Print(); Utils.WriteLine("search: score = {0}", Score); for (int i = 0; i < Moves.Count; i++) { Utils.WriteLine("search: move[{0}] = {1}", i, Moves[i]); } Utils.WriteLine("Nodes searched: {0}", NodesSearched); } if (Diagnostics) { Utils.WriteLine("search: score = {0}", Score); for (int i = 0; i < Moves.Count; i++) { Utils.WriteLine("search: move[{0}] = {1}", i, Moves[i]); } } return(Moves); }
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); }