Nun ist die Luftlinie auch in der Knoten Klasse integriert.
[schiebespiel.git] / Knoten.java
blob8cfb78102d4491ef055112e04d236a0515457c4b
1 /* Schiebespiel - effectively computes solution for a one player board game
2 * Copyright (C) 2007 Dirk Ribbroch / Martin Drohmann
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 import java.util.*;
20 /**
21 * Knoten repräsentiert einen Zustand des Spielfeldes.
23 public class Knoten implements Comparable<Knoten> {
24 public static final int leer=0;
25 public static final int einer=1;
26 public static final int zweier_rechts=2;
27 public static final int zweier_links=3;
28 public static final int zweier_oben=4;
29 public static final int zweier_unten=5;
30 public static final int vierer_ro=6;
31 public static final int vierer_ru=7;
32 public static final int vierer_lo=8;
33 public static final int vierer_lu=9;
34 public static final int nicht_leer=21;
35 public static final int HEURISTIK_SIMPEL=0;
36 public static final int HEURISTIK_DIFFERENZ=1;
37 public static final int HEURISTIK_LUFT=2;
38 private static int HeuristikTyp = 0;
39 private static Knoten ziel;
40 private static Knoten start;
41 private static MinSchritte ms;
43 /**
44 * Setzt den Typ der Heuristik, nach der Knoten bewertet werden sollen. Die
45 * Standardheuristik ist 0 (=HEURISTIK_SIMPEL), 1(HEURISTIK_DIFFERENZ)
47 public static void setHeuristikTyp(int typ) {
48 if(typ == HEURISTIK_LUFT) {
49 MinSchritte ms = new MinSchritte(ziel);
51 HeuristikTyp = typ;
54 /**
55 * Gibt eine Kopie des aktuellen Spielfeldzustands zurück.
56 * @return Spielfeldkopie
58 private int[][] getSpielfeldKopie() {
59 int[][] neu = new int[5][4];
60 for (int i=0; i<5; ++i) {
61 System.arraycopy( spielfeld[i], 0, neu[i], 0, 4 );
63 return neu;
66 /**
67 * Überprüft, ob der this Knoten mit dem Knoten k übereinstimmt.
68 * @param k Knoten mit dem der this Knoten verglichen wird.
70 public boolean is_clone(Knoten k) {
71 for (int x = 0; x < spielfeld.length; ++x) {
72 for (int y = 0; y < spielfeld[0].length; ++y) {
73 if(this.get_field(x,y) != k.get_field(x,y)) {
74 return false;
78 return true;
81 /**
82 * Fügt den Knoten einer Liste von Knoten liste hinzu. Falls ein Klon
83 * dieses Knotens bereits in der Liste existiert, wird der Knoten mit der
84 * geringeren Tiefe gelöscht.
85 * @param liste Eine LinkedList von Knoten, der der this Knoten hinzugefügt
86 * wird.
88 public void add_dublikatfrei(LinkedList<Knoten> liste) {
89 for (Knoten k: liste) {
90 if (is_clone(k)) {
91 if (k.get_tiefe() <= get_tiefe()) {
92 return;
93 } else {
94 liste.remove(k);
95 liste.add(this);
96 return;
100 liste.add(this);
104 * Überprüft, ob wir ein Klon eines unserer Elterknotens sind.
105 * @return Wahrheitswert
107 public boolean is_reinkarnation() {
108 Knoten k = this;
109 while (k.get_parent() != null) {
110 k = k.get_parent();
111 boolean equal=true;
112 for (int x = 0; x < spielfeld.length; ++x) {
113 for (int y = 0; y < spielfeld[0].length; ++y) {
114 if(this.get_field(x,y) != k.get_field(x,y)) {
115 equal = false;
119 if(equal) {
120 return true;
123 return false;
127 * Berechnet eine Heuristik zu dem Knoten, abhängig davon, wie der
128 * derzeitige Heuristik Typ gesetzt wurde.
129 * @return die Bewertung
131 private void computeHeuristik() {
132 switch(HeuristikTyp) {
133 case HEURISTIK_SIMPEL:
134 this.heuristik = heuristik_simpel();
135 case HEURISTIK_DIFFERENZ:
136 this.heuristik = heuristik_differenz();
137 case HEURISTIK_LUFT:
138 this.heuristik = heuristik_luft();
140 /* Default: */
141 this.heuristik = heuristik_simpel();
145 * Eine sehr einfache Heuristik, die einen Knoten mit 1 bewertet, falls er
146 * vom Zielknoten abweicht, und andernfalls mit 0 bewertet. Ein A*
147 * Algorithmus würde somit zu einer Breitensuche degenerieren.
149 private int heuristik_simpel() {
150 for(int x = 0; x < spielfeld.length; ++x) {
151 for(int y = 0; y < spielfeld[0].length; ++y) {
152 if(spielfeld[x][y] != ziel.get_field(x,y)) {
153 return 1;
157 return 0;
161 * Eine einfache Heuristik, die einen Knoten mit der Anzahl der abweichenden Zellen bewertet, und andernfalls mit 0 bewertet. A* ist also hiermit sehr zielgerichtet
163 private int heuristik_differenz() {
164 int differenzen=0;
165 for(int x = 0; x < spielfeld.length; ++x) {
166 for(int y = 0; y < spielfeld[0].length; ++y) {
167 if(spielfeld[x][y] != ziel.get_field(x,y)) {
168 differenzen++;
172 if (differenzen==0) return 0; // knoten sind identisch
173 if (this.get_tiefe()>18) return 100; // wir sind zu tief
174 //mit der tiefe des knotens verrechnen
175 //differenzen = (int)(differenzen + (float)(10/this.get_tiefe()));
177 return differenzen;
181 * Eine einfache Heuristik, die einen Knoten mit der Anzahl notwendigen Schritte in Luftlinie bewertet
183 private int heuristik_luft() {
184 return ms.computeLuftlinie(this);
187 public int[][] get_spielfeld() {
188 return spielfeld;
192 * Setzt den Zielzustand.
194 public static void set_ziel(Knoten ziel) {
195 Knoten.ziel = ziel;
199 * Setzt den Startzustand.
201 public static void set_start(Knoten start) {
202 Knoten.start = start;
207 * Erstellt einen leeren Knoten. Dies ist nur für Start- und Endknoten von
208 * Interesse.
210 public Knoten() {
211 this.parent = null;
212 this.heuristik = 0;
213 this.tiefe = 0;
216 /** Erstellt einen Knoten aus einem Elterknoten nach folgendem Verfahren.
217 * Das Array bfield wird über das Spielfeld um die in offset gegebenen
218 * Koordinaten verschoben, und die "darunterliegenden" Werte des
219 * Elterknotens mit denen aus bfield überschrieben.
220 * @param parent Der Elterknoten
221 * @param offset Offsetkoordinaten für bfield (s.Beschreibung)
222 * @param bfield Die zu ersetzenden Koordinaten
224 public Knoten(Knoten parent, int[] offset, int[][] bfield) {
225 this.parent = parent;
226 this.tiefe = parent.get_tiefe() + 1;
227 this.spielfeld = parent.getSpielfeldKopie();
228 for(int x = 0; x < bfield.length; ++x) {
229 for(int y = 0; y < bfield[0].length; ++y) {
230 this.spielfeld[offset[0] + x][offset[1] + y] =
231 bfield[x][y];
234 int count=0;
235 for(int x = 0; x < spielfeld.length; ++x) {
236 for(int y = 0; y < spielfeld[0].length; ++y) {
237 if(spielfeld[x][y] == leer) {
238 set_leere(count, x, y);
239 count++;
242 if(count == 2) {
243 set_leere(2,-1,-1);
244 set_leere(3,-1,-1);
247 computeHeuristik();
250 /**
251 * Vergleicht die Heuristik mit der Heuristik des Knotens k
253 public int compareTo(Knoten k) {
254 return new Integer(this.heuristik).compareTo(new
255 Integer(k.heuristik));
258 Knoten parent;
259 int[][] spielfeld = new int[5][4];
260 int heuristik;
261 int tiefe;
262 int[][] leere = new int[4][2];
265 * Gibt die Tiefe des Knotens im Baum zurück.
267 int get_tiefe() {
268 return tiefe;
272 * Gibt den Heuristikwert des Knotens zurück.
274 int get_heuristik() {
275 return heuristik;
279 * Gibt den Elterknoten zurück.
281 Knoten get_parent() {
282 return parent;
286 * Gibt die Stein-ID des Spielfelds an der Stelle (x,y) zurück.
288 int get_field(int x, int y) {
289 return spielfeld[x][y];
293 * Gibt die Koordinaten des leeren Feldes mit dem Index index zurück. (Die
294 * leeren Felder sind willkürlich durchnummeriert.)
295 * @param index Index des leeren Feldes, welches zurückgegeben werden soll.
296 * (0-3)
298 int[] get_leere(int index) {
299 return leere[index];
302 void set_field(int x, int y, int value) {
303 spielfeld[x][y] = value;
306 void set_leere(int index, int x, int y) {
307 leere[index][0] = x;
308 leere[index][1] = y;
312 * Gibt das Spielfeld in einer menschen-lesbaren Form aus.
314 public String toString() {
315 String ret = new String();
316 for (int y = spielfeld[0].length - 1; y >= 0; --y) {
317 for (int x = 0; x < spielfeld.length; ++x) {
318 ret += "" + get_field(x, y) + " ";
320 ret+="\n";
322 return ret;