/// <summary> /// Konstruktor /// </summary> /// <param name="spielFeld">Spielfeld als Textzeilen</param> public SokoWahn_5th(string spielFeld) { SokowahnStaticTools.SpielfeldEinlesen(spielFeld, out feldBreite, out feldHöhe, out feldSpielerStartPos, out feldData, out feldDataLeer); raumBasis = new SokowahnRaum(feldData, feldBreite); Directory.CreateDirectory(TempOrdner); // --- Vorwärtssuche initialisieren --- bekannteStellungen = new SokowahnHash_Index24Multi(); bekannteStellungen.Add(raumBasis.Crc, 0); vorwärtsTiefe = 0; vorwärtsTiefeAktuell = 0; vorwärtsSucher = new SokowahnLinearList2[0]; vorwärtsSucherPunkte = new Dictionary<int, int>[0]; VorwärtsAdd(raumBasis.GetStellung(), new SokowahnPunkte()); // --- Zielstellungen und Rückwärtssuche initialisieren --- zielStellungen = new SokowahnHash_Index24Multi(); rückwärtsTiefe = 0; rückwärtsSucher = new SokowahnLinearList2[0]; foreach (SokowahnStellung stellung in SokowahnStaticTools.SucheZielStellungen(raumBasis)) { zielStellungen.Add(stellung.crc64, 60000); RückwärtsAdd(stellung); } ulong spielFeldCrc = Crc64.Start.Crc64Update(raumBasis.FeldBreite, raumBasis.FeldHöhe, raumBasis.FeldData); blocker = new SokowahnBlockerB2(Environment.CurrentDirectory + "\\temp\\blocker2_x" + spielFeldCrc.ToString("x").PadLeft(16, '0') + ".gz", raumBasis); }
/// <summary> /// initialisiert die ersten Blocker-Kisten für den SammlerStart /// </summary> /// <param name="zielVariante">gibt an, ob nur Varianten ermittelt werden sollen, wo alle Kisten bereits auf dem Zielfeld stehen</param> void SammleKistenInit(bool zielVariante) { sammlerCheckKisten = Enumerable.Range(0, suchKistenAnzahl).Select(i => i).ToArray(); sammlerCheckKisten[suchKistenAnzahl - 1]--; // letzte eins zurück setzen, damit beim ersten sammlerNext() auch die erste Variante gesetzt werden kann char[] feldData = basisRaum.FeldData; int feldBreite = basisRaum.FeldBreite; bool[] spielerRaum = SokowahnStaticTools.SpielfeldRaumScan(feldData, feldBreite); raumAnzahl = spielerRaum.Count(x => x); int[] raumZuFeld = Enumerable.Range(0, spielerRaum.Length).Where(i => spielerRaum[i]).ToArray(); int[] feldZuRaum = Enumerable.Range(0, feldData.Length).Select(i => spielerRaum[i] ? raumZuFeld.ToList().IndexOf(i) : -1).ToArray(); if (zielVariante) { sammlerCheckKistenRaum = raumZuFeld.Select(i => (feldData[i] == '.' || feldData[i] == '*' || feldData[i] == '+') ? feldZuRaum[i] : -1).Where(i => i >= 0).ToArray(); } else { sammlerCheckKistenRaum = raumZuFeld.Select(i => (feldData[i] == '$' || feldData[i] == '*') ? feldZuRaum[i] : -1).Where(i => i >= 0).ToArray(); } if (!zielVariante) // nur beim ersten mal Initialisieren { tmpRaum = new SokowahnRaum(feldData, feldBreite); tmpRaum.KistenAnzahl = suchKistenAnzahl; threadRäume = Enumerable.Range(0, 256).Select(i => new SokowahnRaum(tmpRaum)).ToArray(); bekannteStellungen = new SokowahnHash_Index24Multi(); prüfListe = new SokowahnLinearList2(suchKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", listeMax / 32768); prüfListeSammler = new SokowahnLinearList2(suchKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", listeMax / 32768); prüfListeGut = new SokowahnLinearList2(suchKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", listeMax / 32768); prüfListeBöse = new SokowahnLinearList2(suchKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", listeMax / 32768); } }
/// <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); } }
/// <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) { switch (blockerStatus) { #region # case BlockerStatus.init: case BlockerStatus.init: { sammlerKistenAnzahl++; if (sammlerKistenAnzahl == basisRaum.KistenAnzahl) { Abbruch(); return false; } sammlerAbfrage = SammlerBerechneZielStellungen().GetEnumerator(); sammlerHash = new SokowahnHash_Index16Multi(); sammlerStats = new long[60001]; rückwärtsSucher = Enumerable.Range(0, maxVorschau).Select(x => new SokowahnLinearList2(sammlerKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", multi32k)).ToArray(); rückwärtsTiefe = 0; alleStellungen = new SokowahnLinearList2(sammlerKistenAnzahl + 1, Environment.CurrentDirectory + "\\temp\\", multi32k * 4); alleBlockerHash = new HashSet<ulong>(); char[] feldData = basisRaum.FeldData; int feldBreite = basisRaum.FeldBreite; bool[] spielerRaum = SokowahnStaticTools.SpielfeldRaumScan(feldData, feldBreite); raumAnzahl = spielerRaum.Count(x => x); tempBlocker = new SokowahnBlockerB.BlockerFeld[raumAnzahl]; for (int i = 0; i < tempBlocker.Length; i++) tempBlocker[i].kistenNummerLeer = sammlerKistenAnzahl; blockerStatus = BlockerStatus.sammleZiele; return true; } #endregion #region # case BlockerStatus.sammleZiele: case BlockerStatus.sammleZiele: { while (limit-- > 0 && sammlerAbfrage.MoveNext()) { var satz = sammlerAbfrage.Current; if (sammlerHash.Get(satz.crc64) == 65535) { sammlerHash.Add(satz.crc64, 60000); sammlerStats[60000]++; rückwärtsSucher[0].Add(satz.raumSpielerPos, satz.kistenZuRaum); alleStellungen.Add(satz.raumSpielerPos, satz.kistenZuRaum); } } if (limit >= 0) { tempRaum = new SokowahnRaum(basisRaum); tempRaum.KistenAnzahl = sammlerKistenAnzahl; threadRäume = Enumerable.Range(0, 256).Select(i => new SokowahnRaum(tempRaum)).ToArray(); blockerStatus = BlockerStatus.suchModus; } return true; } #endregion #region # case BlockerStatus.suchModus: case BlockerStatus.suchModus: { if (SucheRückwärts(limit)) return true; Array.Resize(ref bekannteBlockerHashes, sammlerKistenAnzahl); bekannteBlockerHashes[sammlerKistenAnzahl - 1] = sammlerHash; Array.Resize(ref bekannteSammlerStats, sammlerKistenAnzahl); var tmp = sammlerStats.Reverse().ToList(); while (tmp[tmp.Count - 1] == 0) tmp.RemoveAt(tmp.Count - 1); bekannteSammlerStats[sammlerKistenAnzahl - 1] = tmp.ToArray(); for (int i = 0; i < rückwärtsSucher.Length; i++) rückwärtsSucher[i].Dispose(); blockerStatus = BlockerStatus.blockerSuche; return true; } #endregion #region # case BlockerStatus.blockerSuche: case BlockerStatus.blockerSuche: { limit = (int)Math.Min((long)limit, alleStellungen.SatzAnzahl); if (limit == 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].geprüfteStellungen = sammlerHash.HashAnzahl; bekannteBlocker[i].kistenNummerLeer = sammlerKistenAnzahl + 1; } long geprüfteStellungenGesamt = 0; for (int i = 0; i < bekannteBlocker.Length; i += raumAnzahl) geprüfteStellungenGesamt += bekannteBlocker[i].geprüfteStellungen; for (int i = 0; i < bekannteBlocker.Length; i++) bekannteBlocker[i].Sortieren(); blockerStatus = BlockerStatus.init; return true; } var stellungen = alleStellungen.Pop(limit); int satzGröße = alleStellungen.SatzGröße; var ergebnisse = Enumerable.Range(0, limit) #if !parallelDeaktivieren .AsParallel() #if parallelGeordnet .AsOrdered() #endif #endif .SelectMany(stellung => { SokowahnRaum raum = threadRäume[Thread.CurrentThread.ManagedThreadId]; raum.LadeStellung(stellungen, stellung * satzGröße, 0); // return raum.GetVarianten(this).Where(x => sammlerHash.Get(x.crc64) == 65535); var ausgabe = raum.GetVarianten(this).Where(x => { if (sammlerHash.Get(x.crc64) == 65535) return true; x.zugTiefe = 60000 - sammlerHash.Get(x.crc64); //string dbg = x.Debug(basisRaum); return false; } ); return ausgabe; }).ToArray(); foreach (var stellung in ergebnisse) { if (alleBlockerHash.Contains(stellung.crc64)) continue; alleBlockerHash.Add(stellung.crc64); tempBlocker[stellung.raumSpielerPos].Add(stellung.kistenZuRaum, sammlerKistenAnzahl); } return true; } #endregion #region # case BlockerStatus.bereit: case BlockerStatus.bereit: { return false; } #endregion default: throw new NotImplementedException(); } }
/// <summary> /// entfernt unnötige Einträge aus der Hashtabelle (sofern möglich) /// </summary> /// <returns>Anzahl der Einträge, welche entfernt werden konnten</returns> public long Refresh() { // --- hash aufräumen --- var old = hashStellungen; hashStellungen = new SokowahnHash_Index24Multi(); var übertrag = new List<StellungsSatz>(); foreach (var satz in old.GetAll()) { if (satz.Value <= suchTiefe) { übertrag.Add(new StellungsSatz { stellung = satz.Key, tiefe = satz.Value }); } else { hashStellungen.Add(satz.Key, satz.Value); } } übertrag.Sort((x, y) => x.stellung.CompareTo(y.stellung)); ArchivEintrag(übertrag); return archivGro + (long)48000000; }
/// <summary> /// berechnet den nächsten Schritt /// </summary> /// <param name="limit">Anzahl der zu berechnenden (kleinen) Arbeitsschritte, negativer Wert = optionale Vorbereitungsschritte)</param> /// <returns>gibt an, ob noch weitere Berechnungen anstehen</returns> public bool Next(int limit) { if (limit == 0) return false; switch (modus) { #region # case Modus.unbekannt: case Modus.Unbekannt: { if (limit > 0) { modus = Modus.SucheInit; // Initialisierung für die Suche direkt starten } else { modus = Modus.SteinerInit; // Initialisierung der Vorbereitung starten } return true; } #endregion #region # case Modus.steinerInit: case Modus.SteinerInit: { if (limit > 0) { modus = Modus.SucheInit; // Vorbereitung abbrechen und Suche direkt starten return true; } int maxKisten = feldData.Where(c => c == '$' || c == '*').Count(); kistenAnzahl++; if (kistenAnzahl >= maxKisten) { return false; // Maximale Kistenanzahl erreicht, weitere Berechnungen sind nicht mehr möglich } modus = Modus.SteinerVarianten; // neue Aufgabe: alle Varianten ermitteln KistenSteinerInit(); steinerPrüfStellungen = new List<Variante>(); steinerPrüfstellungenPos = 0; return true; } #endregion #region # case Modus.steinerVarianten: case Modus.SteinerVarianten: { if (limit > 0) { modus = Modus.SucheInit; // Vorbereitung abbrechen und Suche direkt starten return true; } limit = -limit; while (limit > 0) { if (steinerPrüfstellungenPos == steinerPrüfStellungen.Count) { limit--; if (!KistenSteinerNext()) { modus = Modus.SteinerLösen; steinerBöseStellungen = steinerPrüfStellungen.Where(x => hashStellungen.Get(x.stellungCrc) == 1).ToArray(); steinerPrüfStellungen = steinerPrüfStellungen.Where(x => hashStellungen.Get(x.stellungCrc) == 2).ToList(); var old = hashStellungen; hashStellungen = new SokowahnHash_Index24Multi(); foreach (var satz in old.GetAll().Where(x => x.Value == 1)) { hashStellungen.Add(satz.Key, satz.Value); } steinerPrüfstellungenPos = 0; return true; } } else { var prüf = steinerPrüfStellungen[steinerPrüfstellungenPos++]; LadeStellung(prüf.stellung); limit--; if (kistenMitZiel == kistenAnzahl) { if (hashStellungen.Get(prüf.stellungCrc) == 65535) hashStellungen.Add(prüf.stellungCrc, 2); else hashStellungen.Update(prüf.stellungCrc, 2); } foreach (var neuPrüf in SucheVariantenSteiner()) { steinerPrüfStellungen.Add(neuPrüf); hashStellungen.Add(neuPrüf.stellungCrc, 1); } } } return true; } #endregion #region # case Modus.steinerLösen: case Modus.SteinerLösen: { if (limit > 0) { modus = Modus.SucheInit; // Vorbereitung abbrechen und Suche direkt starten return true; } limit = -limit; while (limit > 0) { if (steinerPrüfstellungenPos == steinerPrüfStellungen.Count) { // var dummy = steinerBöseStellungen.Where(x => bekannteStellungen.ContainsKey(x.stellungCrc)).ToArray(); foreach (var satz in steinerBöseStellungen.Where(x => hashStellungen.Get(x.stellungCrc) < 65535)) { SteinerBlockerDazu(satz.stellung); } modus = Modus.SteinerInit; return true; } var prüf = steinerPrüfStellungen[steinerPrüfstellungenPos++]; LadeStellung(prüf.stellung); limit--; suchTiefe = 99999; var vorgänger = SucheVariantenVorgänger().ToArray(); if (vorgänger.Length > 0) { foreach (var check in vorgänger) { if (hashStellungen.Get(check.stellungCrc) < 65535) { steinerPrüfStellungen.Add(new Variante { stellung = check.stellung, stellungCrc = check.stellungCrc, tiefe = 1 }); hashStellungen.Remove(check.stellungCrc); } } } else // gute Stellung, da keine Vorgänger möglich sind { if (hashStellungen.Get(prüf.stellungCrc) < 65535) { hashStellungen.Remove(prüf.stellungCrc); } } } return true; } #endregion #region # case Modus.sucheInit: case Modus.SucheInit: { raumSpielerPos = feldZuRaum[startSpielerPos]; KistenStandardInit(); suchListenSatz = kistenAnzahl + 1; suchTiefe = 0; SuchListeInsert(GetStellung(), suchTiefe); hashStellungen = new SokowahnHash_Index24Multi(); hashStellungen.Add(StellungCrc(GetStellung()), 0); modus = Modus.SucheRechne; return true; } #endregion #region # case Modus.sucheRechne: case Modus.SucheRechne: { var listeAuswahl = suchListe[suchTiefe]; limit = (int)Math.Min(limit, listeAuswahl.SatzAnzahl); for (int listenPos = 0; listenPos < limit; listenPos++) { LadeStellung(listeAuswahl.Pop()); if (kistenMitZiel == kistenAnzahl) { modus = Modus.SucheGefunden; return false; } if (StellungBekannt(StellungCrc(GetStellung())) < suchTiefe) continue; // zu prüfende Stellung wurde schon früher (mit weniger Tiefe) berechnet foreach (var variante in SucheVariantenHashcheck()) { var tmpTiefe = StellungBekannt(variante.stellungCrc); if (tmpTiefe >= 0) { if (tmpTiefe <= variante.tiefe) continue; // Vorschlag ignorieren hashStellungen.Update(variante.stellungCrc, variante.tiefe); SuchListeInsert(variante.stellung, variante.tiefe); } else { hashStellungen.Add(variante.stellungCrc, variante.tiefe); SuchListeInsert(variante.stellung, variante.tiefe); } } } if (listeAuswahl.SatzAnzahl == 0) suchTiefe++; // keine weiteren Stellungen bei dieser Zugtiefe vorhanden, dann zur nächsten Zugtiefe springen return true; } #endregion case Modus.SucheGefunden: return false; // Ergebnis gefunden, keine weiteren Berechnungen notwendig default: throw new Exception("? " + modus); } }
/// <summary> /// initialisiert die ersten Steiner-Kisten /// </summary> void KistenSteinerInit() { raumZuKiste = raumZuFeld.Select(i => -1).ToArray(); kistenZuRaum = new int[kistenAnzahl]; steinerCheckKisten = Enumerable.Range(0, kistenAnzahl).Select(i => i).ToArray(); steinerCheckKisten[kistenAnzahl - 1]--; // letzte eins zurück setzen, damit beim ersten steinerNext() auch die erste Variante gesetzt werden kann steinerCheckKistenRaum = raumZuFeld.Select(i => (feldData[i] == '$' || feldData[i] == '*') ? feldZuRaum[i] : -1).Where(i => i >= 0).ToArray(); hashStellungen = new SokowahnHash_Index24(); }
/// <summary> /// Konstruktor /// </summary> /// <param name="raum">Basisdaten anhand eines vorhanden Raumes nutzen</param> /// <param name="blocker">bekannte Blocker</param> /// <param name="startHash">bekannte Einträge in der Start-Hashtable</param> /// <param name="zielHash">bekannte Einträge in der Ziel-Hashtable</param> public SokowahnRaum(SokowahnRaum raum, ISokowahnBlocker blocker, ISokowahnHash startHash, ISokowahnHash zielHash) { this.feldData = raum.feldData; this.feldBreite = raum.feldBreite; this.raumAnzahl = raum.raumAnzahl; this.raumLinks = raum.raumLinks; this.raumRechts = raum.raumRechts; this.raumOben = raum.raumOben; this.raumUnten = raum.raumUnten; this.raumSpielerPos = raum.raumSpielerPos; this.spielerZugTiefe = raum.spielerZugTiefe; this.raumZuKisten = raum.raumZuKisten.ToArray(); // Kopie erstellen this.kistenAnzahl = raum.kistenAnzahl; this.kistenZuRaum = raum.kistenZuRaum.ToArray(); // Kopie erstellen this.tmpCheckRaumPosis = new int[raumAnzahl]; this.tmpCheckRaumTiefe = new int[raumAnzahl]; this.tmpRaumCheckFertig = new bool[raumAnzahl + 1]; this.tmpRaumCheckFertig[raumAnzahl] = true; // Ende-Feld schon auf fertig setzen this.merkBlocker = blocker; this.merkStartHash = startHash; this.merkZielHash = zielHash; }