//********************************************************************************************************
//
// This class belongs to the Solitare system
// It holds all the groundwork for analysing the board and finding the solution to the game
//
// ------------------------------------------------------
// Version : 1.0.0 September 2012 - original version
// Programmer : Morten Skaarup
// Language : c#, .Net Framework 4.0
// ------------------------------------------------------
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing ;
namespace Solitaire
{
public class SolitaireClass
{
#region --- Enumerators ---
public enum Cell
{
Void = -1,
Empty = 0,
Full = 1,
}
public enum Direction
{
Up = 0,
Down = 1,
Left = 2,
Right = 3
}
#endregion --- Enumerators ---
public static Cell[,] Board;
public static int movecount = 0; // Move counter - ensures unique ID
public static List<SolitaireMove> Moves = new List<SolitaireMove>(); // Complete list of moves
public static List<int> Path = new List<int>(); // The current sequence of moves
public static List<string> History = new List<string>(); // Text based history of moves and findings
public static List<int> PerformedMoves = new List<int>(); // List of actually performed moves (the ID)
public static Int16 Level = 0; // The current level. 31 means only one pin left
public static List<int> Best = new List<int>(); // The best sequence so far
public static Int16 BestLevel = 0; // The current best level
private static int _maxcount = 5000000; // The limitator of moves
#region --- Delegates ---
public delegate void ReportHistory(string str);
private static ReportHistory _history;
public static void OnCurrent(ReportHistory cur)
{
_history = cur;
}
public delegate void ReportCount(int count);
private static ReportCount _count;
public static void OnCount(ReportCount cnt)
{
_count = cnt;
}
#endregion --- Delelgates ---
/// <summary>
/// Initialize the board to the default
/// </summary>
public static void Init()
{
Board = new Cell[7, 7];
for (int i = 0; i <= 6; i++)
{
for (int j = 0; j <= 6; j++)
{
Board[i, j] = Cell.Full;
}
}
Board[0, 0] = Cell.Void;
Board[0, 1] = Cell.Void;
Board[0, 5] = Cell.Void;
Board[0, 6] = Cell.Void;
Board[1, 0] = Cell.Void;
Board[1, 1] = Cell.Void;
Board[1, 5] = Cell.Void;
Board[1, 6] = Cell.Void;
Board[5, 0] = Cell.Void;
Board[5, 1] = Cell.Void;
Board[5, 5] = Cell.Void;
Board[5, 6] = Cell.Void;
Board[6, 0] = Cell.Void;
Board[6, 1] = Cell.Void;
Board[6, 5] = Cell.Void;
Board[6, 6] = Cell.Void;
Board[3, 3] = Cell.Empty;
}
/// <summary>
/// Reset all the variables before the analysis
/// </summary>
public static void Reset()
{
movecount = 0;
Moves = new List<SolitaireMove>();
Path = new List<int>();
History = new List<string>();
PerformedMoves = new List<int>();
Level = 0;
Best = new List<int>();
BestLevel = 0;
}
/// <summary>
/// Find the possible moves on the board in the current state
/// </summary>
/// <param name="parent">The link to the parent move</param>
/// <returns>The list of possible moves - could be empty</returns>
private static List<SolitaireMove> Analyze(int parent)
{
List<SolitaireMove> temp = new List<SolitaireMove>();
for (int i = 0; i <= 6; i++)
{
for (int j = 0; j <= 6; j++)
{
if (Board[i, j] == Cell.Full)
{
//up
if (i >= 2)
{
if ((Board[i - 1, j] == Cell.Full) && (Board[i - 2, j] == Cell.Empty))
{
temp.Add(new SolitaireMove(movecount, parent, new Point(i, j), new Point(i - 2, j), Direction.Up));
movecount++;
}
}
// down
if (i <= 4)
{
if ((Board[i + 1, j] == Cell.Full) && (Board[i + 2, j] == Cell.Empty))
{
temp.Add(new SolitaireMove(movecount, parent, new Point(i, j), new Point(i + 2, j), Direction.Down));
movecount++;
}
}
//right
if (j <= 4)
{
if ((Board[i, j + 1] == Cell.Full) && (Board[i, j + 2] == Cell.Empty))
{
temp.Add(new SolitaireMove(movecount, parent, new Point(i, j), new Point(i, j + 2), Direction.Right));
movecount++;
}
}
//left
if (j >= 2)
{
if ((Board[i, j - 1] == Cell.Full) && (Board[i, j - 2] == Cell.Empty))
{
temp.Add(new SolitaireMove(movecount, parent, new Point(i, j), new Point(i, j - 2), Direction.Left));
movecount++;
}
}
}
}
}
if (_history !=null)
_history("To Parent " + parent.ToString() + " - added " + temp.Count.ToString() + " nodes");
string s = " ";
foreach (SolitaireMove sm in temp)
{
s += sm.Id.ToString() + ": (" + sm.From.X.ToString() + "," + sm.From.Y.ToString() + ") -> (" + sm.To.X.ToString() + "," + sm.To.Y.ToString() + "), ";
}
if (_history !=null)
_history(s.Substring(0, s.Length - 2));
if (_count != null)
_count(movecount);
return temp;
}
/// <summary>
/// Adjust the board, performing a given move
/// </summary>
/// <param name="sm">The move to perform</param>
public static void DoMove(SolitaireMove sm)
{
Board[sm.From.X, sm.From.Y] = Cell.Empty;
Board[sm.To.X, sm.To.Y] = Cell.Full;
switch (sm.MoveDirection)
{
case Direction.Up: { Board[sm.From.X - 1, sm.From.Y] = Cell.Empty; break; }
case Direction.Down: { Board[sm.From.X + 1, sm.From.Y] = Cell.Empty; break; }
case Direction.Left: { Board[sm.From.X, sm.From.Y - 1] = Cell.Empty; break; }
case Direction.Right: { Board[sm.From.X, sm.From.Y + 1] = Cell.Empty; break; }
default: break;
}
if (_history !=null)
_history("For Parent: " + sm.Parent.ToString() + " - Did move " + sm.ToString());
}
/// <summary>
/// Adjust the board, performing a reverse move
/// </summary>
/// <param name="sm">The move to reverse</param>
private static void UnDoMove(SolitaireMove sm)
{
Board[sm.From.X, sm.From.Y] = Cell.Full;
Board[sm.To.X, sm.To.Y] = Cell.Empty;
switch (sm.MoveDirection)
{
case Direction.Up: { Board[sm.From.X - 1, sm.From.Y] = Cell.Full; break; }
case Direction.Down: { Board[sm.From.X + 1, sm.From.Y] = Cell.Full; break; }
case Direction.Left: { Board[sm.From.X, sm.From.Y - 1] = Cell.Full; break; }
case Direction.Right: { Board[sm.From.X, sm.From.Y + 1] = Cell.Full; break; }
default: break;
}
}
/// <summary>
/// Start the analysis of the board
/// </summary>
/// <param name="max">The maximum number of moves</param>
public static void Run(int max)
{
_maxcount = max;
SolitaireMove root = new SolitaireMove();
root.Moves = Analyze(-1);
Moves.AddRange(root.Moves);
GoDeeper(root);
}
/// <summary>
/// Recursively iterate through the possible moves
/// </summary>
/// <param name="mn">The move to go deeper with</param>
private static void GoDeeper(SolitaireMove mn)
{
if (_history !=null)
_history("Digging into: "+mn.Id.ToString());
if ((mn.Moves.Count > 0) && (movecount <= _maxcount))
{
foreach (SolitaireMove node in mn.Moves)
{
if (_history != null)
_history("Examining " + node.Id.ToString());
if (node.Used == false)
{
Path.Add(node.Id);
PerformedMoves.Add(node.Id);
DoMove(node);
Level++;
if (Level > BestLevel)
{
Best.Clear();
foreach (int m in Path)
{
Best.Add(m);
}
BestLevel = Level;
}
node.Used = true;
node.Moves = Analyze(node.Id);
if (node.Moves.Count > 0)
{
Moves.AddRange(node.Moves);
GoDeeper(node);
}
else
{
if (Level <= 31)
{
UnDoMove(node);
Level--;
if (_history != null)
_history(node.Reverse() + " - Level now=" + Level.ToString());
Path.RemoveAt(Path.Count - 1);
PerformedMoves.Add(-node.Id);
}
else
break;
}
}
else
if (_history != null)
_history(node.Id.ToString() + " already used");
if (movecount >= _maxcount)
break;
}
if ((Level <= 31) && (Level > 0) && (movecount < _maxcount))
{
UnDoMove(mn);
Level--;
if (_history != null)
_history(mn.Reverse() + " - Level now=" + Level.ToString() + " *");
Path.RemoveAt(Path.Count - 1);
PerformedMoves.Add(-mn.Id);
}
}
}
}
}