/// <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(); }
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; } } } }
/// <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 }