- schiebe4 hinzugefügt
[schiebespiel.git] / Knoten.java
bloba655739fa9797188a1c1a775a3d655e175789b8b
1 /* Schiebespiel - effectively computes solution for a one player board game
2 * Copyright (C) 2007 Dirk Ribbrock / 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), 2(HEURISTIK_LUFT)
47 public static void setHeuristikTyp(int typ) {
48 if(typ == HEURISTIK_LUFT) {
49 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 public void computeHeuristik() {
132 switch(HeuristikTyp) {
133 case HEURISTIK_SIMPEL:
134 this.heuristik = heuristik_simpel();
135 break;
136 case HEURISTIK_DIFFERENZ:
137 this.heuristik = heuristik_differenz();
138 break;
139 case HEURISTIK_LUFT:
140 this.heuristik = heuristik_luft();
141 break;
142 default:
143 /* Default: */
144 this.heuristik = heuristik_simpel();
145 break;
149 *Prüft ob der Knoten gleich dem Knoten k ist
151 public boolean is(Knoten k)
153 for(int x = 0; x < spielfeld.length; ++x) {
154 for(int y = 0; y < spielfeld[0].length; ++y) {
155 if(spielfeld[x][y] != k.get_field(x,y)) {
156 return false;
160 return true;
165 * Eine sehr einfache Heuristik, die einen Knoten mit 1 bewertet, falls er
166 * vom Zielknoten abweicht, und andernfalls mit 0 bewertet. Ein A*
167 * Algorithmus würde somit zu einer Breitensuche degenerieren.
169 private int heuristik_simpel() {
170 for(int x = 0; x < spielfeld.length; ++x) {
171 for(int y = 0; y < spielfeld[0].length; ++y) {
172 if(spielfeld[x][y] != ziel.get_field(x,y)) {
173 return 1;
177 return 0;
181 * Eine einfache Heuristik, die einen Knoten mit der Anzahl der abweichenden Zellen bewertet, und andernfalls mit 0 bewertet. A* ist also hiermit sehr zielgerichtet
183 private int heuristik_differenz() {
184 int differenzen=0;
185 for(int x = 0; x < spielfeld.length; ++x) {
186 for(int y = 0; y < spielfeld[0].length; ++y) {
187 if(spielfeld[x][y] != ziel.get_field(x,y)) {
188 differenzen++;
192 if (differenzen==0) return 0; // knoten sind identisch
193 if (this.get_tiefe()>18) return 100; // wir sind zu tief
194 //mit der tiefe des knotens verrechnen
195 //differenzen = (int)(differenzen + (float)(10/this.get_tiefe()));
197 return differenzen;
201 * Eine einfache Heuristik, die einen Knoten mit der Anzahl notwendigen Schritte in Luftlinie bewertet
203 private int heuristik_luft() {
204 return get_tiefe() + ms.computeLuftlinie(this);
207 public int[][] get_spielfeld() {
208 return spielfeld;
211 public static Knoten getZiel()
213 return Knoten.ziel;
216 public static Knoten getStart()
218 return Knoten.start;
222 * Setzt den Zielzustand.
224 public static void set_ziel(Knoten ziel) {
225 Knoten.ziel = ziel;
229 * Setzt den Startzustand.
231 public static void set_start(Knoten start) {
232 Knoten.start = start;
237 * Erstellt einen leeren Knoten. Dies ist nur für Start- und Endknoten von
238 * Interesse.
240 public Knoten() {
241 this.parent = null;
242 this.heuristik = 0;
243 this.tiefe = 0;
246 /** Erstellt einen Knoten aus einem Elterknoten nach folgendem Verfahren.
247 * Das Array bfield wird über das Spielfeld um die in offset gegebenen
248 * Koordinaten verschoben, und die "darunterliegenden" Werte des
249 * Elterknotens mit denen aus bfield überschrieben.
250 * @param parent Der Elterknoten
251 * @param offset Offsetkoordinaten für bfield (s.Beschreibung)
252 * @param bfield Die zu ersetzenden Koordinaten
254 public Knoten(Knoten parent, int[] offset, int[][] bfield) {
255 this.parent = parent;
256 this.tiefe = parent.get_tiefe() + 1;
257 this.spielfeld = parent.getSpielfeldKopie();
258 for(int x = 0; x < bfield.length; ++x) {
259 for(int y = 0; y < bfield[0].length; ++y) {
260 this.spielfeld[offset[0] + x][offset[1] + y] =
261 bfield[x][y];
264 int count=0;
265 for(int x = 0; x < spielfeld.length; ++x) {
266 for(int y = 0; y < spielfeld[0].length; ++y) {
267 if(spielfeld[x][y] == leer) {
268 set_leere(count, x, y);
269 count++;
272 if(count == 2) {
273 set_leere(2,-1,-1);
274 set_leere(3,-1,-1);
277 computeHeuristik();
280 /**
281 * Vergleicht die Heuristik mit der Heuristik des Knotens k
283 public int compareTo(Knoten k) {
284 return new Integer(this.heuristik).compareTo(new
285 Integer(k.heuristik));
288 Knoten parent;
289 int[][] spielfeld = new int[5][4];
290 int heuristik;
291 int tiefe;
292 int[][] leere = new int[4][2];
295 * Gibt die Tiefe des Knotens im Baum zurück.
297 int get_tiefe() {
298 return tiefe;
302 * Gibt den Heuristikwert des Knotens zurück.
304 int get_heuristik() {
305 return heuristik;
309 * Gibt den Elterknoten zurück.
311 Knoten get_parent() {
312 return parent;
316 * Gibt die Stein-ID des Spielfelds an der Stelle (x,y) zurück.
318 int get_field(int x, int y) {
319 return spielfeld[x][y];
323 * Gibt die Koordinaten des leeren Feldes mit dem Index index zurück. (Die
324 * leeren Felder sind willkürlich durchnummeriert.)
325 * @param index Index des leeren Feldes, welches zurückgegeben werden soll.
326 * (0-3)
328 int[] get_leere(int index) {
329 return leere[index];
332 void set_field(int x, int y, int value) {
333 spielfeld[x][y] = value;
336 void set_leere(int index, int x, int y) {
337 leere[index][0] = x;
338 leere[index][1] = y;
342 * Gibt das Spielfeld in einer menschen-lesbaren Form aus.
344 public String toString() {
345 String ret = new String();
346 for (int y = spielfeld[0].length - 1; y >= 0; --y) {
347 for (int x = 0; x < spielfeld.length; ++x) {
348 ret += "" + get_field(x, y) + " ";
350 ret+="\n";
352 return ret;