// Etat : tuple de 9 entiers ; 0 = case vide.
// On utilise ValueTuple<int,int,int,int,int,int,int,int,int> pour 9 champs nommes.
public class EightPuzzle : SearchProblem {
// Etat but classique (ligne par ligne)
public static readonly (int a, int b, int c, int d, int e, int f, int g, int h, int i) GOAL =
(1, 2, 3, 4, 5, 6, 7, 8, 0);
// Mouvements possibles de la case vide selon sa position (index 0-8)
private static readonly Dictionary<int, Dictionary<string, int>> MOVES = new() {
[0] = new() { ["Bas"] = 3, ["Droite"] = 1 },
[1] = new() { ["Bas"] = 4, ["Gauche"] = 0, ["Droite"] = 2 },
[2] = new() { ["Bas"] = 5, ["Gauche"] = 1 },
[3] = new() { ["Haut"] = 0, ["Bas"] = 6, ["Droite"] = 4 },
[4] = new() { ["Haut"] = 1, ["Bas"] = 7, ["Gauche"] = 3, ["Droite"] = 5 },
[5] = new() { ["Haut"] = 2, ["Bas"] = 8, ["Gauche"] = 4 },
[6] = new() { ["Haut"] = 3, ["Droite"] = 7 },
[7] = new() { ["Haut"] = 4, ["Gauche"] = 6, ["Droite"] = 8 },
[8] = new() { ["Haut"] = 5, ["Gauche"] = 7 },
};
private readonly (int a, int b, int c, int d, int e, int f, int g, int h, int i) start;
public EightPuzzle((int a, int b, int c, int d, int e, int f, int g, int h, int i) start) {
this.start = start;
}
public override object InitialState() => start;
// Trouve la position de la case vide (0).
private static int BlankPos((int a, int b, int c, int d, int e, int f, int g, int h, int i) state) {
if (state.a == 0) return 0; if (state.b == 0) return 1; if (state.c == 0) return 2;
if (state.d == 0) return 3; if (state.e == 0) return 4; if (state.f == 0) return 5;
if (state.g == 0) return 6; if (state.h == 0) return 7; return 8;
}
public override IEnumerable<object> Actions(object state) {
var st = ((int, int, int, int, int, int, int, int, int))state;
var bp = BlankPos(st);
return MOVES[bp].Keys.Cast<object>();
}
public override object Transition(object state, object action) {
var st = ((int, int, int, int, int, int, int, int, int))state;
var arr = new[] { st.Item1, st.Item2, st.Item3, st.Item4,
st.Item5, st.Item6, st.Item7, st.Item8, st.Item9 };
var bp = Array.IndexOf(arr, 0);
string a = (string)action;
var targetPos = MOVES[bp][a];
(arr[bp], arr[targetPos]) = (arr[targetPos], arr[bp]);
return (arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7], arr[8]);
}
public override bool IsGoal(object state) => state.Equals(GOAL);
// |S| = 9!/2 = 181 440 etats atteignables (la moitie des permutations)
public override int? StateSpaceSize() {
long f = 1; for (int i = 2; i <= 9; i++) f *= i;
return (int)(f / 2);
}
}
// Creation
var puzzle = new EightPuzzle((7, 2, 4, 5, 0, 6, 8, 3, 1));
var s0p = puzzle.InitialState();
Console.WriteLine($"Taille de l'espace d'etats : {puzzle.StateSpaceSize():N0} etats");
Console.WriteLine($"Etat initial : {s0p}");
Console.WriteLine($"Etat but : {EightPuzzle.GOAL}");
Console.WriteLine($"Actions depuis l'etat initial : [{string.Join(", ", puzzle.Actions(s0p).Select(a => (string)a))}]");