/// <summary> /// bewegt den Spieler um eins nach unten /// </summary> /// <param name="zielHash">Ziel, in dem das neue Spielfeld erstellt wird</param> /// <returns>true, wenn auch eine Box verschoben wurde</returns> public bool BewegeUnten(HashFeld zielHash) { int ziel = zielHash.hashPos; int spieler = this.SpielerPos; for (int i = 0; i < hashSatzGro; i++) hashData[ziel + i] = hashData[hashPos + i]; hashData[ziel + spieler] ^= 8; // Spielerfigur entfernen spieler += feldBreite; zielHash.SpielerPos = spieler; hashData[ziel + spieler] ^= 8; // Spielerfigur setzen if ((hashData[ziel + spieler] & 2) > 0) // Box vorhanden? { hashData[ziel + spieler] ^= 2; // Box entfernen hashData[ziel + spieler + feldBreite] ^= 2; // Box neu setzen switch ((hashData[ziel + spieler] & 0x4) | ((hashData[ziel + spieler + feldBreite] << 4) & 0x40)) { case 0x00: break; case 0x04: zielHash.RestWürfel++; break; case 0x40: zielHash.RestWürfel--; break; case 0x44: break; } return true; } return false; }
/// <summary> /// System, welche die FehlerMapErstellen erstellt /// </summary> /// <param name="würfel">Anzahl der zu testenden Würfel (muss immer weniger sein als die Ziel-Anzahl)</param> void FehlerMapErstellen(int würfel) { int[] kannFelder = Enumerable.Range(0, feldAnzahl).Where(pos => erlaubteBoxFelder[pos]).ToArray(); var ofss = new[] { -1, +1, -feldBreite, +feldBreite }; switch (würfel) { #region # case 1: // --- einzelne unerlaubte Boxen ermitteln --- case 1: { Dictionary<int, bool> spielerFertig = kannFelder.ToDictionary(p => p, p => false); foreach (int boxPos in kannFelder) { List<int> spielerGut = new List<int>(); List<int> spielerSchlecht = new List<int>(); foreach (int p in kannFelder) spielerFertig[p] = false; MapReset(true); feldData[boxPos] ^= 2; spielerFertig[boxPos] = true; foreach (int spielerPos in kannFelder.Where(pos => !spielerFertig[pos])) { #region # // --- Spieler setzen und Hash resetten --- feldData[spielerPos] ^= 8; HashFeld hashFeld = new HashFeld(0, feldData, true); hashIndex.Clear(); hashGro = hashSatzGro; feldData[spielerPos] ^= 8; #endregion #region # // --- prüfen, ob Stellung gelöst werden kann --- bool find = false; for (int hashPos = 0; hashPos < hashGro; hashPos += hashSatzGro) { hashFeld = new HashFeld(hashPos); if (hashFeld.RestWürfel == 0) // machbare Lösung gefunden? { find = true; break; } if (hashGro > hashInfoMax * hashSatzGro) { hashInfoMax *= 2; Array.Resize(ref hashInfo, hashInfoMax + 65536); } if (hashFeld.KannLinks) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeLinks(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannRechts) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeRechts(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannOben) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeOben(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannUnten) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeUnten(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } } #endregion #region # // --- alle zusätzlich erreichbare Spielerpositionen ermitteln --- List<int> alleSpielerPos = new List<int>(); alleSpielerPos.Add(spielerPos); spielerFertig[spielerPos] = true; for (int i = 0; i < alleSpielerPos.Count; i++) { int checkPos = alleSpielerPos[i]; foreach (int ofs in ofss) { if ((feldData[checkPos + ofs] & 3) == 0 && !alleSpielerPos.Any(p => p == checkPos + ofs)) { alleSpielerPos.Add(checkPos + ofs); if (erlaubteBoxFelder[checkPos + ofs]) spielerFertig[checkPos + ofs] = true; } } } #endregion if (find) { spielerGut.AddRange(alleSpielerPos); } else { spielerSchlecht.AddRange(alleSpielerPos); hashGro = 0; hashIndex.Clear(); } } if (spielerGut.Count == 0) erlaubteBoxFelder[boxPos] = false; } FehlerDataInit(); } break; #endregion #region # case 2: // --- unerlaubte Zweier-Kombinationen der Boxen ermitteln --- case 2: { Dictionary<int, bool> spielerFertig = kannFelder.ToDictionary(p => p, p => false); MapReset(true); for (int p1 = 0; p1 < kannFelder.Length; p1++) { int boxPos1 = kannFelder[p1]; feldData[boxPos1] ^= 2; feldZeichner.Zeichne(pictureBox1, feldData); Application.DoEvents(); for (int p2 = p1 + 1; p2 < kannFelder.Length; p2++) { int boxPos2 = kannFelder[p2]; feldData[boxPos2] ^= 2; List<int> spielerGut = new List<int>(); List<int> spielerSchlecht = new List<int>(); foreach (int p in kannFelder) spielerFertig[p] = false; spielerFertig[boxPos1] = true; spielerFertig[boxPos2] = true; foreach (int spielerPos in kannFelder.Where(pos => !spielerFertig[pos])) { #region # // --- Spieler setzen und Hash resetten --- feldData[spielerPos] ^= 8; HashFeld hashFeld = new HashFeld(0, feldData, true); hashIndex.Clear(); hashGro = hashSatzGro; feldData[spielerPos] ^= 8; #endregion #region # // --- prüfen, ob Stellung gelöst werden kann --- bool find = false; for (int hashPos = 0; hashPos < hashGro; hashPos += hashSatzGro) { hashFeld = new HashFeld(hashPos); if (hashFeld.RestWürfel == 0) // machbare Lösung gefunden? { find = true; break; } if (hashGro > hashInfoMax * hashSatzGro) { hashInfoMax *= 2; Array.Resize(ref hashInfo, hashInfoMax + 65536); } if (hashFeld.KannLinks) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeLinks(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannRechts) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeRechts(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannOben) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeOben(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannUnten) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeUnten(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } } #endregion #region # // --- alle zusätzlich erreichbare Spielerpositionen ermitteln --- List<int> alleSpielerPos = new List<int>(); alleSpielerPos.Add(spielerPos); spielerFertig[spielerPos] = true; for (int i = 0; i < alleSpielerPos.Count; i++) { int checkPos = alleSpielerPos[i]; foreach (int ofs in ofss) { if ((feldData[checkPos + ofs] & 3) == 0 && !alleSpielerPos.Any(p => p == checkPos + ofs)) { alleSpielerPos.Add(checkPos + ofs); if (erlaubteBoxFelder[checkPos + ofs]) spielerFertig[checkPos + ofs] = true; } } } #endregion if (find) { spielerGut.AddRange(alleSpielerPos); } else { spielerSchlecht.AddRange(alleSpielerPos); } } if (spielerGut.Count == 0 || spielerSchlecht.Count > 0) { #region # // --- Box 1 -> Box 2 --- List<short> dazu = new List<short>(); dazu.Add((short)boxPos2); if (spielerGut.Count < spielerSchlecht.Count) // nur die guten Felder merken? { dazu.Add((short)spielerGut.Count); dazu.AddRange(spielerGut.Select(pos => (short)pos)); } else // nur die schlechten Felder merken (da weniger) { dazu.Add((short)-spielerSchlecht.Count); dazu.AddRange(spielerSchlecht.Select(pos => (short)pos)); } FehlerDataUpdate(ref fehlerBox2[boxPos1], dazu); fehlerVorhanden[boxPos1] |= 1; #endregion #region # // --- Box 2 -> Box 1 --- dazu.Clear(); dazu.Add((short)boxPos1); if (spielerGut.Count < spielerSchlecht.Count) // nur die guten Felder merken? { dazu.Add((short)spielerGut.Count); dazu.AddRange(spielerGut.Select(pos => (short)pos)); } else // nur die schlechten Felder merken (da weniger) { dazu.Add((short)-spielerSchlecht.Count); dazu.AddRange(spielerSchlecht.Select(pos => (short)pos)); } FehlerDataUpdate(ref fehlerBox2[boxPos2], dazu); fehlerVorhanden[boxPos2] |= 1; #endregion } feldData[boxPos2] ^= 2; } feldData[boxPos1] ^= 2; } } break; #endregion #region # case 3: // --- unerlaubte Dreier-Kombinationen der Boxen ermitteln --- case 3: { Dictionary<int, bool> spielerFertig = kannFelder.ToDictionary(p => p, p => false); MapReset(true); for (int p1 = 0; p1 < kannFelder.Length; p1++) { int boxPos1 = kannFelder[p1]; int boxPos1x = boxPos1 % feldBreite; int boxPos1y = boxPos1 / feldBreite; feldData[boxPos1] ^= 2; for (int p2 = p1 + 1; p2 < kannFelder.Length; p2++) { int boxPos2 = kannFelder[p2]; if (FehlerCheckDirekt(boxPos2)) continue; int boxPos2x = boxPos2 % feldBreite; int boxPos2y = boxPos2 / feldBreite; if (Math.Abs(boxPos2x - boxPos1x) > 2) continue; if (Math.Abs(boxPos2y - boxPos1y) > 2) continue; feldData[boxPos2] ^= 2; feldZeichner.Zeichne(pictureBox1, feldData); Application.DoEvents(); for (int p3 = p2 + 1; p3 < kannFelder.Length; p3++) { int boxPos3 = kannFelder[p3]; if (FehlerCheckDirekt(boxPos3)) continue; int boxPos3x = boxPos3 % feldBreite; int boxPos3y = boxPos3 / feldBreite; if (Math.Abs(boxPos3x - boxPos2x) > 2 && Math.Abs(boxPos3x - boxPos1x) > 2) continue; if (Math.Abs(boxPos3y - boxPos2y) > 2 && Math.Abs(boxPos3y - boxPos1y) > 2) continue; feldData[boxPos3] ^= 2; List<int> spielerGut = new List<int>(); List<int> spielerSchlecht = new List<int>(); foreach (int p in kannFelder) spielerFertig[p] = false; spielerFertig[boxPos1] = true; spielerFertig[boxPos2] = true; spielerFertig[boxPos3] = true; foreach (int spielerPos in kannFelder.Where(pos => !spielerFertig[pos])) { #region # // --- Spieler setzen und Hash resetten --- feldData[spielerPos] ^= 8; HashFeld hashFeld = new HashFeld(0, feldData, true); hashIndex.Clear(); hashGro = hashSatzGro; feldData[spielerPos] ^= 8; #endregion #region # // --- prüfen, ob Stellung gelöst werden kann --- bool find = false; for (int hashPos = 0; hashPos < hashGro; hashPos += hashSatzGro) { hashFeld = new HashFeld(hashPos); if (hashFeld.RestWürfel == 0) // machbare Lösung gefunden? { find = true; break; } if (hashGro > hashInfoMax * hashSatzGro) { hashInfoMax *= 2; Array.Resize(ref hashInfo, hashInfoMax + 65536); } if (hashFeld.KannLinks) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeLinks(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannRechts) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeRechts(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannOben) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeOben(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (hashFeld.KannUnten) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(hashPos, hashFeld.BewegeUnten(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } } #endregion #region # // --- alle zusätzlich erreichbare Spielerpositionen ermitteln --- List<int> alleSpielerPos = new List<int>(); alleSpielerPos.Add(spielerPos); spielerFertig[spielerPos] = true; for (int i = 0; i < alleSpielerPos.Count; i++) { int checkPos = alleSpielerPos[i]; foreach (int ofs in ofss) { if ((feldData[checkPos + ofs] & 3) == 0 && !alleSpielerPos.Any(p => p == checkPos + ofs)) { alleSpielerPos.Add(checkPos + ofs); if (erlaubteBoxFelder[checkPos + ofs]) spielerFertig[checkPos + ofs] = true; } } } #endregion if (find) { spielerGut.AddRange(alleSpielerPos); } else { spielerSchlecht.AddRange(alleSpielerPos); } } if (spielerGut.Count == 0 || spielerSchlecht.Count > 0) { #region # // --- Box 1 -> Box 2 + 3 --- List<short> dazu = new List<short>(); dazu.Add((short)boxPos2); dazu.Add((short)boxPos3); if (spielerGut.Count < spielerSchlecht.Count) // nur die guten Felder merken? { dazu.Add((short)spielerGut.Count); dazu.AddRange(spielerGut.Select(pos => (short)pos)); } else // nur die schlechten Felder merken (da weniger) { dazu.Add((short)-spielerSchlecht.Count); dazu.AddRange(spielerSchlecht.Select(pos => (short)pos)); } FehlerDataUpdate(ref fehlerBox3[boxPos1], dazu); fehlerVorhanden[boxPos1] |= 1; #endregion #region # // --- Box 2 -> Box 1 + 3 --- dazu.Clear(); dazu.Add((short)boxPos1); dazu.Add((short)boxPos3); if (spielerGut.Count < spielerSchlecht.Count) // nur die guten Felder merken? { dazu.Add((short)spielerGut.Count); dazu.AddRange(spielerGut.Select(pos => (short)pos)); } else // nur die schlechten Felder merken (da weniger) { dazu.Add((short)-spielerSchlecht.Count); dazu.AddRange(spielerSchlecht.Select(pos => (short)pos)); } FehlerDataUpdate(ref fehlerBox3[boxPos2], dazu); fehlerVorhanden[boxPos2] |= 1; #endregion #region # // --- Box 3 -> Box 1 + 2 --- dazu.Clear(); dazu.Add((short)boxPos1); dazu.Add((short)boxPos2); if (spielerGut.Count < spielerSchlecht.Count) // nur die guten Felder merken? { dazu.Add((short)spielerGut.Count); dazu.AddRange(spielerGut.Select(pos => (short)pos)); } else // nur die schlechten Felder merken (da weniger) { dazu.Add((short)-spielerSchlecht.Count); dazu.AddRange(spielerSchlecht.Select(pos => (short)pos)); } FehlerDataUpdate(ref fehlerBox3[boxPos3], dazu); fehlerVorhanden[boxPos3] |= 1; #endregion } feldData[boxPos3] ^= 2; } feldData[boxPos2] ^= 2; } feldData[boxPos1] ^= 2; } } break; #endregion default: throw new NotSupportedException(); } }
/// <summary> /// Konstruktor zum erstellen eines neuen Hash-Eintrages /// </summary> /// <param name="hashPos">Hashposition im Hash-Array</param> /// <param name="feldData">Daten des Feldes</param> public HashFeld(int hashPos, int[] feldData) { this = new HashFeld(hashPos, feldData, false); }
/// <summary> /// berechnet einen oder mehrere Schritte /// </summary> /// <param name="limit">maximal zu berechnende Schritte</param> void Tick(int limit) { zurückButton.Enabled = false; vorButton.Enabled = false; for (int lim = 0; lim < limit; lim++) { do { if (merkHashPos == hashGro) { tickButton.Text = "keine weiteren Knoten bekannt"; return; } if (hashGro > hashInfoMax * hashSatzGro) { hashInfoMax *= 2; Array.Resize(ref hashInfo, hashInfoMax + 65536); } HashFeld h = new HashFeld(merkHashPos); if (h.RestWürfel == 0) { feldZeichner.Zeichne(pictureBox1, new HashFeld(merkHashPos)); tickButton.Text = "Ziel gefunden! (" + hashIndex.Count.ToString("#,##0") + " Knoten)"; int pos = merkHashPos; List<int> posListe = new List<int>(); posListe.Add(pos); while (pos > 0) { pos = hashInfo[pos / hashSatzGro].vorgänger; posListe.Add(pos); } posListe.Reverse(); viewListe = posListe.ToArray(); viewPos = 0; zurückButton.Enabled = false; vorButton.Enabled = true; return; } if (h.KannLinks) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(merkHashPos, h.BewegeLinks(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (h.KannRechts) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(merkHashPos, h.BewegeRechts(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (h.KannOben) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(merkHashPos, h.BewegeOben(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } if (h.KannUnten) { HashFeld neuHash = new HashFeld(hashGro); hashInfo[hashGro / hashSatzGro] = new HashInfo(merkHashPos, h.BewegeUnten(neuHash)); if (!HashBekannt(neuHash)) hashGro += hashSatzGro; } merkHashPos += hashSatzGro; } while (merkHashPos < hashGro && !hashInfo[merkHashPos / hashSatzGro].verschoben); if (merkHashPos < hashGro) { if (lim + 1 == limit) { feldZeichner.Zeichne(pictureBox1, new HashFeld(merkHashPos)); int schritte = OptiErmitteleTiefe(merkHashPos / hashSatzGro); tickButton.Text = (hashGro / hashSatzGro).ToString("#,##0") + " Felder (" + ((double)hashGro / 1048576.0).ToString("#,##0.0") + " MB) [" + schritte + "]"; tickButton.Update(); } } else { tickButton.Text = "keine weiteren Knoten bekannt"; return; } } }
/// <summary> /// gibt an, ob der Hashknoten schon bekannt ist /// </summary> /// <param name="suchHash">Hash nach dem gesucht werden soll</param> /// <returns>true, wenn der Hashknoten schon bekannt ist</returns> bool HashBekannt(HashFeld suchHash) { ulong suchCrc = suchHash.GetCrc64(); bool find; if (hashIndex.TryGetValue(suchCrc, out find)) return true; hashIndex.Add(suchCrc, true); return false; }
public void Zeichne(PictureBox pictureBox, HashFeld hashFeld) { Zeichne(pictureBox, Enumerable.Range(hashFeld.hashPos, hashSatzGro).Select(i => (int)hashData[i]).ToArray()); }
private void button1_Click(object sender, EventArgs e) { button1.Text = "scan"; button1.Update(); pictureBox1.Image = null; pictureBox1.Update(); ScanFelder(2 + 16, 26 + 32); if (feldData != null) { #region # // --- Feld auf das nötigste verkleinern --- // --- obere leere Zeilen entfernen --- for (int c = 0; ; ) { for (int i = 0; i < feldBreite; i++) if (feldData[i] == 0) c++; if (c < feldBreite) break; for (int i = feldBreite; i < feldData.Length; i++) feldData[i - feldBreite] = feldData[i]; feldHöhe--; Array.Resize(ref feldData, feldData.Length - feldBreite); c = 0; } // --- untere leere Zeilen entfernen --- for (int c = 0; ; ) { for (int i = 0; i < feldBreite; i++) if (feldData[i + (feldHöhe - 1) * feldBreite] == 0) c++; if (c < feldBreite) break; feldHöhe--; Array.Resize(ref feldData, feldData.Length - feldBreite); c = 0; } // --- linke leere Spalten entfernen --- for (int c = 0; ; ) { for (int i = 0; i < feldHöhe; i++) if (feldData[i * feldBreite] == 0) c++; if (c < feldHöhe) break; for (int y = 0; y < feldHöhe; y++) for (int x = 1; x < feldBreite; x++) feldData[(x - 1) + y * (feldBreite - 1)] = feldData[x + y * feldBreite]; feldBreite--; Array.Resize(ref feldData, feldData.Length - feldHöhe); c = 0; } // --- rechte leere Spalten entfernen --- for (int c = 0; ; ) { for (int i = 0; i < feldHöhe; i++) if (feldData[i * feldBreite + feldBreite - 1] == 0) c++; if (c < feldHöhe) break; for (int y = 1; y < feldHöhe; y++) for (int x = 0; x < feldBreite - 1; x++) feldData[x + y * (feldBreite - 1)] = feldData[x + y * feldBreite]; feldBreite--; Array.Resize(ref feldData, feldData.Length - feldHöhe); c = 0; } #endregion feldAnzahl = feldBreite * feldHöhe; hashSatzGro = feldAnzahl + 1 + 1 + 1; // Anzahl + SpielerL + SpielerH + Restwürfel button1.Text = "rechne..."; button1.Update(); ErstelleErlaubteBoxFelder((new HashFeld(0, feldData)).SpielerPos); int[] merkFelder = new int[feldAnzahl]; Array.Copy(feldData, merkFelder, feldAnzahl); FehlerMapErstellen(1); FehlerMapErstellen(2); FehlerMapErstellen(3); MapReset(true); hashGro = hashSatzGro; Array.Copy(merkFelder, feldData, feldAnzahl); HashFeld h = new HashFeld(0, feldData); feldZeichner.Zeichne(pictureBox1, h); merkHashPos = 0; hashInfo[0].verschoben = true; hashInfo[0].vorgänger = 0; zurückButton.Enabled = false; vorButton.Enabled = false; hashIndex.Clear(); } button1.Text = "ok"; }