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.
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
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
;
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
) {
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 );
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
)) {
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
82 public void add_dublikatfrei(LinkedList
<Knoten
> liste
) {
83 for (Knoten k
: liste
) {
85 if (k
.get_tiefe() <= get_tiefe()) {
98 * Überprüft, ob wir ein Klon eines unserer Elterknotens sind.
99 * @return Wahrheitswert
101 public boolean is_reinkarnation() {
103 while (k
.get_parent() != null) {
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
)) {
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();
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
)) {
151 * Setzt den Zielzustand.
153 public static void set_ziel(Knoten 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
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
] =
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
);
206 this.heuristik
= compute_heuristik();
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
));
218 int[][] spielfeld
= new int[5][4];
221 int[][] leere
= new int[4][2];
224 * Gibt die Tiefe des Knotens im Baum zurück.
231 * Gibt den Elterknoten zurück.
233 Knoten
get_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.
250 int[] get_leere(int 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
) {
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
) + " ";
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
)
291 this.elter
.add(start
);
299 if (koordinaten
.size()==0)
300 // Koordiante neu erstellt, wir befinden uns an der Wurzel
303 tempList
= trs
.expandKnoten(elter
.getLast());
304 knoten
= tempList
.get(0);
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());
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)
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
)
340 knoten
=tempList
.get(koordinaten
.getLast());
349 public Knoten
getKnoten()