Advertisement
Guest User

Untitled

a guest
Dec 9th, 2022
798
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 18.70 KB | None | 0 0
  1. using System.Drawing;
  2. using System.Runtime.CompilerServices;
  3. using System.Text;
  4. using static System.Diagnostics.Debug;
  5.  
  6. [assembly: InternalsVisibleTo("solitaireTests")]
  7.  
  8. //Solitaire2.Run(StartingBoards.DebugSmall);
  9. //Solitaire2.Run(StartingBoards.DebugBig);
  10. Solitaire2.Run(StartingBoards.Real);
  11.  
  12. static class StartingBoards
  13. {
  14.     public static string Real = @"
  15.  XXX  
  16.  XXX  
  17. XXXXXXX
  18. XXXOXXX
  19. XXXXXXX
  20.  XXX  
  21.  XXX  ";
  22.  
  23.     public static string DebugBig = @"
  24.  XXX  
  25.  XOX  
  26. XOOXXXX
  27. OOXXXOX
  28. XXXXXXX
  29.  XXX  
  30.  XXX  ";
  31.  
  32.     public static string DebugSmall = @"
  33.  OOO  
  34.  OXO  
  35. OOXOOOO
  36. OXOOOOO
  37. OXOOOOO
  38.  OOO  
  39.  OOO  ";
  40. }
  41.  
  42. static class WriteHelper
  43. {
  44.     public static void Write(string msg)
  45.     {
  46.         string[] ss = msg.Split('{', '}');
  47.         ConsoleColor c;
  48.         foreach (var s in ss)
  49.             if (s.StartsWith("/"))
  50.                 Console.ResetColor();
  51.             else if (s.StartsWith("=") && Enum.TryParse(s.Substring(1), out c))
  52.                 Console.ForegroundColor = c;
  53.             else
  54.                 Console.Write(s);
  55.     }
  56. }
  57.  
  58. public class Board
  59. {
  60.     private static readonly HashSet<long> seenBoards = new();
  61.     private readonly byte[,] p_data = new byte[7, 7];
  62.  
  63.     public void Record()
  64.     {
  65.         seenBoards.Add(Signature());
  66.     }
  67.  
  68.     public bool IsKnownBoard()
  69.     {
  70.         return seenBoards.Contains(Signature());
  71.     }
  72.  
  73.     private long Signature()
  74.     {
  75.         long sig = 1;
  76.         for (int row = 0; row < 7; row++)
  77.         {
  78.             for (int col = 0; col < 7; col++)
  79.             {
  80.                 if (p_data[row, col] == 2) sig = (sig << 1) + 0b1;
  81.                 else if (p_data[row, col] == 1) sig = (sig << 1) + 0b0;
  82.             }
  83.         }
  84.         //Console.WriteLine(Convert.ToString(sig, 2));
  85.         return sig;
  86.     }
  87.  
  88.     public Board(string startingBoard)
  89.     {
  90.         int charNumber = 0;
  91.         for (int i = 0; i < startingBoard.Length; i++)
  92.         {
  93.             if (startingBoard[i] == '\r' || startingBoard[i] == '\n') continue;
  94.             Assert(startingBoard[i] == ' ' || startingBoard[i] == 'X' || startingBoard[i] == 'O');
  95.             var row = charNumber / 7;
  96.             var col = charNumber % 7;
  97.             charNumber++;
  98.             if (startingBoard[i] == ' ')
  99.             {
  100.                 p_data[row, col] = 0;
  101.  
  102.             }
  103.             else if (startingBoard[i] == 'O')
  104.             {
  105.                 p_data[row, col] = 1;
  106.  
  107.             }
  108.             else if (startingBoard[i] == 'X')
  109.             {
  110.                 p_data[row, col] = 2;
  111.             }
  112.             else
  113.             {
  114.                 throw new Exception();
  115.             }
  116.         }
  117.         Assert(charNumber == 49);
  118.     }
  119.  
  120.     private Board(byte[] bytes)
  121.     {
  122.         for (int i = 0; i < bytes.Length; i++)
  123.         {
  124.             Assert(bytes[i] == 0 || bytes[i] == 1 || bytes[i] == 2);
  125.             var row = i / 7;
  126.             var col = i % 7;
  127.             p_data[row, col] = bytes[i];
  128.         }
  129.         Assert(bytes.Length == 49);
  130.     }
  131.  
  132.     public IList<Move> FindLegalMoves()
  133.     {
  134.         IList<Move> moves = new List<Move>();
  135.  
  136.         for (int row = 0; row < 7; row++)
  137.         {
  138.             for (int col = 0; col < 7 - 2; col++)
  139.             {
  140.                 if (p_data[row, col] == 2 && p_data[row, col + 1] == 2 && p_data[row, col + 2] == 1)
  141.                 {
  142.                     moves.Add(new Move(Jumping: (row, col), Removed: (row, col + 1), Landing: (row, col + 2)));
  143.                 }
  144.                 else if (p_data[row, col] == 1 && p_data[row, col + 1] == 2 && p_data[row, col + 2] == 2)
  145.                 {
  146.                     moves.Add(new Move(Jumping: (row, col + 2), Removed: (row, col + 1), Landing: (row, col)));
  147.                 }
  148.             }
  149.         }
  150.  
  151.         for (int col = 0; col < 7; col++)
  152.         {
  153.             for (int row = 0; row < 7 - 2; row++)
  154.             {
  155.                 if (p_data[row, col] == 2 && p_data[row + 1, col] == 2 && p_data[row + 2, col] == 1)
  156.                 {
  157.                     moves.Add(new Move(Jumping: (row, col), Removed: (row + 1, col), Landing: (row + 2, col)));
  158.                 }
  159.                 else if (p_data[row, col] == 1 && p_data[row + 1, col] == 2 && p_data[row + 2, col] == 2)
  160.                 {
  161.                     moves.Add(new Move(Landing: (row, col), Removed: (row + 1, col), Jumping: (row + 2, col)));
  162.                 }
  163.             }
  164.         }
  165.  
  166.         return moves;
  167.     }
  168.  
  169.     public string Draw()
  170.     {
  171.         return Draw(null);
  172.     }
  173.  
  174.     public string Draw(Move? selectedMove)
  175.     {
  176.         Dictionary<byte, char> map = new()
  177.         {
  178.             { 0, ' ' },
  179.             { 1, 'O' },
  180.             { 2, 'X' },
  181.         };
  182.  
  183.         StringBuilder sb = new StringBuilder();
  184.         for (var row = 0; row < 7; row++)
  185.         {
  186.             sb.Append('\n');
  187.             for (var col = 0; col < 7; col++)
  188.             {
  189.                 if (selectedMove?.IsJumpingMarble((row, col)) == true)
  190.                 {
  191.                     sb.Append("{=Red}");
  192.                     sb.Append(map[p_data[row, col]]);
  193.                     sb.Append("{/}");
  194.                 }
  195.                 else if (selectedMove?.IsRemovedMarble((row, col)) == true)
  196.                 {
  197.                     sb.Append("{=Red}");
  198.                     sb.Append(map[p_data[row, col]]);
  199.                     sb.Append("{/}");
  200.                 }
  201.                 else if (selectedMove?.IsLandingSpot((row, col)) == true)
  202.                 {
  203.                     sb.Append("{=Green}");
  204.                     sb.Append(map[p_data[row, col]]);
  205.                     sb.Append("{/}");
  206.                 }
  207.                 else
  208.                 {
  209.                     sb.Append(map[p_data[row, col]]);
  210.                 }
  211.             }
  212.         }
  213.         return sb.ToString();
  214.     }
  215.  
  216.     public bool IsVictory()
  217.     {
  218.         for (var row = 0; row < 7; row++)
  219.         {
  220.             for (var col = 0; col < 7; col++)
  221.             {
  222.                 if (row == 3 && col == 3)
  223.                 {
  224.                     if (p_data[3, 3] != 2) return false;
  225.                 }
  226.                 else if (p_data[row, col] == 2) return false;
  227.             }
  228.         }
  229.         return true;
  230.     }
  231.  
  232.     public Board PlayMove(Move? move)
  233.     {
  234.         byte[] bytes = new byte[49];
  235.         int index = 0;
  236.         for (var row = 0; row < 7; row++)
  237.         {
  238.             for (var col = 0; col < 7; col++)
  239.             {
  240.                 if (move?.IsJumpingMarble((row, col)) == true)
  241.                 {
  242.                     bytes[index++] = 1;
  243.                 }
  244.                 else if (move?.IsRemovedMarble((row, col)) == true)
  245.                 {
  246.                     bytes[index++] = 1;
  247.                 }
  248.                 else if (move?.IsLandingSpot((row, col)) == true)
  249.                 {
  250.                     bytes[index++] = 2;
  251.                 }
  252.                 else
  253.                 {
  254.                     bytes[index++] = p_data[row, col];
  255.                 }
  256.             }
  257.         }
  258.         return new Board(bytes);
  259.     }
  260.  
  261.     internal static (int, int) RotateCoord((int, int) coordIn, int quarterTurns)
  262.     {
  263.         int row = coordIn.Item1;
  264.         int col = coordIn.Item2;
  265.  
  266.         if (quarterTurns == 0)
  267.         { // no rotation
  268.             return (row, col);
  269.         }
  270.         else if (quarterTurns == 1)
  271.         {
  272.             return (col, -row + 6);
  273.         }
  274.         else if (quarterTurns == 2)
  275.         {
  276.             return (6 - row, 6 - col);
  277.         }
  278.         else // if (quarterTurns == 3)
  279.         {
  280.             return (6 - col, row);
  281.         }
  282.     }
  283.  
  284.     public Board Rotate(int quarterTurns)
  285.     {
  286.         quarterTurns %= 4;
  287.         byte[] bytes = new byte[49];
  288.         for (var row = 0; row < 7; row++)
  289.         {
  290.             for (var col = 0; col < 7; col++)
  291.             {
  292.                 (int rotatedRowIndex, int rotatedColIndex) = RotateCoord((row, col), quarterTurns);
  293.  
  294.                 bytes[rotatedRowIndex * 7 + rotatedColIndex] = p_data[row, col];
  295.             }
  296.         }
  297.         return new Board(bytes);
  298.     }
  299.  
  300.     public bool IsSimilar(Board otherBoard)
  301.     {
  302.         long sig = Signature();
  303.         long otherSig = otherBoard.Signature();
  304.         return sig == otherSig;
  305.     }
  306.  
  307.     internal void DrawGraphic(Move? move, DrawParameters drawParams)
  308.     {
  309.         Brush boardBrush = new SolidBrush(drawParams.boardColor);
  310.         Brush marbleBrush = new SolidBrush(drawParams.marbleColor);
  311.         Brush highlightedMarbleBrush = new SolidBrush(Color.Green);
  312.         Brush emptySpotBrush = new SolidBrush(Color.Black);
  313.         Pen pen = new Pen(highlightedMarbleBrush, 2.5f);
  314.         drawParams.boardRectangle.X = drawParams.padding;
  315.         drawParams.boardRectangle.Y = drawParams.updateableBoardPosition;
  316.  
  317.         drawParams.graphics.FillEllipse(boardBrush, drawParams.boardRectangle);
  318.  
  319.         for (var row = 0; row < 7; row++)
  320.         {
  321.             drawParams.marbleRectangle.Y = drawParams.boardRectangle.Y + drawParams.padding + row * (drawParams.marbleSize + drawParams.padding);
  322.             drawParams.highlightedMarbleRectangle.Y = drawParams.marbleRectangle.Y - (drawParams.highlightedMarbleRectangle.Width - drawParams.marbleRectangle.Width) / 2;
  323.             for (var col = 0; col < 7; col++)
  324.             {
  325.                 drawParams.marbleRectangle.X = drawParams.boardRectangle.X + drawParams.padding + col * (drawParams.marbleSize + drawParams.padding);
  326.                 drawParams.highlightedMarbleRectangle.X = drawParams.marbleRectangle.X - (drawParams.highlightedMarbleRectangle.Width - drawParams.marbleRectangle.Width) / 2;
  327.  
  328.                 if (p_data[row, col] == 2)
  329.                 {
  330.                     drawParams.graphics.FillEllipse(marbleBrush, drawParams.marbleRectangle);
  331.                 }
  332.                 else if (p_data[row, col] == 1)
  333.                 {
  334.                     drawParams.graphics.FillEllipse(emptySpotBrush, drawParams.marbleRectangle);
  335.                 }
  336.  
  337.                 if (move?.IsJumpingMarble((row, col)) == true || move?.IsLandingSpot((row, col)) == true || move?.IsRemovedMarble((row, col)) == true)
  338.                 {
  339.                     drawParams.graphics.DrawEllipse(pen, drawParams.highlightedMarbleRectangle);
  340.                 }
  341.             }
  342.         }
  343.  
  344.  
  345.  
  346.         //drawParams.graphics.DrawEllipse(pen, drawParams.marbleRectangle);
  347.  
  348.         drawParams.updateableBoardPosition += drawParams.boardSize + drawParams.padding;
  349.     }
  350. }
  351.  
  352. public class Move
  353. {
  354.     private readonly (int, int) Jumping;
  355.     private readonly (int, int) Removed;
  356.     private readonly (int, int) Landing;
  357.     public Move((int, int) Jumping, (int, int) Removed, (int, int) Landing)
  358.     {
  359.         this.Jumping = Jumping;
  360.         this.Removed = Removed;
  361.         this.Landing = Landing;
  362.     }
  363.  
  364.     public bool IsJumpingMarble((int, int) coordinate)
  365.     {
  366.         return Jumping.Item1 == coordinate.Item1 && Jumping.Item2 == coordinate.Item2;
  367.     }
  368.  
  369.     public bool IsRemovedMarble((int, int) coordinate)
  370.     {
  371.         return Removed.Item1 == coordinate.Item1 && Removed.Item2 == coordinate.Item2;
  372.     }
  373.  
  374.     public bool IsLandingSpot((int, int) coordinate)
  375.     {
  376.         return Landing.Item1 == coordinate.Item1 && Landing.Item2 == coordinate.Item2;
  377.     }
  378.  
  379.     public override bool Equals(object? obj)
  380.     {
  381.         return obj is Move move &&
  382.                Jumping.Equals(move.Jumping) &&
  383.                Removed.Equals(move.Removed) &&
  384.                Landing.Equals(move.Landing);
  385.     }
  386. }
  387.  
  388. class Solitaire2
  389. {
  390.     public static void Run(string startingBoard)
  391.     {
  392.         //RunInteractively(startingBoard);
  393.         RunAutomatically(startingBoard);
  394.     }
  395.  
  396.     public static void RunDebug(string startingBoard)
  397.     {
  398.         Board board = new Board(startingBoard);
  399.         IList<Move> moves = board.FindLegalMoves();
  400.         var boardString = board.Draw();
  401.         WriteHelper.Write(boardString);
  402.         boardString = board.Draw(moves[0]);
  403.         WriteHelper.Write(boardString);
  404.         //board.IsVictory();
  405.         //Board updatedBoard = board.PlayMove(moves[0]);
  406.     }
  407.  
  408.     class Node
  409.     {
  410.         public readonly Board board;
  411.         private readonly Stack<Move> moves;
  412.         public Node(Board board, IList<Move> moves)
  413.         {
  414.             this.board = board;
  415.             this.moves = new Stack<Move>(moves);
  416.         }
  417.  
  418.         public bool HasMoves()
  419.         {
  420.             return moves.Count > 0;
  421.         }
  422.  
  423.         public Move PopMove()
  424.         {
  425.             return moves.Pop();
  426.         }
  427.     }
  428.  
  429.     public static void RunAutomatically(string startingBoard)
  430.     {
  431.         Stack<Node> stack = new();
  432.         Stack<Move?> recordedMoves = new();
  433.  
  434.         {
  435.             Board firstBoard = new Board(startingBoard);
  436.             IList<Move> firstMoves = firstBoard.FindLegalMoves();
  437.             stack.Push(new Node(firstBoard, firstMoves));
  438.             recordedMoves.Push(null);
  439.         }
  440.  
  441.         int counter = 0;
  442.         while (stack.Count > 0)
  443.         {
  444.             Node currentNode = stack.Peek();
  445.             counter++;
  446.             if (counter % 100000 == 0)
  447.             {
  448.                 // WriteHelper.Write(counter.ToString());
  449.                 // WriteHelper.Write("\n");
  450.             }
  451.             if (currentNode.HasMoves())
  452.             {
  453.                 Move nextMove = currentNode.PopMove();
  454.                 Board newBoard = currentNode.board.PlayMove(nextMove);
  455.                 if (!newBoard.IsKnownBoard()
  456.                     && !newBoard.Rotate(1).IsKnownBoard()
  457.                     && !newBoard.Rotate(2).IsKnownBoard()
  458.                     && !newBoard.Rotate(3).IsKnownBoard()
  459.                     )
  460.                 {
  461.                     recordedMoves.Push(nextMove);
  462.                     newBoard.Record();
  463.                     IList<Move> newMoves = newBoard.FindLegalMoves();
  464.                     stack.Push(new Node(newBoard, newMoves));
  465.                 }
  466.             }
  467.             else
  468.             {
  469.                 if (currentNode.board.IsVictory())
  470.                 {
  471.                     DrawParameters drawParams = new(marbleSize: 15, padding: 10, numberMoves: recordedMoves.Count)
  472.                     {
  473.                         marbleColor = Color.Brown,
  474.                         boardColor = Color.DarkGray,
  475.                     };
  476.  
  477.                     Board board = new(startingBoard);
  478.  
  479.                     foreach (var move in recordedMoves.Reverse().Skip(1))
  480.                     {
  481.                         board.DrawGraphic(move, drawParams);
  482.                         board = board.PlayMove(move);
  483.                     }
  484.                     board.DrawGraphic(null, drawParams);
  485.  
  486.                     using (FileStream fs = new FileStream("C:\\Users\\frenchyp\\Downloads\\solitaire.png", FileMode.Create, FileAccess.Write))
  487.                     {
  488.                         drawParams.bitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
  489.                     }
  490.                     return;
  491.                 }
  492.                 recordedMoves.Pop();
  493.                 stack.Pop();
  494.             }
  495.         }
  496.     }
  497.  
  498.     public static void RunInteractively(string startingBoard)
  499.     {
  500.         Board board = new Board(startingBoard);
  501.         bool run = true;
  502.         Move? selectedMove = null;
  503.         int selectedMoveIndex = 0;
  504.         IList<Move> moves = new List<Move>();
  505.  
  506.         while (run)
  507.         {
  508.             WriteHelper.Write(board.Draw(selectedMove));
  509.             WriteHelper.Write("\n");
  510.  
  511.             var command = Console.ReadKey(true);
  512.             switch (command.Key)
  513.             {
  514.                 case ConsoleKey.Q:
  515.                     run = false;
  516.                     break;
  517.                 case ConsoleKey.Enter:
  518.                     if (selectedMove != null)
  519.                     {
  520.                         board = board.PlayMove(selectedMove);
  521.                     }
  522.                     moves = board.FindLegalMoves();
  523.                     selectedMove = null;
  524.                     selectedMoveIndex = 0;
  525.                     if (moves.Count > 0)
  526.                     {
  527.                         selectedMove = moves[selectedMoveIndex];
  528.                     }
  529.                     else
  530.                     {
  531.                         if (board.IsVictory()) WriteHelper.Write("VICTORY!");
  532.                         else WriteHelper.Write("lost..");
  533.                         return;
  534.                     }
  535.  
  536.                     break;
  537.                 case ConsoleKey.LeftArrow:
  538.                 case ConsoleKey.UpArrow:
  539.                     selectedMoveIndex++;
  540.                     if (selectedMoveIndex >= moves.Count) selectedMoveIndex = 0;
  541.                     selectedMove = null;
  542.                     if (selectedMoveIndex < moves.Count) selectedMove = moves[selectedMoveIndex];
  543.                     break;
  544.                 case ConsoleKey.RightArrow:
  545.                 case ConsoleKey.DownArrow:
  546.                     selectedMoveIndex--;
  547.                     if (selectedMoveIndex < 0) selectedMoveIndex = moves.Count - 1;
  548.                     if (selectedMoveIndex < 0) selectedMoveIndex = 0;
  549.                     selectedMove = null;
  550.                     if (selectedMoveIndex < moves.Count) selectedMove = moves[selectedMoveIndex];
  551.                     break;
  552.                 default:
  553.                     break;
  554.             }
  555.         }
  556.     }
  557. }
  558.  
  559. internal class DrawParameters
  560. {
  561.     internal int marbleSize;
  562.     internal int updateableBoardPosition;
  563.  
  564.     public Rectangle marbleRectangle;
  565.     public Rectangle highlightedMarbleRectangle;
  566.     public Rectangle boardRectangle;
  567.     public Bitmap bitmap { get; }
  568.     public Graphics graphics { get; }
  569.  
  570.     internal Color marbleColor;
  571.     internal Color boardColor;
  572.     internal int padding;
  573.     private readonly int numberMoves;
  574.  
  575.     public DrawParameters(int marbleSize, int padding, int numberMoves)
  576.     {
  577.         this.marbleSize = marbleSize;
  578.         this.padding = padding;
  579.         this.updateableBoardPosition = padding;
  580.         this.marbleRectangle = new Rectangle(0, 0, marbleSize, marbleSize);
  581.         this.highlightedMarbleRectangle = new Rectangle(0, 0, marbleSize + padding / 3, marbleSize + padding / 3);
  582.         this.boardRectangle = new Rectangle(0, 0, boardSize, boardSize);
  583.         this.numberMoves = numberMoves;
  584.  
  585.         bitmap = new Bitmap(bitmapWidth, bitmapHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
  586.         graphics = Graphics.FromImage(bitmap);
  587.         Brush brush = new SolidBrush(Color.White);
  588.         graphics.FillRectangle(brush, 0, 0, bitmapWidth, bitmapHeight);
  589.     }
  590.  
  591.     public int boardSize { get { return marbleSize * 7 + padding * 8; } }
  592.     public int bitmapWidth { get { return boardSize + padding * 2; } }
  593.     public int bitmapHeight { get { return boardSize * numberMoves + padding * (numberMoves + 1); } }
  594. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement