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 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
;
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
);
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 );
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
)) {
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
88 public void add_dublikatfrei(LinkedList
<Knoten
> liste
) {
89 for (Knoten k
: liste
) {
91 if (k
.get_tiefe() <= get_tiefe()) {
104 * Überprüft, ob wir ein Klon eines unserer Elterknotens sind.
105 * @return Wahrheitswert
107 public boolean is_reinkarnation() {
109 while (k
.get_parent() != null) {
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
)) {
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();
138 this.heuristik
= heuristik_luft();
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
)) {
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() {
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
)) {
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()));
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() {
192 * Setzt den Zielzustand.
194 public static void set_ziel(Knoten 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
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
] =
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
);
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
));
259 int[][] spielfeld
= new int[5][4];
262 int[][] leere
= new int[4][2];
265 * Gibt die Tiefe des Knotens im Baum zurück.
272 * Gibt den Heuristikwert des Knotens zurück.
274 int get_heuristik() {
279 * Gibt den Elterknoten zurück.
281 Knoten
get_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.
298 int[] get_leere(int 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
) {
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
) + " ";