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