Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System.Drawing;
- using System.Runtime.CompilerServices;
- using System.Text;
- using static System.Diagnostics.Debug;
- [assembly: InternalsVisibleTo("solitaireTests")]
- //Solitaire2.Run(StartingBoards.DebugSmall);
- //Solitaire2.Run(StartingBoards.DebugBig);
- Solitaire2.Run(StartingBoards.Real);
- static class StartingBoards
- {
- public static string Real = @"
- XXX
- XXX
- XXXXXXX
- XXXOXXX
- XXXXXXX
- XXX
- XXX ";
- public static string DebugBig = @"
- XXX
- XOX
- XOOXXXX
- OOXXXOX
- XXXXXXX
- XXX
- XXX ";
- public static string DebugSmall = @"
- OOO
- OXO
- OOXOOOO
- OXOOOOO
- OXOOOOO
- OOO
- OOO ";
- }
- static class WriteHelper
- {
- public static void Write(string msg)
- {
- string[] ss = msg.Split('{', '}');
- ConsoleColor c;
- foreach (var s in ss)
- if (s.StartsWith("/"))
- Console.ResetColor();
- else if (s.StartsWith("=") && Enum.TryParse(s.Substring(1), out c))
- Console.ForegroundColor = c;
- else
- Console.Write(s);
- }
- }
- public class Board
- {
- private static readonly HashSet<long> seenBoards = new();
- private readonly byte[,] p_data = new byte[7, 7];
- public void Record()
- {
- seenBoards.Add(Signature());
- }
- public bool IsKnownBoard()
- {
- return seenBoards.Contains(Signature());
- }
- private long Signature()
- {
- long sig = 1;
- for (int row = 0; row < 7; row++)
- {
- for (int col = 0; col < 7; col++)
- {
- if (p_data[row, col] == 2) sig = (sig << 1) + 0b1;
- else if (p_data[row, col] == 1) sig = (sig << 1) + 0b0;
- }
- }
- //Console.WriteLine(Convert.ToString(sig, 2));
- return sig;
- }
- public Board(string startingBoard)
- {
- int charNumber = 0;
- for (int i = 0; i < startingBoard.Length; i++)
- {
- if (startingBoard[i] == '\r' || startingBoard[i] == '\n') continue;
- Assert(startingBoard[i] == ' ' || startingBoard[i] == 'X' || startingBoard[i] == 'O');
- var row = charNumber / 7;
- var col = charNumber % 7;
- charNumber++;
- if (startingBoard[i] == ' ')
- {
- p_data[row, col] = 0;
- }
- else if (startingBoard[i] == 'O')
- {
- p_data[row, col] = 1;
- }
- else if (startingBoard[i] == 'X')
- {
- p_data[row, col] = 2;
- }
- else
- {
- throw new Exception();
- }
- }
- Assert(charNumber == 49);
- }
- private Board(byte[] bytes)
- {
- for (int i = 0; i < bytes.Length; i++)
- {
- Assert(bytes[i] == 0 || bytes[i] == 1 || bytes[i] == 2);
- var row = i / 7;
- var col = i % 7;
- p_data[row, col] = bytes[i];
- }
- Assert(bytes.Length == 49);
- }
- public IList<Move> FindLegalMoves()
- {
- IList<Move> moves = new List<Move>();
- for (int row = 0; row < 7; row++)
- {
- for (int col = 0; col < 7 - 2; col++)
- {
- if (p_data[row, col] == 2 && p_data[row, col + 1] == 2 && p_data[row, col + 2] == 1)
- {
- moves.Add(new Move(Jumping: (row, col), Removed: (row, col + 1), Landing: (row, col + 2)));
- }
- else if (p_data[row, col] == 1 && p_data[row, col + 1] == 2 && p_data[row, col + 2] == 2)
- {
- moves.Add(new Move(Jumping: (row, col + 2), Removed: (row, col + 1), Landing: (row, col)));
- }
- }
- }
- for (int col = 0; col < 7; col++)
- {
- for (int row = 0; row < 7 - 2; row++)
- {
- if (p_data[row, col] == 2 && p_data[row + 1, col] == 2 && p_data[row + 2, col] == 1)
- {
- moves.Add(new Move(Jumping: (row, col), Removed: (row + 1, col), Landing: (row + 2, col)));
- }
- else if (p_data[row, col] == 1 && p_data[row + 1, col] == 2 && p_data[row + 2, col] == 2)
- {
- moves.Add(new Move(Landing: (row, col), Removed: (row + 1, col), Jumping: (row + 2, col)));
- }
- }
- }
- return moves;
- }
- public string Draw()
- {
- return Draw(null);
- }
- public string Draw(Move? selectedMove)
- {
- Dictionary<byte, char> map = new()
- {
- { 0, ' ' },
- { 1, 'O' },
- { 2, 'X' },
- };
- StringBuilder sb = new StringBuilder();
- for (var row = 0; row < 7; row++)
- {
- sb.Append('\n');
- for (var col = 0; col < 7; col++)
- {
- if (selectedMove?.IsJumpingMarble((row, col)) == true)
- {
- sb.Append("{=Red}");
- sb.Append(map[p_data[row, col]]);
- sb.Append("{/}");
- }
- else if (selectedMove?.IsRemovedMarble((row, col)) == true)
- {
- sb.Append("{=Red}");
- sb.Append(map[p_data[row, col]]);
- sb.Append("{/}");
- }
- else if (selectedMove?.IsLandingSpot((row, col)) == true)
- {
- sb.Append("{=Green}");
- sb.Append(map[p_data[row, col]]);
- sb.Append("{/}");
- }
- else
- {
- sb.Append(map[p_data[row, col]]);
- }
- }
- }
- return sb.ToString();
- }
- public bool IsVictory()
- {
- for (var row = 0; row < 7; row++)
- {
- for (var col = 0; col < 7; col++)
- {
- if (row == 3 && col == 3)
- {
- if (p_data[3, 3] != 2) return false;
- }
- else if (p_data[row, col] == 2) return false;
- }
- }
- return true;
- }
- public Board PlayMove(Move? move)
- {
- byte[] bytes = new byte[49];
- int index = 0;
- for (var row = 0; row < 7; row++)
- {
- for (var col = 0; col < 7; col++)
- {
- if (move?.IsJumpingMarble((row, col)) == true)
- {
- bytes[index++] = 1;
- }
- else if (move?.IsRemovedMarble((row, col)) == true)
- {
- bytes[index++] = 1;
- }
- else if (move?.IsLandingSpot((row, col)) == true)
- {
- bytes[index++] = 2;
- }
- else
- {
- bytes[index++] = p_data[row, col];
- }
- }
- }
- return new Board(bytes);
- }
- internal static (int, int) RotateCoord((int, int) coordIn, int quarterTurns)
- {
- int row = coordIn.Item1;
- int col = coordIn.Item2;
- if (quarterTurns == 0)
- { // no rotation
- return (row, col);
- }
- else if (quarterTurns == 1)
- {
- return (col, -row + 6);
- }
- else if (quarterTurns == 2)
- {
- return (6 - row, 6 - col);
- }
- else // if (quarterTurns == 3)
- {
- return (6 - col, row);
- }
- }
- public Board Rotate(int quarterTurns)
- {
- quarterTurns %= 4;
- byte[] bytes = new byte[49];
- for (var row = 0; row < 7; row++)
- {
- for (var col = 0; col < 7; col++)
- {
- (int rotatedRowIndex, int rotatedColIndex) = RotateCoord((row, col), quarterTurns);
- bytes[rotatedRowIndex * 7 + rotatedColIndex] = p_data[row, col];
- }
- }
- return new Board(bytes);
- }
- public bool IsSimilar(Board otherBoard)
- {
- long sig = Signature();
- long otherSig = otherBoard.Signature();
- return sig == otherSig;
- }
- internal void DrawGraphic(Move? move, DrawParameters drawParams)
- {
- Brush boardBrush = new SolidBrush(drawParams.boardColor);
- Brush marbleBrush = new SolidBrush(drawParams.marbleColor);
- Brush highlightedMarbleBrush = new SolidBrush(Color.Green);
- Brush emptySpotBrush = new SolidBrush(Color.Black);
- Pen pen = new Pen(highlightedMarbleBrush, 2.5f);
- drawParams.boardRectangle.X = drawParams.padding;
- drawParams.boardRectangle.Y = drawParams.updateableBoardPosition;
- drawParams.graphics.FillEllipse(boardBrush, drawParams.boardRectangle);
- for (var row = 0; row < 7; row++)
- {
- drawParams.marbleRectangle.Y = drawParams.boardRectangle.Y + drawParams.padding + row * (drawParams.marbleSize + drawParams.padding);
- drawParams.highlightedMarbleRectangle.Y = drawParams.marbleRectangle.Y - (drawParams.highlightedMarbleRectangle.Width - drawParams.marbleRectangle.Width) / 2;
- for (var col = 0; col < 7; col++)
- {
- drawParams.marbleRectangle.X = drawParams.boardRectangle.X + drawParams.padding + col * (drawParams.marbleSize + drawParams.padding);
- drawParams.highlightedMarbleRectangle.X = drawParams.marbleRectangle.X - (drawParams.highlightedMarbleRectangle.Width - drawParams.marbleRectangle.Width) / 2;
- if (p_data[row, col] == 2)
- {
- drawParams.graphics.FillEllipse(marbleBrush, drawParams.marbleRectangle);
- }
- else if (p_data[row, col] == 1)
- {
- drawParams.graphics.FillEllipse(emptySpotBrush, drawParams.marbleRectangle);
- }
- if (move?.IsJumpingMarble((row, col)) == true || move?.IsLandingSpot((row, col)) == true || move?.IsRemovedMarble((row, col)) == true)
- {
- drawParams.graphics.DrawEllipse(pen, drawParams.highlightedMarbleRectangle);
- }
- }
- }
- //drawParams.graphics.DrawEllipse(pen, drawParams.marbleRectangle);
- drawParams.updateableBoardPosition += drawParams.boardSize + drawParams.padding;
- }
- }
- public class Move
- {
- private readonly (int, int) Jumping;
- private readonly (int, int) Removed;
- private readonly (int, int) Landing;
- public Move((int, int) Jumping, (int, int) Removed, (int, int) Landing)
- {
- this.Jumping = Jumping;
- this.Removed = Removed;
- this.Landing = Landing;
- }
- public bool IsJumpingMarble((int, int) coordinate)
- {
- return Jumping.Item1 == coordinate.Item1 && Jumping.Item2 == coordinate.Item2;
- }
- public bool IsRemovedMarble((int, int) coordinate)
- {
- return Removed.Item1 == coordinate.Item1 && Removed.Item2 == coordinate.Item2;
- }
- public bool IsLandingSpot((int, int) coordinate)
- {
- return Landing.Item1 == coordinate.Item1 && Landing.Item2 == coordinate.Item2;
- }
- public override bool Equals(object? obj)
- {
- return obj is Move move &&
- Jumping.Equals(move.Jumping) &&
- Removed.Equals(move.Removed) &&
- Landing.Equals(move.Landing);
- }
- }
- class Solitaire2
- {
- public static void Run(string startingBoard)
- {
- //RunInteractively(startingBoard);
- RunAutomatically(startingBoard);
- }
- public static void RunDebug(string startingBoard)
- {
- Board board = new Board(startingBoard);
- IList<Move> moves = board.FindLegalMoves();
- var boardString = board.Draw();
- WriteHelper.Write(boardString);
- boardString = board.Draw(moves[0]);
- WriteHelper.Write(boardString);
- //board.IsVictory();
- //Board updatedBoard = board.PlayMove(moves[0]);
- }
- class Node
- {
- public readonly Board board;
- private readonly Stack<Move> moves;
- public Node(Board board, IList<Move> moves)
- {
- this.board = board;
- this.moves = new Stack<Move>(moves);
- }
- public bool HasMoves()
- {
- return moves.Count > 0;
- }
- public Move PopMove()
- {
- return moves.Pop();
- }
- }
- public static void RunAutomatically(string startingBoard)
- {
- Stack<Node> stack = new();
- Stack<Move?> recordedMoves = new();
- {
- Board firstBoard = new Board(startingBoard);
- IList<Move> firstMoves = firstBoard.FindLegalMoves();
- stack.Push(new Node(firstBoard, firstMoves));
- recordedMoves.Push(null);
- }
- int counter = 0;
- while (stack.Count > 0)
- {
- Node currentNode = stack.Peek();
- counter++;
- if (counter % 100000 == 0)
- {
- // WriteHelper.Write(counter.ToString());
- // WriteHelper.Write("\n");
- }
- if (currentNode.HasMoves())
- {
- Move nextMove = currentNode.PopMove();
- Board newBoard = currentNode.board.PlayMove(nextMove);
- if (!newBoard.IsKnownBoard()
- && !newBoard.Rotate(1).IsKnownBoard()
- && !newBoard.Rotate(2).IsKnownBoard()
- && !newBoard.Rotate(3).IsKnownBoard()
- )
- {
- recordedMoves.Push(nextMove);
- newBoard.Record();
- IList<Move> newMoves = newBoard.FindLegalMoves();
- stack.Push(new Node(newBoard, newMoves));
- }
- }
- else
- {
- if (currentNode.board.IsVictory())
- {
- DrawParameters drawParams = new(marbleSize: 15, padding: 10, numberMoves: recordedMoves.Count)
- {
- marbleColor = Color.Brown,
- boardColor = Color.DarkGray,
- };
- Board board = new(startingBoard);
- foreach (var move in recordedMoves.Reverse().Skip(1))
- {
- board.DrawGraphic(move, drawParams);
- board = board.PlayMove(move);
- }
- board.DrawGraphic(null, drawParams);
- using (FileStream fs = new FileStream("C:\\Users\\frenchyp\\Downloads\\solitaire.png", FileMode.Create, FileAccess.Write))
- {
- drawParams.bitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
- }
- return;
- }
- recordedMoves.Pop();
- stack.Pop();
- }
- }
- }
- public static void RunInteractively(string startingBoard)
- {
- Board board = new Board(startingBoard);
- bool run = true;
- Move? selectedMove = null;
- int selectedMoveIndex = 0;
- IList<Move> moves = new List<Move>();
- while (run)
- {
- WriteHelper.Write(board.Draw(selectedMove));
- WriteHelper.Write("\n");
- var command = Console.ReadKey(true);
- switch (command.Key)
- {
- case ConsoleKey.Q:
- run = false;
- break;
- case ConsoleKey.Enter:
- if (selectedMove != null)
- {
- board = board.PlayMove(selectedMove);
- }
- moves = board.FindLegalMoves();
- selectedMove = null;
- selectedMoveIndex = 0;
- if (moves.Count > 0)
- {
- selectedMove = moves[selectedMoveIndex];
- }
- else
- {
- if (board.IsVictory()) WriteHelper.Write("VICTORY!");
- else WriteHelper.Write("lost..");
- return;
- }
- break;
- case ConsoleKey.LeftArrow:
- case ConsoleKey.UpArrow:
- selectedMoveIndex++;
- if (selectedMoveIndex >= moves.Count) selectedMoveIndex = 0;
- selectedMove = null;
- if (selectedMoveIndex < moves.Count) selectedMove = moves[selectedMoveIndex];
- break;
- case ConsoleKey.RightArrow:
- case ConsoleKey.DownArrow:
- selectedMoveIndex--;
- if (selectedMoveIndex < 0) selectedMoveIndex = moves.Count - 1;
- if (selectedMoveIndex < 0) selectedMoveIndex = 0;
- selectedMove = null;
- if (selectedMoveIndex < moves.Count) selectedMove = moves[selectedMoveIndex];
- break;
- default:
- break;
- }
- }
- }
- }
- internal class DrawParameters
- {
- internal int marbleSize;
- internal int updateableBoardPosition;
- public Rectangle marbleRectangle;
- public Rectangle highlightedMarbleRectangle;
- public Rectangle boardRectangle;
- public Bitmap bitmap { get; }
- public Graphics graphics { get; }
- internal Color marbleColor;
- internal Color boardColor;
- internal int padding;
- private readonly int numberMoves;
- public DrawParameters(int marbleSize, int padding, int numberMoves)
- {
- this.marbleSize = marbleSize;
- this.padding = padding;
- this.updateableBoardPosition = padding;
- this.marbleRectangle = new Rectangle(0, 0, marbleSize, marbleSize);
- this.highlightedMarbleRectangle = new Rectangle(0, 0, marbleSize + padding / 3, marbleSize + padding / 3);
- this.boardRectangle = new Rectangle(0, 0, boardSize, boardSize);
- this.numberMoves = numberMoves;
- bitmap = new Bitmap(bitmapWidth, bitmapHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
- graphics = Graphics.FromImage(bitmap);
- Brush brush = new SolidBrush(Color.White);
- graphics.FillRectangle(brush, 0, 0, bitmapWidth, bitmapHeight);
- }
- public int boardSize { get { return marbleSize * 7 + padding * 8; } }
- public int bitmapWidth { get { return boardSize + padding * 2; } }
- public int bitmapHeight { get { return boardSize * numberMoves + padding * (numberMoves + 1); } }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement