/// <summary> /// Normale Suche nach vorne (von der Startstellung aus beginnend) /// </summary> /// <param name="limit">maximale Anzahl der Rechenschritte</param> /// <returns>true, wenn noch weitere Berechnungen anstehen</returns> bool SucheVorwärts(int limit) { if (limit == 0) { vorwärtsTiefeAktuell = vorwärtsTiefe; return true; } if (vorwärtsTiefe >= vorwärtsSucher.Length) return false; if (limit > 1111100000) { maxZüge = limit - 1111100000 + 1; for (int i = maxZüge; i < vorwärtsSucher.Length; i++) { vorwärtsSucher[i].Dispose(); vorwärtsSucher[i] = null; vorwärtsSucherPunkte[i].Clear(); vorwärtsSucherPunkte[i] = null; } Array.Resize(ref vorwärtsSucher, Math.Min(vorwärtsSucher.Length, maxZüge)); Array.Resize(ref vorwärtsSucherPunkte, vorwärtsSucher.Length); MessageBox.Show("Maxzüge gesetzt auf: " + (maxZüge - 1)); vorwärtsTiefeAktuell = vorwärtsTiefe; return true; } bool schnell = limit >= 100000; var liste = vorwärtsSucher[vorwärtsTiefeAktuell]; var listePunkte = vorwärtsSucherPunkte[vorwärtsTiefeAktuell]; //if (liste.SatzAnzahl * 2L > (long)limit) //{ // if (liste.SatzAnzahl < (long)limit * 10L) // { // limit = (int)(liste.SatzAnzahl / 2L); // } //} limit = (int)Math.Min((long)limit, liste.SatzAnzahl); #region # // --- raumPool abfragen bzw. erstellen --- if (raumPool == null) { raumPool = Enumerable.Range(0, 256).Select(x => new SokowahnRaum(raumBasis, blocker, bekannteStellungen, zielStellungen)).ToArray(); SokowahnRaum r = new SokowahnRaum(raumPool[0]); r.KistenAnzahl = 1; int[] kistePos = new int[1]; int[] kisteIndex = new int[1]; int raumAnzahl = r.RaumAnzahl; einzelKistenDauer = new int[raumAnzahl]; char[] feldLeer = raumPool[0].FeldDataLeer; bool[] spielerRaum = SokowahnStaticTools.SpielfeldRaumScan(feldData, feldBreite); int[] raumZuFeld = Enumerable.Range(0, spielerRaum.Length).Where(i => spielerRaum[i]).ToArray(); raumZiele = new bool[raumAnzahl]; for (int i = 0; i < raumAnzahl; i++) raumZiele[i] = feldLeer[raumZuFeld[i]] == '.'; for (int k = 0; k < raumAnzahl; k++) { kistePos[0] = k; r.LadeStellung(kisteIndex, kistePos); einzelKistenDauer[k] = RechneMinZügeKiste(r); } laufFelder = new ushort[raumAnzahl * raumAnzahl]; ushort[] laufPosis = new ushort[raumAnzahl + 1]; for (int y = 0; y < laufFelder.Length; y += raumAnzahl) { int pp = 0; laufPosis[pp++] = (ushort)(y / raumAnzahl); for (int x = 0; x < raumAnzahl; x++) { if (x > pp) throw new Exception("Fatal!"); ushort p = laufPosis[x]; laufFelder[y + x] = p; if ((laufPosis[pp] = (ushort)r.raumOben[p]) < raumAnzahl && !laufPosis.Take(pp).Any(i => i == laufPosis[pp])) pp++; if ((laufPosis[pp] = (ushort)r.raumRechts[p]) < raumAnzahl && !laufPosis.Take(pp).Any(i => i == laufPosis[pp])) pp++; if ((laufPosis[pp] = (ushort)r.raumUnten[p]) < raumAnzahl && !laufPosis.Take(pp).Any(i => i == laufPosis[pp])) pp++; if ((laufPosis[pp] = (ushort)r.raumLinks[p]) < raumAnzahl && !laufPosis.Take(pp).Any(i => i == laufPosis[pp])) pp++; } } } #endregion #region # // --- Teilliste mit den besten Stellungen erzeugen (falls notwendig) --- if (liste.SatzAnzahl > limit) { int punkteOk = 0; int findAnzahl = 0; int gutDazu = 0; foreach (var satz in listePunkte.OrderBy(x => x.Key)) { punkteOk = satz.Key; findAnzahl += satz.Value; if (findAnzahl >= limit) { gutDazu = limit - (findAnzahl - satz.Value); break; } } var listeOk = new SokowahnLinearList2(raumBasis.KistenAnzahl + 1 + 2, TempOrdner); var listeAufheben = new SokowahnLinearList2(raumBasis.KistenAnzahl + 1 + 2, TempOrdner); long bis = liste.SatzAnzahl; for (long i = 0; i < bis; i++) { var stellung = liste.Pop(); SokowahnPunkte punkte = new SokowahnPunkte(stellung); if (punkte.tiefeMax <= punkteOk) { if (punkte.tiefeMax == punkteOk) { if (gutDazu > 0) { gutDazu--; } else { listeAufheben.Add(stellung); continue; } } listeOk.Add(stellung); if (listeOk.SatzAnzahl == limit) break; } else { listeAufheben.Add(stellung); } } if (liste.SatzAnzahl < listeAufheben.SatzAnzahl) { bis = liste.SatzAnzahl; for (long i = 0; i < bis; i++) listeAufheben.Add(liste.Pop()); vorwärtsSucher[vorwärtsTiefeAktuell] = listeAufheben; liste.Dispose(); } else { bis = listeAufheben.SatzAnzahl; for (long i = 0; i < bis; i++) liste.Add(listeAufheben.Pop()); listeAufheben.Dispose(); } liste = listeOk; } #if DEBUG if (limit != liste.SatzAnzahl) throw new Exception("aua?"); #endif #endregion SokowahnRaum raum = raumPool[Thread.CurrentThread.ManagedThreadId]; int mx = maxZüge - rückwärtsTiefe; var ergebnisse = Enumerable.Range(0, limit).Select(i => liste.Pop()).SelectMany(stellung => { raum.LadeStellung(stellung, vorwärtsTiefeAktuell); SokowahnPunkte punkte = new SokowahnPunkte(stellung); listePunkte[punkte.tiefeMax]--; if (bekannteStellungen.Get(raum.Crc) < vorwärtsTiefeAktuell) return Enumerable.Empty<SokowahnStellungRun>(); return raum.GetVariantenRun().Where(v => v.zugTiefe <= mx && v.zugTiefe < bekannteStellungen.Get(v.crc64)); }).ToArray(); #if !parallelDeaktivieren var punkteListe = new SokowahnPunkte[ergebnisse.Length]; if (schnell) { ParallelEnumerable.Range(0, ergebnisse.Length).Select(v => { SokowahnRaum r = raumPool[Thread.CurrentThread.ManagedThreadId]; r.LadeStellung(ergebnisse[v]); punkteListe[v] = r.BerechnePunkte2(einzelKistenDauer, laufFelder); //punkteListe[v] = r.BerechnePunkteSchnell(einzelKistenDauer); return true; }).Count(); } else { ParallelEnumerable.Range(0, ergebnisse.Length).Select(v => { SokowahnRaum r = raumPool[Thread.CurrentThread.ManagedThreadId]; r.LadeStellung(ergebnisse[v]); punkteListe[v] = r.BerechnePunkte(einzelKistenDauer); return true; }).Count(); } #endif for (int v = 0; v < ergebnisse.Length; v++) { var variante = ergebnisse[v]; int findQuelle = bekannteStellungen.Get(variante.crc64); #if parallelDeaktivieren raum.LadeStellung(variante); // SokowahnPunkte punkte = raum.BerechnePunkte(einzelKistenDauer); SokowahnPunkte punkte = raum.BerechnePunkte2(einzelKistenDauer, laufFelder); // SokowahnPunkte punkte = raum.BerechnePunkte3(einzelKistenDauer, laufFelder); #else var punkte = punkteListe[v]; #endif if (variante.zugTiefe < findQuelle) // neue Stellung oder bessere Variante gefunden { int findZiel = zielStellungen.Get(variante.crc64); if (findZiel < 65535) { if (variante.zugTiefe + 60000 - findZiel < maxZüge) { #region // --- neue (bessere) Variante gefunden --- maxZüge = variante.zugTiefe + 60000 - findZiel; //gefundenCrc = variante.crc64; for (int i = maxZüge; i < vorwärtsSucher.Length; i++) { vorwärtsSucher[i].Dispose(); vorwärtsSucher[i] = null; vorwärtsSucherPunkte[i].Clear(); vorwärtsSucherPunkte[i] = null; } if (maxZüge < vorwärtsSucher.Length) { Array.Resize(ref vorwärtsSucher, maxZüge); Array.Resize(ref vorwärtsSucherPunkte, maxZüge); } #endregion } continue; } //if (punkte.tiefeMin + variante.zugTiefe < maxZüge) //if (variante.zugTiefe + rückwärtsTiefe < maxZüge) if (variante.zugTiefe + rückwärtsTiefe < maxZüge && punkte.tiefeMin + variante.zugTiefe < maxZüge) { if (findQuelle < 65535) bekannteStellungen.Update(variante.crc64, variante.zugTiefe); else bekannteStellungen.Add(variante.crc64, variante.zugTiefe); VorwärtsAdd(variante, punkte); } } } vorwärtsTiefeAktuell++; while (vorwärtsTiefe < vorwärtsSucher.Length && vorwärtsSucher[vorwärtsTiefe].SatzAnzahl == 0) { vorwärtsTiefe++; vorwärtsTiefeAktuell = vorwärtsTiefe; } if (vorwärtsTiefeAktuell == vorwärtsSucher.Length) { vorwärtsTiefeAktuell = vorwärtsTiefe; } return true; }
/// <summary> /// reduziert den Speicherverbrauch (falls ein Multiplikator angegeben wurde) und lagert die Daten in eine Temp-Datei aus /// </summary> /// <returns>Anzahl der Bytes, welche frei geworden sind</returns> public long Refresh() { if (schreibBuffer == null || schreibBuffer.Length == BufferElemente) return 0; // wurde bereits Refreshed #if byteModus var tmp = new SokowahnLinearList2Byte(satzGröße, tempOrdner); #else var tmp = new SokowahnLinearList2(satzGröße, tempOrdner); #endif long bis = this.SatzAnzahl; for (long i = 0; i < bis; i++) { tmp.Add(this.Pop()); } this.Dispose(); this.bufferMax = tmp.bufferMax; this.leseBuffer = tmp.leseBuffer; this.leseBufferGro = tmp.leseBufferGro; this.leseBufferPos = tmp.leseBufferPos; this.schreibBuffer = tmp.schreibBuffer; this.schreibBufferPos = tmp.schreibBufferPos; this.tempDatei = tmp.tempDatei; tmp.tempDatei = null; this.tmpBufferBelegt = tmp.tmpBufferBelegt; this.tmpBufferFrei = tmp.tmpBufferFrei; tmp.Dispose(); GC.Collect(); return SatzAnzahl * (long)sizeof(SatzTyp); }
/// <summary> /// berechnet die nächsten Blocker /// </summary> /// <param name="limit">maximale Anzahl der Berechnungen, oder 0, wenn die Berechnung beendet werden soll</param> /// <returns>true, wenn noch weitere Berechnungen anstehen</returns> public bool Next(int limit) { int maxKisten = basisRaum.FeldData.Where(c => c == '$' || c == '*').Count(); if (limit <= 0) // Marker für Abbruch { return false; } switch (status) { #region # case BlockerStatus.Init: // Start einer Blocker-Sucher (eine neue Kistenanzahl wird ausprobiert) case BlockerStatus.Init: { if (suchKistenAnzahl + 1 >= maxKisten) { Abbruch(); return false; // Kisten-Limit erreicht } suchKistenAnzahl++; SammleKistenInit(false); status = BlockerStatus.SammleStartStellungen; return true; } #endregion #region # case BlockerStatus.SammleStartStellungen: // sammelt alle Start-Stellungen mit der entsprechenden Kistenanzahl (sind automatisch auch gleichzeitig Stellungen, mit denen das Ziel erreichbar ist) case BlockerStatus.SammleStartStellungen: { limit--; while (limit > 0 && SammleKistenNext()) limit--; if (SammleKistenNext()) { return true; } else { SammleKistenInit(true); status = BlockerStatus.SammleZielStellungen; return true; } } #endregion #region # case BlockerStatus.SammleZielStellungen: // sammelt alle Ziel-Stellungen, wo jede Kiste auf ein Zielfeld steht case BlockerStatus.SammleZielStellungen: { limit--; while (limit > 0 && SammleKistenNext()) limit--; if (!SammleKistenNext()) status = BlockerStatus.SucheVarianten; return true; } #endregion #region # case BlockerStatus.SucheVarianten: // sucht vorwärts alle möglichen Varianten (eventuell bereits vorhandene Blocker werden beachten) case BlockerStatus.SucheVarianten: { if (prüfListe.SatzAnzahl == 0) { prüfListe.Dispose(); prüfListe = prüfListeSammler; prüfListeSammler = new SokowahnLinearList2(suchKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", listeMax / 32768); if (prüfListe.SatzAnzahl == 0) { prüfListeSammler.Dispose(); status = BlockerStatus.VerschmelzeZielStellungen; verschmelzenRest = prüfListeBöse.SatzAnzahl; return true; } } limit = (int)Math.Min((long)limit, prüfListe.SatzAnzahl); var ergebnisse = Enumerable.Range(0, limit).Select(i => prüfListe.Pop()).AsParallel().SelectMany(stellung => { SokowahnRaum raum = threadRäume[Thread.CurrentThread.ManagedThreadId]; raum.LadeStellung(stellung, 0, 0); return raum.GetVarianten(this); }).Where(x => bekannteStellungen.Get(x.crc64) == 65535).ToArray(); foreach (var stellung in ergebnisse) { int find = bekannteStellungen.Get(stellung.crc64); if (find == 65535) { bekannteStellungen.Add(stellung.crc64, 12345); prüfListeSammler.Add(stellung.raumSpielerPos, stellung.kistenZuRaum); prüfListeBöse.Add(stellung.raumSpielerPos, stellung.kistenZuRaum); } } return true; } #endregion #region # case BlockerStatus.VerschmelzeZielStellungen: // ermittelt (anhand der Rückwärts-Suche) welche der ermittelten Stellungen zum Ziel führen können und markiert diese case BlockerStatus.VerschmelzeZielStellungen: { if (prüfListe.SatzAnzahl == 0) { prüfListe.Dispose(); prüfListe = prüfListeGut; prüfListeGut = new SokowahnLinearList2(suchKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", listeMax / 32768); if (prüfListe.SatzAnzahl == 0) { prüfListe.Dispose(); prüfListeGut.Dispose(); prüfListe = null; prüfListeGut = null; status = BlockerStatus.ErstelleBlocker; tempBlocker = new BlockerFeld[raumAnzahl]; for (int i = 0; i < tempBlocker.Length; i++) { tempBlocker[i].geprüfteStellungen = bekannteStellungen.HashAnzahl; tempBlocker[i].kistenNummerLeer = suchKistenAnzahl; } return true; } } limit = (int)Math.Min((long)limit, prüfListe.SatzAnzahl); verschmelzenRest -= (long)limit; var ergebnisse = Enumerable.Range(0, limit).Select(i => prüfListe.Pop()).AsParallel().SelectMany(stellung => { SokowahnRaum raum = threadRäume[Thread.CurrentThread.ManagedThreadId]; raum.LadeStellung(stellung, 0, 0); return raum.GetVariantenRückwärts(); }).Where(x => bekannteStellungen.Get(x.crc64) < 65535).ToArray(); foreach (var stellung in ergebnisse) { int find = bekannteStellungen.Get(stellung.crc64); if (find == 60000) { continue; } else { prüfListeGut.Add(stellung.raumSpielerPos, stellung.kistenZuRaum); bekannteStellungen.Update(stellung.crc64, 60000); } } return true; } #endregion #region # case BlockerStatus.ErstelleBlocker: // erstellt die Blocker anhand der restlichen Stellungen, welche nicht zum Ziel führten case BlockerStatus.ErstelleBlocker: { limit = (int)Math.Min((long)limit, prüfListeBöse.SatzAnzahl); var ergebnisse = Enumerable.Range(0, limit).Select(i => prüfListeBöse.Pop()).Select(x => { tmpRaum.LadeStellung(x, 0, 0); return tmpRaum.GetStellung(); }).Where(stellung => bekannteStellungen.Get(stellung.crc64) == 12345).ToArray(); foreach (var stellung in ergebnisse) { tempBlocker[stellung.raumSpielerPos].Add(stellung.kistenZuRaum, suchKistenAnzahl); } if (prüfListeBöse.SatzAnzahl == 0) { int startPos = bekannteBlocker.Length; Array.Resize(ref bekannteBlocker, startPos + raumAnzahl); for (int i = 0; i < raumAnzahl; i++) bekannteBlocker[startPos + i] = tempBlocker[i]; for (int i = 0; i < bekannteBlocker.Length; i++) bekannteBlocker[i].kistenNummerLeer = suchKistenAnzahl + 1; long geprüfteStellungenGesamt = 0; for (int i = 0; i < bekannteBlocker.Length; i += raumAnzahl) geprüfteStellungenGesamt += bekannteBlocker[i].geprüfteStellungen; if (geprüfteStellungenGesamt > 100000) SpeichereAlleBlocker(); status = BlockerStatus.Init; bekannteStellungen = null; } return true; } #endregion #region # case BlockerStatus.Fertig: // Blockersuche wurde beendet, (nur noch der Check-Methode steht bereit) case BlockerStatus.Fertig: return false; #endregion default: throw new Exception("Status unbekant: " + status); } }