comments added
[schiebespiel.git] / Knoten.java
blob17dc4aa4cb66ca81db36b8136a2de261b6523115
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 private static int HeuristikTyp = 0;
37 private static Knoten ziel;
38 private static Knoten start;
40 /**
41 * Setzt den Typ der Heuristik, nach der Knoten bewertet werden sollen. Die
42 * Standardheuristik ist 0 (=HEURISTIK_SIMPEL)
44 public static void SetHeuristikTyp(int typ) {
45 HeuristikTyp = typ;
48 /**
49 * Gibt eine Kopie des aktuellen Spielfeldzustands zurück.
50 * @return Spielfeldkopie
52 private int[][] getSpielfeldKopie() {
53 int[][] neu = new int[5][4];
54 for (int i=0; i<5; ++i) {
55 System.arraycopy( spielfeld[i], 0, neu[i], 0, 4 );
57 return neu;
60 /**
61 * Überprüft, ob der this Knoten mit dem Knoten k übereinstimmt.
62 * @param k Knoten mit dem der this Knoten verglichen wird.
64 public boolean is_clone(Knoten k) {
65 for (int x = 0; x < spielfeld.length; ++x) {
66 for (int y = 0; y < spielfeld[0].length; ++y) {
67 if(this.get_field(x,y) != k.get_field(x,y)) {
68 return false;
72 return true;
75 /**
76 * Fügt den Knoten einer Liste von Knoten liste hinzu. Falls ein Klon
77 * dieses Knotens bereits in der Liste existiert, wird der Knoten mit der
78 * geringeren Tiefe gelöscht.
79 * @param liste Eine LinkedList von Knoten, der der this Knoten hinzugefügt
80 * wird.
82 public void add_dublikatfrei(LinkedList<Knoten> liste) {
83 for (Knoten k: liste) {
84 if (is_clone(k)) {
85 if (k.get_tiefe() <= get_tiefe()) {
86 return;
87 } else {
88 liste.remove(k);
89 liste.add(this);
90 return;
94 liste.add(this);
97 /**
98 * Überprüft, ob wir ein Klon eines unserer Elterknotens sind.
99 * @return Wahrheitswert
101 public boolean is_reinkarnation() {
102 Knoten k = this;
103 while (k.get_parent() != null) {
104 k = k.get_parent();
105 boolean equal=true;
106 for (int x = 0; x < spielfeld.length; ++x) {
107 for (int y = 0; y < spielfeld[0].length; ++y) {
108 if(this.get_field(x,y) != k.get_field(x,y)) {
109 equal = false;
113 if(equal) {
114 return true;
117 return false;
121 * Berechnet eine Heuristik zu dem Knoten, abhängig davon, wie der
122 * derzeitige Heuristik Typ gesetzt wurde.
123 * @return die Bewertung
125 private int compute_heuristik() {
126 switch(HeuristikTyp) {
127 case HEURISTIK_SIMPEL:
128 return heuristik_simpel();
130 /* Default: */
131 return heuristik_simpel();
135 * Eine sehr einfache Heuristik, die einen Knoten mit 1 bewertet, falls er
136 * vom Zielknoten abweicht, und andernfalls mit 0 bewertet. Ein A*
137 * Algorithmus würde somit zu einer Breitensuche degenerieren.
139 private int heuristik_simpel() {
140 for(int x = 0; x < spielfeld.length; ++x) {
141 for(int y = 0; y < spielfeld[0].length; ++y) {
142 if(spielfeld[x][y] != ziel.get_field(x,y)) {
143 return 1;
147 return 0;
151 * Setzt den Zielzustand.
153 public static void set_ziel(Knoten ziel) {
154 Knoten.ziel = ziel;
158 * Setzt den Startzustand.
160 public static void set_start(Knoten start) {
161 Knoten.start = start;
166 * Erstellt einen leeren Knoten. Dies ist nur für Start- und Endknoten von
167 * Interesse.
169 public Knoten() {
170 this.parent = null;
171 this.heuristik = 0;
172 this.tiefe = 0;
175 /** Erstellt einen Knoten aus einem Elterknoten nach folgendem Verfahren.
176 * Das Array bfield wird über das Spielfeld um die in offset gegebenen
177 * Koordinaten verschoben, und die "darunterliegenden" Werte des
178 * Elterknotens mit denen aus bfield überschrieben.
179 * @param parent Der Elterknoten
180 * @param offset Offsetkoordinaten für bfield (s.Beschreibung)
181 * @param bfield Die zu ersetzenden Koordinaten
183 public Knoten(Knoten parent, int[] offset, int[][] bfield) {
184 this.parent = parent;
185 this.tiefe = parent.get_tiefe() + 1;
186 this.spielfeld = parent.getSpielfeldKopie();
187 for(int x = 0; x < bfield.length; ++x) {
188 for(int y = 0; y < bfield[0].length; ++y) {
189 this.spielfeld[offset[0] + x][offset[1] + y] =
190 bfield[x][y];
193 int count=0;
194 for(int x = 0; x < spielfeld.length; ++x) {
195 for(int y = 0; y < spielfeld[0].length; ++y) {
196 if(spielfeld[x][y] == leer) {
197 set_leere(count, x, y);
198 count++;
201 if(count == 2) {
202 set_leere(2,-1,-1);
203 set_leere(3,-1,-1);
206 this.heuristik = compute_heuristik();
209 /**
210 * Vergleicht die Heuristik mit der Heuristik des Knotens k
212 public int compareTo(Knoten k) {
213 return new Integer(this.heuristik).compareTo(new
214 Integer(k.heuristik));
217 Knoten parent;
218 int[][] spielfeld = new int[5][4];
219 int heuristik;
220 int tiefe;
221 int[][] leere = new int[4][2];
224 * Gibt die Tiefe des Knotens im Baum zurück.
226 int get_tiefe() {
227 return tiefe;
231 * Gibt den Elterknoten zurück.
233 Knoten get_parent() {
234 return parent;
238 * Gibt die Stein-ID des Spielfelds an der Stelle (x,y) zurück.
240 int get_field(int x, int y) {
241 return spielfeld[x][y];
245 * Gibt die Koordinaten des leeren Feldes mit dem Index index zurück. (Die
246 * leeren Felder sind willkürlich durchnummeriert.)
247 * @param index Index des leeren Feldes, welches zurückgegeben werden soll.
248 * (0-3)
250 int[] get_leere(int index) {
251 return leere[index];
254 void set_field(int x, int y, int value) {
255 spielfeld[x][y] = value;
258 void set_leere(int index, int x, int y) {
259 leere[index][0] = x;
260 leere[index][1] = y;
264 * Gibt das Spielfeld in einer menschen-lesbaren Form aus.
266 public String toString() {
267 String ret = new String();
268 for (int y = spielfeld[0].length - 1; y >= 0; --y) {
269 for (int x = 0; x < spielfeld.length; ++x) {
270 ret += "" + get_field(x, y) + " ";
272 ret+="\n";
274 return ret;
278 class Koordinate
280 private Transitions trs = new Transitions();
281 private Knoten start;
282 private Knoten knoten;
283 private LinkedList<Knoten> elter= new LinkedList<Knoten>();
284 private LinkedList<Integer> koordinaten = new
285 LinkedList<Integer>();
286 private LinkedList<Knoten> tempList = new LinkedList<Knoten>();
287 public Koordinate(Knoten start)
289 this.start = start;
290 this.knoten = start;
291 this.elter.add(start);
292 koordinaten.clear();
295 public void succ()
297 int temp=0;
298 int tiefe=0;
299 if (koordinaten.size()==0)
300 // Koordiante neu erstellt, wir befinden uns an der Wurzel
302 koordinaten.add(0);
303 tempList = trs.expandKnoten(elter.getLast());
304 knoten = tempList.get(0);
305 return;
307 if (koordinaten.getLast() < tempList.size()-1)
308 //Es gibt auf dieser Ebene noch weitere Knoten zu denen verzweigt werden kann
310 temp=koordinaten.removeLast();
311 koordinaten.addLast(temp+1);
312 knoten=tempList.get(koordinaten.getLast());
314 else
315 //Ebene zu Ende, eins zurück, erhöhen und wieder vor
317 tiefe=koordinaten.size();
318 //solang zurückgehen wie es keine weiteren kannten am elterknoten gibt
319 while(koordinaten.getLast() >= tempList.size()-1)
321 koordinaten.removeLast();
322 knoten=elter.removeLast();
323 tempList=trs.expandKnoten(knoten);
325 // hier fehlt noch, wenn man wieder an der wurzel ist muss die koordinatenlänge(tiefe) um eins erhöht werden und dann halt koord= 0,0,0,0...
326 if (koordinaten.size()==0)
330 else
332 //dieser elter hat noch unerreichte kinder, koordinate um eins erhöhen
333 temp=koordinaten.removeLast();
334 koordinaten.addLast(temp+1);
335 //wieder runtergehen jeweils den linkesten ast nehmend
336 while (koordinaten.size()<tiefe)
338 koordinaten.add(0);
339 elter.add(knoten);
340 knoten=tempList.get(koordinaten.getLast());
349 public Knoten getKnoten()
351 return knoten;