Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ShootMe authored and ShootMe committed Apr 14, 2023
1 parent f034b33 commit 5123093
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 41 deletions.
50 changes: 28 additions & 22 deletions Entities/Board.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public unsafe sealed class Board {
internal const int TableauStart = FoundationEnd + 1;
internal const int TableauEnd = TableauStart + TableauSize - 1;
internal const int StockPile = TableauEnd + 1;
private static readonly int[] BitsPerPile = { 21, 21, 21, 21, 18, 17, 15 };
private static readonly int[] BitsPerPile = { 21, 21, 21, 20, 17, 16, 15 };

public bool AllowFoundationToTableau { get; set; }
private readonly Card[] state, initialState, deck;
Expand Down Expand Up @@ -84,6 +84,16 @@ public Board(int drawAmount) {

Shuffle(0);
}
public bool CanAutoPlay() {
return piles[StockPile].Size == 0 && piles[WastePile].Size == 0 &&
(piles[TableauStart].Size == 0 || piles[TableauStart].First == 0) &&
(piles[TableauStart + 1].Size == 0 || piles[TableauStart + 1].First == 0) &&
(piles[TableauStart + 2].Size == 0 || piles[TableauStart + 2].First == 0) &&
(piles[TableauStart + 3].Size == 0 || piles[TableauStart + 3].First == 0) &&
(piles[TableauStart + 4].Size == 0 || piles[TableauStart + 4].First == 0) &&
(piles[TableauStart + 5].Size == 0 || piles[TableauStart + 5].First == 0) &&
(piles[TableauStart + 6].Size == 0 || piles[TableauStart + 6].First == 0);
}
public void PlayMoves(string moves) {
Reset();

Expand Down Expand Up @@ -141,24 +151,20 @@ public SolveDetail SolveRandom(int randomGamesToTry = 40000, int maxMoves = 250,
for (int i = 0; i < randomGamesToTry; i++) {
Reset();

Estimate movesEstimate = Estimate;
int movesMadeRnd = 0;
do {
moves.Clear();
GetAvailableMoves(moves);
if (moves.Count > 0) {
Move move = GetRandomMove(moves);
int newMoves = MovesAdded(move);
MakeMove(move);
if (moves.Count == 0) { break; }

int newCurrent = movesEstimate.Current + newMoves;
if (newCurrent > 255) { newCurrent = 255; }
movesEstimate = new Estimate() { Current = (byte)newCurrent, Remaining = (byte)MinimumMovesRemaining(roundCount == maxRounds) };
}
} while (moves.Count > 0 && movesEstimate.Total <= maxMoves && roundCount <= maxRounds);
Move move = GetRandomMove(moves);
movesMadeRnd += MovesAdded(move);
MakeMove(move);
} while (movesMadeRnd < bestMoves && roundCount <= maxRounds);

if (foundationCount >= bestCount) {
if (Solved) {
if (movesEstimate.Total < bestMoves) {
if (movesMadeRnd < bestMoves) {
bestMoves = MovesMade;
solutionCount = movesTotal;
Array.Copy(movesMade, solution, movesTotal);
Expand Down Expand Up @@ -281,7 +287,7 @@ public SolveDetail Solve(int maxMoves = 250, int maxRounds = 20, int maxNodes =
}

if (!Solved) {
short heuristic = (short)((newEstimate.Total << 2) + (DeckSize - foundationCount + (roundCount << 1)));
short heuristic = (short)((newEstimate.Total << 2) + movesAdded + (DeckSize - foundationCount + (roundCount << 1)));
open.Enqueue(new MoveIndex() { Index = nodeCount++, Priority = heuristic, Estimate = newEstimate });
if (nodeCount >= maxNodes) { break; }
}
Expand All @@ -303,10 +309,12 @@ public SolveDetail Solve(int maxMoves = 250, int maxRounds = 20, int maxNodes =
}
}

SolveResult result = nodeCount < maxNodes ? maxFoundationCount == DeckSize ? !terminateEarly ? SolveResult.Minimal : SolveResult.Solved : SolveResult.Impossible : maxFoundationCount == DeckSize ? SolveResult.Solved : SolveResult.Unknown;
return new SolveDetail() {
Result = nodeCount < maxNodes ? maxFoundationCount == DeckSize ? !terminateEarly ? SolveResult.Minimal : SolveResult.Solved : SolveResult.Impossible : maxFoundationCount == DeckSize ? SolveResult.Solved : SolveResult.Unknown,
Result = result,
States = nodeCount,
Time = timer.Elapsed
Time = timer.Elapsed,
Moves = result == SolveResult.Solved || result == SolveResult.Minimal ? MovesMade : 0
};
}
public SolveDetail SolveFast(int maxMoves = 250, int maxRounds = 20, int maxNodes = 2000000) {
Expand Down Expand Up @@ -551,13 +559,11 @@ private bool CheckTableau(List<Move> moves, bool allMoves = false) {
if (cardFoundation != 255) {
Move temp = new Move(i, cardFoundation, 1, pileFromSize > 1 && pileFrom.UpSize == 1);
//is this an auto move?
if ((int)fromBottom.Rank <= foundationMinimum) {
if (!allMoves && (int)fromBottom.Rank <= foundationMinimum) {
moves.Clear();
moves.Add(temp);
if (!allMoves) {
return true;
}
} else if (lastMove.Flip || lastMove.From != cardFoundation || lastMove.To != i) {
return true;
} else {
moves.Add(temp);
}
}
Expand Down Expand Up @@ -639,12 +645,12 @@ private void CheckFoundation(List<Move> moves) {
Pile foundPile = piles[i];
int foundationSize = foundPile.Size;
int foundationMinimum = foundationMinimumBlack < foundationMinimumRed ? foundationMinimumBlack : foundationMinimumRed;
if (foundationSize == 0 || foundationSize <= foundationMinimum) { continue; }
if (foundationSize <= foundationMinimum) { continue; }

Card foundCard = foundPile.BottomNoCheck;
for (byte j = TableauStart; j <= TableauEnd; ++j) {
Card cardTop = piles[j].Bottom;
if (cardTop.Rank - foundCard.Rank == 1 && foundCard.IsRed != cardTop.IsRed && (lastMove.Flip || lastMove.From != j || lastMove.To != i)) {
if (cardTop.Rank - foundCard.Rank == 1 && foundCard.IsRed != cardTop.IsRed) {
moves.Add(new Move(i, j));
if (foundCard.Rank == CardRank.King) { break; }
}
Expand Down
1 change: 1 addition & 0 deletions Entities/SolveResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public struct SolveDetail {
public SolveResult Result;
public int States;
public TimeSpan Time;
public int Moves;

public override string ToString() {
return $"{Result} ({States})";
Expand Down
64 changes: 45 additions & 19 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,53 @@
namespace Klondike {
public class Program {
public static void Main(string[] args) {
Stopwatch sw = new Stopwatch();
sw.Start();
if (args != null && args.Length > 0 && ((args.Length - 1) & 1) == 1) {
Console.WriteLine($"Invalid argument count.");
args = null;
}

//113 Moves ~8 sec
//SolveGame(123);
if (args == null || args.Length == 0) {
Console.WriteLine(
@$"Minimal Klondike
Klondike.exe [Options] [CardSet]
//115 Moves ~26sec
//SolveGame("081054022072134033082024052064053012061013042093084124092122062031083121113023043074051114091014103044131063041102101133011111071073034123104112021132032094");
DrawCount (Default=1)
-D #
//106 Moves ~3sec - Playing a few moves manually before solving ~0.2sec
SolveGame(888, 1, "IC @@AL KL @@AK @@@@@AE LJ AK LK");
Initial Moves
-M ""Moves To Play Initially""
Example:
Klondike.exe -D 1 -M ""HE KE @@@@AD GD LJ @@AH @@AJ GJ @@@@AG @AB"" 081054022072134033082024052064053012061013042093084124092122062031083121113023043074051114091014103044131063041102101133011111071073034123104112021132032094");
return;
}

string cardSet = args[^1];
int drawCount = 1;
string moveSet = null;

for (int i = 0; i < args.Length - 1; i++) {
if (args[i] == "-D" && i + 1 < args.Length) {
if (!int.TryParse(args[i + 1], out drawCount)) {
Console.WriteLine($"Invalid DrawCount argument {args[i + 1]}. Defaulting to 1.");
drawCount = 1;
}
i++;
} else if (args[i] == "-M" && i + 1 < args.Length) {
moveSet = args[i + 1];
i++;
}
}

Stopwatch sw = new Stopwatch();
sw.Start();

SolveGame(cardSet, drawCount, moveSet, true);

sw.Stop();
Console.WriteLine($"Done {sw.Elapsed}");
}
private static SolveResult SolveGame(int deal, int drawCount = 1, string movesMade = null, bool allowFoundationMoves = false) {
private static SolveDetail SolveGame(int deal, int drawCount = 1, string movesMade = null, bool allowFoundationMoves = false) {
Board board = new Board(drawCount);
board.Shuffle(deal);
if (!string.IsNullOrEmpty(movesMade)) {
Expand All @@ -29,7 +60,7 @@ private static SolveResult SolveGame(int deal, int drawCount = 1, string movesMa

return SolveGame(board);
}
private static SolveResult SolveGame(string deal, int drawCount = 1, string movesMade = null, bool allowFoundationMoves = false) {
private static SolveDetail SolveGame(string deal, int drawCount = 1, string movesMade = null, bool allowFoundationMoves = false) {
Board board = new Board(drawCount);
board.SetDeal(deal);
if (!string.IsNullOrEmpty(movesMade)) {
Expand All @@ -39,23 +70,18 @@ private static SolveResult SolveGame(string deal, int drawCount = 1, string move

return SolveGame(board);
}
private static SolveResult SolveGame(Board board) {
private static SolveDetail SolveGame(Board board) {
Console.WriteLine($"Deal: {board.GetDeal()}");
Console.WriteLine();
Console.WriteLine(board);

Stopwatch sw = new Stopwatch();
sw.Start();

SolveDetail result = board.Solve(200, 10, 100000000);

sw.Stop();
SolveDetail result = board.Solve(250, 15, 50_000_000);

Console.WriteLine($"Moves: {board.MovesMadeOutput}");
Console.WriteLine();
Console.WriteLine($"(Deal State: {result.Result} Foundation: {board.CardsInFoundation} Moves: {board.MovesMade} Rounds: {board.TimesThroughDeck} Took: {sw.Elapsed})");
Console.WriteLine($"(Deal State: {result.Result} Foundation: {board.CardsInFoundation} Moves: {board.MovesMade} Rounds: {board.TimesThroughDeck} Took: {result.Time})");

return result.Result;
return result;
}
}
}

0 comments on commit 5123093

Please sign in to comment.