/// <summary> /// sucht die oberste und am weitestende linke Position, welche vom Spieler noch erreichbar ist /// </summary> /// <param name="field">Spielfeld, welches gescannt werden soll</param> /// <returns>erreichbare Spielerposition</returns> public static int ScanTopLeftPos(SokowahnField field) { var data = field.fieldData; int bestPos = int.MaxValue; int width = field.width; var scanned = new bool[data.Length]; var next = new Stack<int>(); next.Push(field.PlayerPos); while (next.Count > 0) { int checkPos = next.Pop(); if (checkPos < bestPos) bestPos = checkPos; scanned[checkPos] = true; checkPos--; if (!scanned[checkPos] && (data[checkPos] == ' ' || data[checkPos] == '.')) next.Push(checkPos); checkPos += 2; if (!scanned[checkPos] && (data[checkPos] == ' ' || data[checkPos] == '.')) next.Push(checkPos); checkPos -= width + 1; if (!scanned[checkPos] && (data[checkPos] == ' ' || data[checkPos] == '.')) next.Push(checkPos); checkPos += width * 2; if (!scanned[checkPos] && (data[checkPos] == ' ' || data[checkPos] == '.')) next.Push(checkPos); } return bestPos; }
/// <summary> /// Konstruktor /// </summary> /// <param name="field">Spielfeld, welches betroffen ist</param> public DeadlockBlocker(SokowahnField field) { this.field = new SokowahnField(field); wayMap = SokoTools.CreateWayMap(field.fieldData, field.width, field.PlayerPos); blockerSingle = wayMap.SelectArray(b => !b); ScanBlockerSingle(); }
/// <summary> /// fügt den Code für das Spielfeld inkl. deren Basis-Werte hinzu /// </summary> /// <param name="csFile">Cs-Datei, wo der Code hinzugefügt werden soll</param> /// <param name="field">Spielfeld, welches dargestellt werden soll</param> public static void AddLevelBasics(CsFile csFile, SokowahnField field) { var fChars = new char[field.width * field.height]; #region # // --- static readonly char[] FieldData = ... --- csFile.Write("const int FieldWidth = " + field.width + ";"); csFile.Write("const int FieldHeight = " + field.height + ";"); csFile.Write("const int FieldCount = FieldWidth * FieldHeight;"); csFile.Write(); csFile.Write("static readonly char[] FieldData =", f => { var zeile = new StringBuilder(); for (int y = 0; y < field.height; y++) { zeile.Clear(); zeile.Append("/* " + (y * field.width).ToString("N0").PadLeft(((field.height - 1) * field.width).ToString("N0").Length) + " */ "); for (int x = 0; x < field.width; x++) { char c = field.fieldData[x + y * field.width]; fChars[x + y * field.width] = c; if (c != '#') c = ' '; zeile.Append('\'').Append(c).Append("',"); } if (y == field.height - 1) zeile.Remove(zeile.Length - 1, 1); f.Write(zeile.ToString()); } }); csFile.WriteV(";\r\n\r\n"); #endregion #region # // --- static readonly ushort[] TargetPosis --- var targetFields = fChars.Select((c, i) => new { c, i }).Where(f => f.c == '.' || f.c == '*').Select(f => (ushort)f.i).ToArray(); csFile.Write("static readonly ushort[] TargetPosis = { " + string.Join(", ", targetFields) + " };"); csFile.Write(); #endregion #region # // --- static readonly ushort[] BoxPosis --- csFile.Write("static readonly ushort[] BoxPosis = { " + string.Join(", ", targetFields.Select(x => fChars.Length - 1)) + " };"); csFile.Write(); #endregion #region # // --- TxtView --- csFile.Write("static string TxtViewP(int playerPos = -1) { return SokoTools.TxtView(FieldData, FieldWidth, TargetPosis, playerPos); }"); csFile.Write("static string TxtView { get { return TxtViewP(); } }"); csFile.Write(); #endregion }
/// <summary> /// einfaches Tool zum finden irgendeiner Lösung eines Spielfeldes /// </summary> /// <param name="field">Spielfeld, welches gescannt werden soll</param> static void MiniSolver(SokowahnField field) { var scanner = new SokowahnField(field); int stateLen = scanner.posis.Length; var todoBuf = new ushort[16777216 * stateLen]; int todoPos = 0; int todoLen = 0; foreach (var p in scanner.posis) todoBuf[todoLen++] = p; var nextBuf = new ushort[stateLen * (stateLen - 1) * 4]; var hashCrcs = new HashSet<ulong>(); while (todoPos < todoLen) { scanner.SetGameState(todoBuf, todoPos); todoPos += stateLen; if (todoLen - todoPos == 0 || (hashCrcs.Count & 0xfff) == 0) { Console.Clear(); Console.WriteLine(scanner); Console.WriteLine(); Console.WriteLine("Todo: " + ((todoLen - todoPos) / stateLen).ToString("N0") + " (" + (200.0 / todoBuf.Length * (todoLen - todoPos)).ToString("N1") + " %)"); Console.WriteLine("Hash: " + hashCrcs.Count.ToString("N0") + " (" + (100.0 / 48000000 * hashCrcs.Count).ToString("N1") + " %)"); } scanner.SetPlayerTopLeft(); var crc = scanner.GetGameStateCrc(); if (hashCrcs.Contains(crc)) continue; hashCrcs.Add(crc); int nextCount = scanner.ScanMoves(nextBuf); for (int i = 0; i < nextCount * stateLen; i++) todoBuf[todoLen++] = nextBuf[i]; if (todoPos * 2 > todoBuf.Length) { for (int i = todoPos; i < todoLen; i++) todoBuf[i - todoPos] = todoBuf[i]; todoLen -= todoPos; todoPos = 0; } } Console.ReadLine(); }
/// <summary> /// fügt Informationen über das Spielfeld als Kommentare im Quellcode hinzu /// </summary> /// <param name="csFile">Cs-Datei, wo der Code hinzugefügt werden soll</param> /// <param name="field">Spielfeld, welches dargestellt werden soll</param> /// <param name="headLine">Überschrift, welche über dem Level verwendet werden soll</param> public static void AddLevelComments(CsFile csFile, SokowahnField field, string headLine) { int commentWidth = Math.Max(field.width + 2, 35); commentWidth = (commentWidth + 1) / 2 * 2 + field.width % 2; string levelId = field.GetLevelId(); string emptyLine = " *" + new string(' ', commentWidth - 2) + "*"; csFile.Write("/" + new string('*', commentWidth)); csFile.Write(emptyLine); csFile.Write((" * " + new string(' ', (commentWidth - 14 - headLine.Length) / 2) + "--- " + headLine + " ---").PadRight(commentWidth, ' ') + "*"); csFile.Write(emptyLine); csFile.Write(" " + new string('*', commentWidth)); csFile.Write(emptyLine); csFile.Write((" * Level-Hash: " + levelId.Remove(0, levelId.LastIndexOf('_') + 1)).PadRight(commentWidth, ' ') + "*"); csFile.Write(emptyLine); csFile.Write((" * Size : " + field.width + " x " + field.height + " (" + (field.width * field.height).ToString("N0") + ")").PadRight(commentWidth, ' ') + "*"); csFile.Write((" * Boxes : " + field.boxesCount).PadRight(commentWidth, ' ') + "*"); csFile.Write(emptyLine); string centerChars = new string(' ', (commentWidth - field.width - 2) / 2); csFile.Write(field.ToString().Replace("\r", "").Split('\n').Select(x => " *" + centerChars + x + centerChars + "*")); csFile.Write(emptyLine); csFile.Write(" " + new string('*', commentWidth) + "/"); }
/// <summary> /// erstellt ein vollständiges Hashbuilder-Projekt und kompiliert dieses /// </summary> /// <param name="field">Spielfeld, welches als Grundlage dient</param> /// <param name="solutionPath">Pfad zum Ordner, wo die Projektmappe erstellt werden soll</param> public static void CreateProject(SokowahnField field, string solutionPath) { var scanner = new SokowahnField(field); Console.WriteLine(scanner.ToString()); Console.WriteLine(); string levelId = scanner.GetLevelId(); var solutionGuid = CsProject.NewGuid("S" + levelId); var projectGuid = CsProject.NewGuid("P" + levelId); string projectName = "Sokowahn_HashBuilder_" + levelId; var csFile = new CsFile(); csFile.Write(); csFile.Write(); GenLevelTools.AddLevelComments(csFile, scanner, "Hash Builder Alpha"); csFile.Write(); csFile.Write(); #region # // --- using *.* --- csFile.Write("#region # using *.*"); csFile.Write(); csFile.Write("using System;"); csFile.Write("using System.Linq;"); csFile.Write("using System.Collections.Generic;"); csFile.Write("using System.Diagnostics;"); csFile.Write(); csFile.Write("// ReSharper disable UnusedMember.Local"); csFile.Write(); csFile.Write("#endregion"); csFile.Write(); csFile.Write(); #endregion #region # // --- Program.cs --- csFile.Write("namespace " + projectName, ns => { ns.Write("static unsafe class Program", cl => { GenLevelTools.AddLevelBasics(cl, scanner); GenLevelTools.AddBoxFunctions(cl); #region # // --- static int ScanTopLeftPos(int startPos) --- cl.Write("static readonly int[] PlayerDirections = { -1, +1, -FieldWidth, +FieldWidth };"); cl.Write(); cl.Write("static readonly int[] ScanTmp = new int[FieldCount];"); cl.Write(); cl.Write("static int ScanTopLeftPosIntern(int* next, bool* scanned, char* fd)", sc => { sc.Write("int bestPos = int.MaxValue;"); sc.Write("int nextPos = 1;"); sc.Write("while (nextPos > 0)", wh => { wh.Write("int checkPos = next[--nextPos];"); wh.Write("if (checkPos < bestPos) bestPos = checkPos;"); wh.Write("scanned[checkPos] = true;"); wh.Write("if (!scanned[checkPos - 1] && fd[checkPos - 1] == ' ') next[nextPos++] = checkPos - 1;"); wh.Write("if (!scanned[checkPos + 1] && fd[checkPos + 1] == ' ') next[nextPos++] = checkPos + 1;"); wh.Write("if (!scanned[checkPos - FieldWidth] && fd[checkPos - FieldWidth] == ' ') next[nextPos++] = checkPos - FieldWidth;"); wh.Write("if (!scanned[checkPos + FieldWidth] && fd[checkPos + FieldWidth] == ' ') next[nextPos++] = checkPos + FieldWidth;"); }); sc.Write("return bestPos;"); }); cl.Write(); cl.Write("static int ScanTopLeftPos(int startPos)", sc => { sc.Write("fixed (int* next = ScanTmp) fixed (char* fd = FieldData)", f => { f.Write("bool* scanned = stackalloc bool[FieldCount];"); f.Write("*next = startPos;"); f.Write("return ScanTopLeftPosIntern(next, scanned, fd);"); }); }); cl.Write(); #endregion #region # // --- static int ScanReverseMoves(int startPlayerPos, ushort[] output) --- cl.Write("static int ScanReverseMoves(int startPlayerPos, ushort[] output, int boxesCount)", sc => { sc.Write("int outputLen = 0;"); sc.Write(); sc.Write("bool* scannedFields = stackalloc bool[FieldCount];"); sc.Write("ushort* scanTodo = stackalloc ushort[FieldCount];"); sc.Write(); sc.Write("int scanTodoPos = 0;"); sc.Write("int scanTodoLen = 0;"); sc.Write(); sc.Write("scanTodo[scanTodoLen++] = (ushort)startPlayerPos;"); sc.Write("scannedFields[startPlayerPos] = true;"); sc.Write(); sc.Write("while (scanTodoPos < scanTodoLen)", wh => { wh.Write("ushort scan = scanTodo[scanTodoPos++];"); wh.Write(); wh.Write("#region # // --- links (zurück nach rechts) ---"); wh.Write("switch (FieldData[scan - 1])", sw => { sw.Write("case '#': break;"); sw.Write("case ' ': if (!scannedFields[scan - 1]) { scannedFields[scan - 1] = true; scanTodo[scanTodoLen++] = (ushort)(scan - 1); } break;"); sw.Write("default:", bx => { bx.Write("if (FieldData[scan + 1] != ' ') break;"); bx.Write(); bx.Write("MoveBox((ushort)(scan - 1), scan);"); bx.Write(); bx.Write("for (int i = 0; i < boxesCount; i++) output[outputLen + i] = BoxPosis[i];"); bx.Write("output[outputLen + boxesCount] = (ushort)(scan + 1);"); bx.Write("outputLen += boxesCount + 1;"); bx.Write(); bx.Write("MoveBox(scan, (ushort)(scan - 1));"); }); sw.Write("break;"); }); wh.Write("#endregion"); wh.Write(); wh.Write("#region # // --- rechts (zurück nach links) ---"); wh.Write("switch (FieldData[scan + 1])", sw => { sw.Write("case '#': break;"); sw.Write("case ' ': if (!scannedFields[scan + 1]) { scannedFields[scan + 1] = true; scanTodo[scanTodoLen++] = (ushort)(scan + 1); } break;"); sw.Write("default:", bx => { bx.Write("if (FieldData[scan - 1] != ' ') break;"); bx.Write(); bx.Write("MoveBox((ushort)(scan + 1), scan);"); bx.Write(); bx.Write("for (int i = 0; i < boxesCount; i++) output[outputLen + i] = BoxPosis[i];"); bx.Write("output[outputLen + boxesCount] = (ushort)(scan - 1);"); bx.Write("outputLen += boxesCount + 1;"); bx.Write(); bx.Write("MoveBox(scan, (ushort)(scan + 1));"); }); sw.Write("break;"); }); wh.Write("#endregion"); wh.Write(); wh.Write("#region # // --- oben (zurück nach unten) ---"); wh.Write("switch (FieldData[scan - FieldWidth])", sw => { sw.Write("case '#': break;"); sw.Write("case ' ': if (!scannedFields[scan - FieldWidth]) { scannedFields[scan - FieldWidth] = true; scanTodo[scanTodoLen++] = (ushort)(scan - FieldWidth); } break;"); sw.Write("default:", bx => { bx.Write("if (FieldData[scan + FieldWidth] != ' ') break;"); bx.Write(); bx.Write("MoveBox((ushort)(scan - FieldWidth), scan);"); bx.Write(); bx.Write("for (int i = 0; i < boxesCount; i++) output[outputLen + i] = BoxPosis[i];"); bx.Write("output[outputLen + boxesCount] = (ushort)(scan + FieldWidth);"); bx.Write("outputLen += boxesCount + 1;"); bx.Write(); bx.Write("MoveBox(scan, (ushort)(scan - FieldWidth));"); }); sw.Write("break;"); }); wh.Write("#endregion"); wh.Write(); wh.Write("#region # // --- unten (zurück nach oben) ---"); wh.Write("switch (FieldData[scan + FieldWidth])", sw => { sw.Write("case '#': break;"); sw.Write("case ' ': if (!scannedFields[scan + FieldWidth]) { scannedFields[scan + FieldWidth] = true; scanTodo[scanTodoLen++] = (ushort)(scan + FieldWidth); } break;"); sw.Write("default:", bx => { bx.Write("if (FieldData[scan - FieldWidth] != ' ') break;"); bx.Write(); bx.Write("MoveBox((ushort)(scan + FieldWidth), scan);"); bx.Write(); bx.Write("for (int i = 0; i < boxesCount; i++) output[outputLen + i] = BoxPosis[i];"); bx.Write("output[outputLen + boxesCount] = (ushort)(scan - FieldWidth);"); bx.Write("outputLen += boxesCount + 1;"); bx.Write(); bx.Write("MoveBox(scan, (ushort)(scan + FieldWidth));"); }); sw.Write("break;"); }); wh.Write("#endregion"); wh.Write(); }); sc.Write(); sc.Write("return outputLen;"); }); cl.Write(); #endregion #region # // --- static void Main() --- cl.Write("static void Main()", main => { main.Write("for (int boxesCount = 1; boxesCount <= TargetPosis.Length; boxesCount++)", bx => { bx.Write("int stateLen = boxesCount + 1;"); bx.Write("var todoBuf = new ushort[16777216 / (stateLen + 1) * (stateLen + 1)];"); bx.Write("int todoLen = 0;"); bx.Write("var stopWatch = Stopwatch.StartNew();"); bx.Write(); #region # // --- Suche End-Varianten --- bx.Write("#region # // --- search all finish-positions -> put into \"todoBuf\" ---", sc => { sc.Write("var checkDuplicates = new HashSet<ulong>();"); sc.Write(); sc.Write("foreach (var boxesVariant in SokoTools.FieldBoxesVariants(TargetPosis.Length, boxesCount).Select(v => v.Select(f => TargetPosis[f]).ToArray()))", fe => { fe.Write("foreach (var box in boxesVariant) FieldData[box] = '$';"); fe.Write(); fe.Write("ulong boxCrc = SokoTools.CrcCompute(SokoTools.CrcStart, boxesVariant, 0, boxesVariant.Length);"); fe.Write(); fe.Write("foreach (var box in boxesVariant)", feb => { feb.Write("foreach (int playerDir in PlayerDirections)", febs => { febs.Write("int playerPos = box - playerDir;"); febs.Write("if (FieldData[playerPos] != ' ') continue;"); febs.Write("if (FieldData[playerPos - playerDir] != ' ') continue;"); febs.Write(); febs.Write("ulong crc = SokoTools.CrcCompute(boxCrc, playerPos);"); febs.Write("if (checkDuplicates.Contains(crc)) continue;"); febs.Write("checkDuplicates.Add(crc);"); febs.Write(); febs.Write("int topPlayerPos = ScanTopLeftPos(playerPos);"); febs.Write(); febs.Write("if (topPlayerPos != playerPos)", febst => { febst.Write("crc = SokoTools.CrcCompute(boxCrc, topPlayerPos);"); febst.Write("if (checkDuplicates.Contains(crc)) continue;"); febst.Write("checkDuplicates.Add(crc);"); }); febs.Write(); febs.Write("todoBuf[todoLen++] = 0;"); febs.Write("for(int i = 0; i < boxesVariant.Length; i++) todoBuf[todoLen + i] = boxesVariant[i];"); febs.Write("todoBuf[todoLen + boxesVariant.Length] = (ushort)topPlayerPos;"); febs.Write("todoLen += stateLen;"); }); }); fe.Write(); fe.Write("foreach (var box in boxesVariant) FieldData[box] = ' ';"); }); }); bx.Write("#endregion"); bx.Write(); #endregion #region # // --- Durchsuche Rückwärts alle Möglichkeiten --- bx.Write("#region # // --- search all possible positions (bruteforce-reverse) ---", sc => { sc.Write("var hash = new DictionaryFastCrc<ushort>();"); sc.Write("var nextBuf = new ushort[stateLen * boxesCount * 4];"); sc.Write(); sc.Write("int todoPos = 0;"); sc.Write("while (todoPos < todoLen)", wh => { wh.Write("ushort depth = todoBuf[todoPos++];"); wh.Write("ulong crc = SetBoxes(todoBuf, todoPos, boxesCount);"); wh.Write("int playerPos = ScanTopLeftPos(todoBuf[todoPos + boxesCount]);"); wh.Write("crc = SokoTools.CrcCompute(crc, playerPos);"); wh.Write(); wh.Write("if (hash.ContainsKey(crc))", skip => { skip.Write("todoPos += stateLen;"); skip.Write("continue;"); }); wh.Write(); wh.Write("hash.Add(crc, depth);"); wh.Write("if ((hash.Count & 0xffff) == 0) Console.WriteLine(\"[\" + boxesCount + \"] (\" + depth + \") \" + ((todoLen - todoPos) / (stateLen + 1)).ToString(\"N0\") + \" / \" + hash.Count.ToString(\"N0\"));"); wh.Write(); wh.Write("depth++;"); wh.Write("int nextLength = ScanReverseMoves(playerPos, nextBuf, boxesCount);"); wh.Write("for (int next = 0; next < nextLength; next += stateLen)", f => { f.Write("todoBuf[todoLen++] = depth;"); f.Write("for (int i = 0; i < stateLen; i++) todoBuf[todoLen++] = nextBuf[next + i];"); }); wh.Write(); wh.Write("todoPos += stateLen;"); wh.Write("if (todoBuf.Length - todoLen < nextLength * 2)", arr => { arr.Write("Array.Copy(todoBuf, todoPos, todoBuf, 0, todoLen - todoPos);"); arr.Write("todoLen -= todoPos;"); arr.Write("todoPos = 0;"); }); }); sc.Write("stopWatch.Stop();"); sc.Write("Console.WriteLine();"); sc.Write("Console.ForegroundColor = ConsoleColor.Yellow;"); sc.Write("Console.WriteLine(\"[\" + boxesCount + \"] ok. Hash: \" + hash.Count.ToString(\"N0\") + \" (\" + stopWatch.ElapsedMilliseconds.ToString(\"N0\") + \" ms)\");"); sc.Write("Console.ForegroundColor = ConsoleColor.Gray;"); sc.Write("Console.WriteLine();"); }); bx.Write("#endregion"); #endregion }); }); #endregion }); }); csFile.SaveToFile(solutionPath + "Program.cs"); #endregion var csSokoTools = GenStaticTools.GenSokoTools(projectName); csSokoTools.SaveToFile(solutionPath + "SokoTools.cs"); var csDictFast = GenStaticTools.GenDictionaryFastCrc(projectName); csDictFast.SaveToFile(solutionPath + "DictionaryFastCrc.cs"); var projectFile = CsProject.CreateCsProjectFile(projectGuid, projectName, new[] { "System" }, new[] { "Program.cs", "SokoTools.cs", "DictionaryFastCrc.cs" }); projectFile.SaveToFile(solutionPath + projectName + ".csproj"); var solutionFile = CsProject.CreateSolutionFile(solutionGuid, projectGuid, "Sokowahn", projectName + ".csproj"); solutionFile.SaveToFile(solutionPath + projectName + ".sln"); CsCompiler.Compile(solutionPath + projectName + ".sln"); }
/// <summary> /// Konstruktor /// </summary> /// <param name="sokowahnField">vorhandenes Sokowahn-Spielfeld, kopiert werden soll</param> /// <param name="gameState">optionaler Spielstatus, welcher stattdessen verwendet werden soll</param> public SokowahnField(SokowahnField sokowahnField, ushort[] gameState = null) { width = sokowahnField.width; height = sokowahnField.height; fieldData = sokowahnField.fieldData.ToArray(); boxesCount = sokowahnField.boxesCount; boxesRemain = sokowahnField.boxesRemain; posis = sokowahnField.posis.ToArray(); if (gameState != null) SetGameState(gameState); }
void Form1_Load(object sender, EventArgs e) { var playField = new SokowahnField(TestData.Level3); drawSystem.DrawField(playField); }
/// <summary> /// analysiert alle möglichen Kistenstellungen mit einfachen (langsamen) Methoden /// </summary> /// <param name="field">Feld, welches durchsucht werden soll</param> /// <param name="minBoxes">minimale Anzahl der Kisten, welche berechnet werden sollen</param> /// <param name="maxBoxes">maximale Anzahl der Kisten, welche berechnet werden sollen</param> static List<Dictionary<ulong, ushort>> MiniSolverHashBuilder(SokowahnField field, int minBoxes = 1, int maxBoxes = int.MaxValue) { var scanner = new SokowahnField(field); var targetFields = scanner.fieldData.Select((c, i) => new { c, i }).Where(f => f.c == '.' || f.c == '*').Select(f => (ushort)f.i).ToArray(); if (targetFields.Length < maxBoxes) maxBoxes = targetFields.Length; if (minBoxes < 1 || minBoxes > maxBoxes) throw new ArgumentException("minBoxes"); var hashResults = new List<Dictionary<ulong, ushort>>(); for (int boxesCount = minBoxes; boxesCount <= maxBoxes; boxesCount++) { // --- Variablen initialisieren --- var boxes = new ushort[boxesCount]; int stateLen = 1 + boxes.Length; var todoBuf = new ushort[16777216 * 2 / (stateLen + 1) * (stateLen + 1)]; int todoPos = 0, todoLen = 0; var stopWatch = Stopwatch.StartNew(); // --- Startaufgaben scannen und setzen --- { int emptyPlayerPos = scanner.fieldData.ToList().IndexOf(' '); int[] playerDirections = { -1, +1, -scanner.width, +scanner.width }; var checkDuplicates = new HashSet<ulong>(); foreach (var boxesVariant in SokoTools.FieldBoxesVariantsStatic(targetFields.Length, boxesCount).Select(v => v.SelectArray(f => targetFields[f]))) { scanner.SetPlayerPos(emptyPlayerPos); scanner.SetBoxes(boxesVariant); var fieldData = scanner.fieldData; foreach (ushort box in boxesVariant) { foreach (int playerDir in playerDirections) { int playerPos = box - playerDir; if (fieldData[playerPos] == '#' || fieldData[playerPos] == '$' || fieldData[playerPos] == '*') continue; int revPos = playerPos - playerDir; if (fieldData[revPos] == '#' || fieldData[revPos] == '$' || fieldData[revPos] == '*') continue; scanner.SetPlayerPos(playerPos); scanner.SetPlayerTopLeft(); ulong crc = scanner.GetGameStateCrc(); if (checkDuplicates.Contains(crc)) continue; checkDuplicates.Add(crc); todoBuf[todoLen++] = 0; todoLen += scanner.GetGameState(todoBuf, todoLen); } } } } Console.WriteLine(field.ToString()); Console.WriteLine(); // --- Aufgaben weiter rückwärts gerichtet abarbeiten --- { var hash = new Dictionary<ulong, ushort>(); var nextBuf = new ushort[stateLen * boxesCount * 4]; int limitTickCount = Environment.TickCount + 1000; while (todoPos < todoLen) { ushort depth = todoBuf[todoPos++]; scanner.SetGameState(todoBuf, todoPos); todoPos += stateLen; scanner.SetPlayerTopLeft(); ulong crc = scanner.GetGameStateCrc(); if (hash.ContainsKey(crc)) continue; hash.Add(crc, depth); if ((hash.Count & 0xff) == 0 && Environment.TickCount > limitTickCount) { limitTickCount = Environment.TickCount + 1000; Console.WriteLine("[" + boxesCount + "] (" + depth + ") " + ((todoLen - todoPos) / (stateLen + 1)).ToString("N0") + " / " + hash.Count.ToString("N0")); } depth++; int nextLength = scanner.ScanReverseMoves(nextBuf) * stateLen; for (int next = 0; next < nextLength; next += stateLen) { todoBuf[todoLen++] = depth; for (int i = 0; i < stateLen; i++) todoBuf[todoLen++] = nextBuf[next + i]; } if (todoBuf.Length - todoLen < nextLength * 2) { Array.Copy(todoBuf, todoPos, todoBuf, 0, todoLen - todoPos); todoLen -= todoPos; todoPos = 0; } } stopWatch.Stop(); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("[" + boxesCount + "] ok. Hash: " + hash.Count.ToString("N0") + " (" + stopWatch.ElapsedMilliseconds.ToString("N0") + " ms)"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(); hashResults.Add(hash); } } return hashResults; }
static void ScanBlocker(SokowahnField field) { Console.WriteLine(field.ToString()); Console.WriteLine(); var test = new DeadlockBlocker(field); int width = field.width; var allPossibleBoxPositions = test.blockerSingle.Select((b, i) => new { b, i }).Where(x => !x.b).SelectArray(x => x.i); int boxDistanceLimit = SokoTools.MaxBoxDistance(allPossibleBoxPositions, 0, allPossibleBoxPositions.Length, width); var areas = new int[field.fieldData.Length]; int boxCount = 2; var nextBuf = new ushort[boxCount * 4 * (boxCount + 1)]; for (int boxDistance = 1; boxDistance <= boxDistanceLimit; boxDistance++) { Func<int[], int, bool> validateMethod = (boxes, index) => !test.blockerSingle[boxes[index]] && SokoTools.MaxBoxDistance(boxes, 0, index + 1, width) <= boxDistance; foreach (var set in SokoTools.FieldBoxesVariantsExtended(field.fieldData.Length, boxCount, validateMethod)) { if (test.blockerSingle[set[set.Length - 1]]) continue; if (SokoTools.MaxBoxDistance(set, 0, set.Length, width) != boxDistance) continue; test.ScanAreasWithBoxes(set, areas); field.SetGameState(areas[2], set.SelectArray(x => (ushort)x)); if (field.boxesRemain == 0) continue; int nextCount = field.ScanMoves(nextBuf); if (nextCount == 0) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("--- DEAD ---\r\n"); Console.ForegroundColor = ConsoleColor.Gray; } Console.WriteLine(field.ToString()); if (nextCount == 0) { int stop = 0; } } } }
static void ScanTopLeftFields(SokowahnField field) { boxesHash = new Dictionary<int, HashSet<ulong>>(); for (int b = 1; b <= field.boxesCount; b++) { Console.Clear(); Console.WriteLine(); for (int known = 1; known < b; known++) { Console.WriteLine(" known Boxes: " + known + " - Hash: " + boxesHash[known].Count.ToString("N0") + " Nodes"); } Console.WriteLine(); Console.WriteLine(" --- Calc Boxes: " + b + " ---"); Console.WriteLine(); int time = Environment.TickCount; var hash = MiniSolverHashBuilder(field, b, b); time = Environment.TickCount - time; boxesHash.Add(b, new HashSet<ulong>(hash.First().Keys)); if (time > 10000) break; if (time > 250 && b == field.boxesCount - 1) break; } Console.Clear(); var view = new SokowahnField(field); int maxBoxes = field.boxesCount; int width = field.width; var ways = FilterWays((ushort)field.PlayerPos, width, new HashSet<ushort>(Enumerable.Range(0, field.fieldData.Length).Select(f => (ushort)f)), new HashSet<ushort>(field.fieldData.Select((c, i) => new { c, i = (ushort)i }).Where(x => x.c == '#').Select(x => x.i))); #region # // --- ungültige Einzel-Box Positionen suchen --- invalidBoxes = new HashSet<ushort>(); foreach (ushort box in ways) { var checkCrc = new List<ulong>(); if (ways.Contains((ushort)(box - 1))) { view.SetGameState(new[] { (ushort)(box - 1), box }); view.SetPlayerTopLeft(); checkCrc.Add(view.GetGameStateCrc()); } if (ways.Contains((ushort)(box + 1))) { view.SetGameState(new[] { (ushort)(box + 1), box }); view.SetPlayerTopLeft(); checkCrc.Add(view.GetGameStateCrc()); } if (ways.Contains((ushort)(box - width))) { view.SetGameState(new[] { (ushort)(box - width), box }); view.SetPlayerTopLeft(); checkCrc.Add(view.GetGameStateCrc()); } if (ways.Contains((ushort)(box + width))) { view.SetGameState(new[] { (ushort)(box + width), box }); view.SetPlayerTopLeft(); checkCrc.Add(view.GetGameStateCrc()); } if (!checkCrc.Any(crc => boxesHash[1].Contains(crc))) { invalidBoxes.Add(box); } } #endregion var todo = new Queue<TopLeftTodo>(); var map = new List<int> { ways.Count }; foreach (var f in ways.OrderBy(x => x)) { map.Add(0); // Index Platzhalter todo.Enqueue(new TopLeftTodo { mapIndex = map.Count - 1, state = new[] { f }, known = new ushort[0] }); } int tick = 0; int nextTick = 0; while (todo.Count > 0) { bool dbg = false; if (map.Count > nextTick) { int t = Environment.TickCount; if (t > tick + 100) { Console.Title = "remain: " + todo.Count.ToString("N0") + " / " + map.Count.ToString("N0") + " (" + (Process.GetCurrentProcess().WorkingSet64 / 1048576.0).ToString("N1") + " MB)"; tick = t; dbg = true; } nextTick += 1000; } var next = todo.Dequeue(); var result = TestScan(width, next, ways, view, dbg); if (result != null) { if (result.Length <= 1) { map[next.mapIndex] = -result[0]; } else { map[next.mapIndex] = map.Count; map.Add(result.Length - 1); var known = new HashSet<ushort>(next.known); for (int r = 1; r < result.Length; r++) { map.Add(result[r]); known.Add(result[r]); map.Add(0); // Index Platzhalter if (next.state.Length <= maxBoxes) { var newState = AppendBoxesNewArray(next.state, result[r]); newState[0] = result[r - 1]; todo.Enqueue(new TopLeftTodo { mapIndex = map.Count - 1, state = newState, lastBox = result[r], known = known.ToArray() }); } } } } else { map[next.mapIndex] = -1; // ungültige Position if (dbg) { nextTick -= 1000; tick = 0; } } } Console.WriteLine(); Console.WriteLine("Map-Size: " + map.Count.ToString("N0") + " (" + (map.Count / 262144.0).ToString("N1") + " MB)"); Console.WriteLine(); }
static bool CheckDeadlock(TopLeftTodo topLeftTodo, SokowahnField view, ushort playerTopLeft) { HashSet<ulong> bHash; int boxesCount = topLeftTodo.state.Length - 1; if (boxesHash.TryGetValue(boxesCount, out bHash)) { ulong crc = Crc64.Start.Crc64Update(playerTopLeft).Crc64Update(topLeftTodo.state, 1, boxesCount); if (!bHash.Contains(crc)) { return true; } } else { // --- schnelle Vorprüfung --- for (int b = 1; b < topLeftTodo.state.Length; b++) { if (invalidBoxes.Contains(topLeftTodo.state[b])) return true; } // --- bestmögliche Hashprüfung --- int boxesCountMin = boxesHash.Count; bHash = boxesHash[boxesCountMin]; var checkBoxes = topLeftTodo.state.Skip(1).Where(b => b != topLeftTodo.lastBox).ToArray(); var checkState = new ushort[1 + boxesCountMin]; checkState[0] = playerTopLeft; foreach (var variant in SokoTools.FieldBoxesVariantsStatic(checkBoxes.Length, boxesCountMin - 1)) { for (int v = 0; v < variant.Length; v++) checkState[v + 1] = checkBoxes[variant[v]]; AppendBoxes(checkState, topLeftTodo.lastBox); view.SetGameState(checkState); view.SetPlayerTopLeft(); ulong crc = view.GetGameStateCrc(); if (!bHash.Contains(crc)) { return true; } } } return false; }
/// <summary> /// scannt nach einzelnen Felder-Positionen, wo keine Kisten stehen dürfen /// </summary> void ScanBlockerSingle() { var targetFields = field.fieldData.Select((c, i) => new { c, i }).Where(f => wayMap[f.i] && (f.c == '.' || f.c == '*')).Select(f => (ushort)f.i).ToArray(); var boxFields = field.fieldData.Select((c, i) => new { c, i }).Where(f => wayMap[f.i] && (f.c == '$' || f.c == '*')).Select(f => (ushort)f.i).ToArray(); var scanner = new SokowahnField(field); var fieldData = scanner.fieldData; int emptyPlayerPos = scanner.fieldData.ToList().IndexOf(' '); int[] playerDirections = { -1, +1, -scanner.width, +scanner.width }; var todoStates = new Queue<ushort[]>(); const int StateLen = 2; #region # // --- Rückwärts-Suche vorbereiten --- foreach (ushort box in targetFields) { scanner.SetGameState(new[] { (ushort)emptyPlayerPos, box }); foreach (int playerDir in playerDirections) { int playerPos = box - playerDir; if (fieldData[playerPos] == '#' || fieldData[playerPos] == '$' || fieldData[playerPos] == '*') continue; int revPos = playerPos - playerDir; if (fieldData[revPos] == '#' || fieldData[revPos] == '$' || fieldData[revPos] == '*') continue; scanner.SetPlayerPos(playerPos); scanner.SetPlayerTopLeft(); todoStates.Enqueue(scanner.GetGameState()); } } #endregion #region # // --- Rückwärts-Suche durchführen --- var reverseHash = new HashSet<ulong>(); // alle Stellungen, welche mit einer Kiste rückwärts erreichbar sind var nextBuf = new ushort[StateLen * (1) * 4]; while (todoStates.Count > 0) { scanner.SetGameState(todoStates.Dequeue()); scanner.SetPlayerTopLeft(); ulong crc = scanner.GetGameStateCrc(); if (reverseHash.Contains(crc)) continue; reverseHash.Add(crc); int nextLength = scanner.ScanReverseMoves(nextBuf) * StateLen; for (int next = 0; next < nextLength; next += StateLen) { todoStates.Enqueue(new[] { nextBuf[next], nextBuf[next + 1] }); } } #endregion #region # // --- Vorwärts-Suche vorbereiten --- foreach (ushort box in boxFields) { todoStates.Enqueue(new[] { (ushort)field.PlayerPos, box }); } #endregion #region # // --- Vorwärts-Suche durchführen --- var forwardHash = new HashSet<ulong>(); // alle Stellungen, welche mit einer Kiste vorwärts erreichbar sind var forwardBoxPosis = new HashSet<ushort>(); // alle Positionen, wo eine Kiste stehen könnte while (todoStates.Count > 0) { var gameState = todoStates.Dequeue(); scanner.SetGameState(gameState); scanner.SetPlayerTopLeft(); ulong crc = scanner.GetGameStateCrc(); if (forwardHash.Contains(crc)) continue; forwardHash.Add(crc); if (!reverseHash.Contains(crc)) continue; forwardBoxPosis.Add(gameState[1]); int nextLength = scanner.ScanMoves(nextBuf) * StateLen; for (int next = 0; next < nextLength; next += StateLen) { todoStates.Enqueue(new[] { nextBuf[next], nextBuf[next + 1] }); } } #endregion #region # // --- geblockte Felder markieren, wo niemals eine Kiste stehen darf --- for (ushort i = 0; i < blockerSingle.Length; i++) { if (!blockerSingle[i] && !forwardBoxPosis.Contains(i)) { blockerSingle[i] = true; } } #endregion }
void UpdateScreen(SokowahnField field) { if (playField.width != drawField.width || playField.height != drawField.height) { throw new NotImplementedException("Resize Screen"); } var drawData = drawField.fieldData; var f = field.fieldData; int w = field.width; var minField = new PointInt(int.MaxValue, int.MaxValue); var maxField = new PointInt(int.MinValue, int.MinValue); int tickLimit = Environment.TickCount + 1000; for (int y = 0; y < field.height; y++) { if (Environment.TickCount > tickLimit) return; for (int x = 0; x < w; x++) { char c = f[x + y * w]; if (drawData[x + y * w] == c) continue; if (x < minField.x) minField.x = x; if (y < minField.y) minField.y = y; if (x > maxField.x) maxField.x = x; if (y > maxField.y) maxField.y = y; drawData[x + y * w] = c; switch (c) { case ' ': MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); break; case '.': MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); MaleTestbild(viewContext, skinContext, x, y, BildElement.FreiZiel); break; case '$': MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); MaleTestbild(viewContext, skinContext, x, y, BildElement.KisteOffen); break; case '*': MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); MaleTestbild(viewContext, skinContext, x, y, BildElement.KisteZiel); break; case '@': MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); MaleTestbild(viewContext, skinContext, x, y, BildElement.Spieler); break; case '+': MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); MaleTestbild(viewContext, skinContext, x, y, BildElement.FreiZiel); MaleTestbild(viewContext, skinContext, x, y, BildElement.Spieler); break; case '#': { MaleTestbild(viewContext, skinContext, x, y, BildElement.Frei); var lo = BildElement.WandVoll; var ro = BildElement.WandVoll; var lu = BildElement.WandVoll; var ru = BildElement.WandVoll; if (GetF(f, x - 1, y, w) && GetF(f, x, y - 1, w)) lo = BildElement.WandSpitzen; if (GetF(f, x - 1, y, w) && !GetF(f, x, y - 1, w)) lo = BildElement.WandSenkrecht; if (!GetF(f, x - 1, y, w) && GetF(f, x, y - 1, w)) lo = BildElement.WandWaagerecht; if (!GetF(f, x - 1, y, w) && !GetF(f, x, y - 1, w)) lo = GetF(f, x - 1, y - 1, w) ? BildElement.WandEcken : BildElement.WandVoll; if (GetF(f, x + 1, y, w) && GetF(f, x, y - 1, w)) ro = BildElement.WandSpitzen; if (GetF(f, x + 1, y, w) && !GetF(f, x, y - 1, w)) ro = BildElement.WandSenkrecht; if (!GetF(f, x + 1, y, w) && GetF(f, x, y - 1, w)) ro = BildElement.WandWaagerecht; if (!GetF(f, x + 1, y, w) && !GetF(f, x, y - 1, w)) ro = GetF(f, x + 1, y - 1, w) ? BildElement.WandEcken : BildElement.WandVoll; if (GetF(f, x - 1, y, w) && GetF(f, x, y + 1, w)) lu = BildElement.WandSpitzen; if (GetF(f, x - 1, y, w) && !GetF(f, x, y + 1, w)) lu = BildElement.WandSenkrecht; if (!GetF(f, x - 1, y, w) && GetF(f, x, y + 1, w)) lu = BildElement.WandWaagerecht; if (!GetF(f, x - 1, y, w) && !GetF(f, x, y + 1, w)) lu = GetF(f, x - 1, y + 1, w) ? BildElement.WandEcken : BildElement.WandVoll; if (GetF(f, x + 1, y, w) && GetF(f, x, y + 1, w)) ru = BildElement.WandSpitzen; if (GetF(f, x + 1, y, w) && !GetF(f, x, y + 1, w)) ru = BildElement.WandSenkrecht; if (!GetF(f, x + 1, y, w) && GetF(f, x, y + 1, w)) ru = BildElement.WandWaagerecht; if (!GetF(f, x + 1, y, w) && !GetF(f, x, y + 1, w)) ru = GetF(f, x + 1, y + 1, w) ? BildElement.WandEcken : BildElement.WandVoll; MaleTestbild(viewContext, skinContext, x, y, lo, BildTeile.LinksOben); MaleTestbild(viewContext, skinContext, x, y, ro, BildTeile.RechtsOben); MaleTestbild(viewContext, skinContext, x, y, lu, BildTeile.LinksUnten); MaleTestbild(viewContext, skinContext, x, y, ru, BildTeile.RechtsUnten); } break; default: throw new Exception("unknown Char: '" + c + "'"); } } } if (minField.x != int.MaxValue) { maxField.x = maxField.x - minField.x + 1; maxField.y = maxField.y - minField.y + 1; viewContext.Present(minField.x * BoxPixelWidth, minField.y * BoxPixelHeight, maxField.x * BoxPixelWidth, maxField.y * BoxPixelHeight); } }
/// <summary> /// analysiert alle möglichen Kistenstellungen mit spezialkompilierten Hochleistungs-Methoden /// </summary> /// <param name="field">Feld, welches durchsucht werden soll</param> static void MiniSolverHashBuilder2(SokowahnField field) { CreateHashBuilder.CreateProject(field, PathTest); }
/// <summary> /// zeichnet ein bestimmtes Spielfeld /// </summary> /// <param name="field">Spielfeld, welches gezeichnet werden soll</param> public void DrawField(SokowahnField field) { if (field.width != drawField.width || field.height != drawField.height) { drawField = new SokowahnField(field); Init(); } var drawData = drawField.fieldData; var f = field.fieldData; int w = field.width; var minField = new PointInt(int.MaxValue, int.MaxValue); var maxField = new PointInt(int.MinValue, int.MinValue); for (int y = 0; y < field.height; y++) { for (int x = 0; x < w; x++) { char c = f[x + y * w]; if (drawData[x + y * w] == c) continue; if (x < minField.x) minField.x = x; if (y < minField.y) minField.y = y; if (x > maxField.x) maxField.x = x; if (y > maxField.y) maxField.y = y; drawData[x + y * w] = c; skin.BlitSprite(drawBitmap, x, y, SpriteType.Empty); switch (c) { case ' ': break; case '.': skin.BlitSprite(drawBitmap, x, y, SpriteType.EmptyFinish); break; case '$': skin.BlitSprite(drawBitmap, x, y, SpriteType.Box); break; case '*': skin.BlitSprite(drawBitmap, x, y, SpriteType.BoxFinish); break; case '@': skin.BlitSprite(drawBitmap, x, y, SpriteType.Player); break; case '+': skin.BlitSprite(drawBitmap, x, y, SpriteType.EmptyFinish); skin.BlitSprite(drawBitmap, x, y, SpriteType.Player); break; case '#': { var lo = SpriteType.WallFill; var ro = SpriteType.WallFill; var lu = SpriteType.WallFill; var ru = SpriteType.WallFill; if (CheckField(f, x - 1, y, w) && CheckField(f, x, y - 1, w)) lo = SpriteType.WallEdge; if (CheckField(f, x - 1, y, w) && !CheckField(f, x, y - 1, w)) lo = SpriteType.WallVertical; if (!CheckField(f, x - 1, y, w) && CheckField(f, x, y - 1, w)) lo = SpriteType.WallHorizon; if (!CheckField(f, x - 1, y, w) && !CheckField(f, x, y - 1, w)) lo = CheckField(f, x - 1, y - 1, w) ? SpriteType.WallCorner : SpriteType.WallFill; if (CheckField(f, x + 1, y, w) && CheckField(f, x, y - 1, w)) ro = SpriteType.WallEdge; if (CheckField(f, x + 1, y, w) && !CheckField(f, x, y - 1, w)) ro = SpriteType.WallVertical; if (!CheckField(f, x + 1, y, w) && CheckField(f, x, y - 1, w)) ro = SpriteType.WallHorizon; if (!CheckField(f, x + 1, y, w) && !CheckField(f, x, y - 1, w)) ro = CheckField(f, x + 1, y - 1, w) ? SpriteType.WallCorner : SpriteType.WallFill; if (CheckField(f, x - 1, y, w) && CheckField(f, x, y + 1, w)) lu = SpriteType.WallEdge; if (CheckField(f, x - 1, y, w) && !CheckField(f, x, y + 1, w)) lu = SpriteType.WallVertical; if (!CheckField(f, x - 1, y, w) && CheckField(f, x, y + 1, w)) lu = SpriteType.WallHorizon; if (!CheckField(f, x - 1, y, w) && !CheckField(f, x, y + 1, w)) lu = CheckField(f, x - 1, y + 1, w) ? SpriteType.WallCorner : SpriteType.WallFill; if (CheckField(f, x + 1, y, w) && CheckField(f, x, y + 1, w)) ru = SpriteType.WallEdge; if (CheckField(f, x + 1, y, w) && !CheckField(f, x, y + 1, w)) ru = SpriteType.WallVertical; if (!CheckField(f, x + 1, y, w) && CheckField(f, x, y + 1, w)) ru = SpriteType.WallHorizon; if (!CheckField(f, x + 1, y, w) && !CheckField(f, x, y + 1, w)) ru = CheckField(f, x + 1, y + 1, w) ? SpriteType.WallCorner : SpriteType.WallFill; skin.BlitSprite(drawBitmap, x, y, lo, SpriteParts.TopLeft); skin.BlitSprite(drawBitmap, x, y, ro, SpriteParts.TopRight); skin.BlitSprite(drawBitmap, x, y, lu, SpriteParts.BottomLeft); skin.BlitSprite(drawBitmap, x, y, ru, SpriteParts.BottomRight); } break; default: throw new Exception("unknown Char: '" + c + "'"); } } } if (minField.x != int.MaxValue) { maxField.x = maxField.x - minField.x + 1; maxField.y = maxField.y - minField.y + 1; drawBitmap.Present(pictureBitmap, minField.x * skin.spriteSize.w, minField.y * skin.spriteSize.h, maxField.x * skin.spriteSize.w, maxField.y * skin.spriteSize.h); } }
/// <summary> /// startet ein Mini-Konsolen Spiel /// </summary> /// <param name="field">Spielfeld, was gespielt werden soll</param> public static void Run(SokowahnField field) { var game = new SokowahnField(field); var steps = new Stack<ushort[]>(); for (; ; ) { string output = game.ToString(); int playerChar = output.IndexOfAny(new[] { '@', '+' }); Console.Clear(); Console.ForegroundColor = ConsoleColor.Gray; Console.Write(output.Substring(0, playerChar)); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write(output[playerChar]); Console.ForegroundColor = ConsoleColor.Gray; Console.Write(output.Remove(0, playerChar + 1)); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("Steps: " + steps.Count.ToString("N0")); Console.WriteLine(); Console.WriteLine("Remain: " + game.boxesRemain); if (game.boxesRemain == 0) return; bool step = false; var oldState = game.GetGameState(); switch (Console.ReadKey(true).Key) { case ConsoleKey.Escape: return; case ConsoleKey.A: case ConsoleKey.NumPad4: case ConsoleKey.LeftArrow: step = game.MoveLeft(); break; case ConsoleKey.D: case ConsoleKey.NumPad6: case ConsoleKey.RightArrow: step = game.MoveRight(); break; case ConsoleKey.W: case ConsoleKey.NumPad8: case ConsoleKey.UpArrow: step = game.MoveUp(); break; case ConsoleKey.S: case ConsoleKey.NumPad2: case ConsoleKey.DownArrow: step = game.MoveDown(); break; case ConsoleKey.Z: case ConsoleKey.Delete: case ConsoleKey.Backspace: { if (steps.Count == 0) break; game.SetGameState(steps.Pop(), 0); } break; default: continue; } if (step) { steps.Push(oldState); } } }
void InitGame(string gameTxt) { playField = new SokowahnField(gameTxt); undoList.Clear(); undoList.Push(playField.GetGameState()); drawField = new SokowahnField(playField); for (int i = 0; i < drawField.fieldData.Length; i++) drawField.fieldData[i] = '-'; int width = playField.width * BoxPixelWidth * Multi; int height = playField.height * BoxPixelHeight * Multi + 1; viewImage = new WriteableBitmap(width, height); viewContext = viewImage.GetBitmapContext(); GameImage.Source = viewImage; UpdateScreen(playField); }
static ushort[] TestScan(int width, TopLeftTodo topLeftTodo, HashSet<ushort> ways, SokowahnField view, bool debug = true) { var state = topLeftTodo.state; view.SetGameState(state); var result = ScanBestTopLeftWay(state[0], width, ways, new HashSet<ushort>(state.Skip(1))); if (state.Length > 1 && CheckDeadlock(topLeftTodo, view, result[result.Count - 1])) return null; if (debug) { Console.SetCursorPosition(0, Math.Max(0, Console.CursorTop - 2)); Console.WriteLine(new string(' ', Console.WindowWidth - 1)); Console.SetCursorPosition(0, 1); Console.WriteLine(view.ToString()); Console.WriteLine(); string line = state[0] + " - " + string.Join(", ", result.Skip(1)); Console.WriteLine(line); Console.WriteLine(); } var known = new HashSet<ushort>(topLeftTodo.known); if (known.Contains(result[result.Count - 1])) return new[] { result[result.Count - 1] }; var resultFiltered = result.Where(f => !known.Contains(f)).ToArray(); return resultFiltered; }