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