001    package de.hska.java.aufgaben.backtracking;
002    
003    import java.util.Arrays;
004    
005    
006    /**
007     * Das Spielfeld für das Englische Solitär mit 32 Feldern.
008     * Die einzelnen Felder haben "kartesische" Koordinaten von 0 bis 6 in der Breite
009     * und 0 bis 6 in der Höhe.
010     * </p>
011    <table border=1>
012      <tr><td> </td><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td></tr>
013      <tr><td>0</td><td></td><td> </td><td>X</td><td>X</td><td>X</td><td> </td><td> </td></tr>
014      <tr><td>1</td><td> </td><td> </td><td>X</td><td>X</td><td>X</td><td> </td><td> </td></tr>
015      <tr><td>2</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td></tr>
016      <tr><td>3</td><td>X</td><td>X</td><td>X</td><td>O</td><td>X</td><td>X</td><td>X</td></tr>
017      <tr><td>4</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td><td>X</td></tr>
018      <tr><td>5</td><td> </td><td> </td><td>X</td><td>X</td><td>X</td><td> </td><td> </td></tr>
019      <tr><td>6</td><td> </td><td> </td><td>X</td><td>X</td><td>X</td><td> </td><td> </td></tr>
020    </table>
021     * 
022     * @author Christian Pape
023     *
024     */
025    public class SolitaerSpielfeld {
026            
027            private static final int BESETZT = 1;
028            
029            private static final int FREI = 2;
030    
031            private static final int RECHTS = 0;
032            private static final int OBEN = 1;
033            private static final int LINKS = 2;
034            private static final int UNTEN = 3;
035            
036            /**
037             * Enthält die erlaubten Richtungen, in die gesprungen werden darf.
038             * In der Praxis würde man besser Aufzählungstypen (enum) verwenden.
039             */
040            private static int [] richtungen = {RECHTS, OBEN, LINKS, UNTEN};
041            
042            /**
043             * Das Spielfeld mit den Werten 1 = Stein, 2 = Leeres Feld und 0 = nicht benutztes Feld.
044             * Die Startkonfiguration wird als Voreinstellung verwendet.
045             */
046            private int [] [] spielfeld = {
047                            {0, 0, 1, 1, 1, 0, 0},
048                            {0, 0, 1, 1, 1, 0, 0},
049                            {1, 1, 1, 1, 1, 1, 1},
050                            {1, 1, 1, 2, 1, 1, 1},
051                            {1, 1, 1, 1, 1, 1, 1},
052                            {0, 0, 1, 1, 1, 0, 0},
053                            {0, 0, 1, 1, 1, 0, 0}
054            };
055    
056            /**
057             * Die Breite des Spielfelds (sollte immer 7 sein).
058             */
059            public int getBreite() {
060                    return spielfeld.length;
061            }
062    
063            /**
064             * Die Höhe des Spielfelds (sollte immer 7 sein).
065             */
066            public int getHoehe() {
067                    return spielfeld.length;
068            }
069    
070            public void feldFreimachen(int x, int y) {
071                    spielfeld[x][y] = FREI;
072            }
073    
074            public void feldBesetzen(int x, int y) {
075                    spielfeld[x][y] = BESETZT;
076            }
077    
078            /**
079             * Kopiert den Zustand  des Spielfelds <code>vonSpielfeld</code>
080             * in das Spielfeld <code>nachSpielfeld</code>
081             */
082            public void kopiereSpielfeld(SolitaerSpielfeld vonSpielfeld, SolitaerSpielfeld nachSpielfeld) {
083                    for (int x = 0; x < getBreite(); x++) {
084                   for (int y = 0; y < getHoehe(); y++) {
085                               nachSpielfeld.spielfeld[x][y] = vonSpielfeld.spielfeld[x][y];
086                       }
087                    }
088            }
089            
090            /**
091             * Prüft, ob ein Spielstein an Stelle x und y, ein leeres Feld
092             * an Stelle neuesX und neuesY ist und eine Spielfigur dazwischen voranden ist.
093             * Es wird nicht geprüft, ob von x,y korrekt 2 Stellen nach links/rechts oder
094             * oben/unten gesprungen wurde.
095             */
096            private boolean isGueltigerZug(int x, int y, int neuesX, int neuesY) {
097                    return 0 <= x && x < spielfeld.length 
098                            && 0 <= y && y < spielfeld[x].length
099                            && 0 <= neuesX && neuesX < spielfeld.length 
100                            && 0 <= neuesY && neuesY < spielfeld[neuesX].length
101                            && spielfeld[neuesX][neuesY] == FREI
102                            && spielfeld[(x + neuesX) / 2][(y + neuesY) / 2] == BESETZT
103                            && spielfeld[x][y] == BESETZT;
104                            
105            }
106            
107            /**
108             * Nimmt die "Spielfigur" vom Feld (x,y) und spring in die gegeben <code>richtung</code>
109             * zwei Felder weiter in das leere Feld. Die Spielfigur dazwischen wird weggebenommen.
110             * Wenn es sich nicht um einen gütligen Sprung handelt, dann wird false zurückgegeben und
111             * das Spielfeld bleibt unverändert.
112             * 
113             * @return true, wenn es sich um einen gültigen Sprung handelt, ansonsten false.
114             */
115            public boolean springe(int x, int y, int richtung) {
116                    int neuesX = getNeuesX(x, richtung);
117                    int neuesY = getNeuesY(y, richtung);
118    
119                    if ( isGueltigerZug(x, y, neuesX, neuesY)) {
120                            feldBesetzen(neuesX, neuesY);
121                            feldFreimachen(x, y);
122                            // Das mittlere Feld bekommt man einfach als Mittelpunkt
123                            // der beiden kartesischen Koordinate von Start und Zielpunkt des
124                            // Sprunges
125                            feldFreimachen((x + neuesX) / 2, (y + neuesY) / 2);
126                            
127                            return true;
128                    }
129                    
130                    return false;
131            }
132            
133    
134            /**
135             * Macht einen Sprung von (x,y) in die gegeben <code>richtung</code>
136             * wieder rückgängig. Es wird nicht geprüft, ob der Sprung wirklich
137             * erlaubt ist.
138             */
139            public void zurueckSpringen(int x, int y, int richtung) {
140                    int neuesX = getNeuesX(x, richtung);
141                    int neuesY = getNeuesY(y, richtung);
142                    
143                    feldFreimachen(neuesX, neuesY);
144                    feldBesetzen(x, y);
145                    feldBesetzen((x + neuesX) / 2, (y + neuesY) / 2);
146            }
147            
148            /**
149             * Gibt die x-Koordinate für einen Sprung ausgehend von <code>x</code>
150             * in  die gegeben <code>richtung</code> zurück.
151             */
152            private int getNeuesX(int x, int richtung) {
153                    int neuesX = x;
154                    switch (richtung) {
155                    case RECHTS: neuesX += 2;
156                                break;
157                    case LINKS: neuesX -= 2;
158                    }
159                    return neuesX;
160            }
161            
162            /**
163             * Gibt die y-Koordinate für einen Sprung ausgehend von <code>y</code>
164             * in die gegeben <code>richtung</code> zurück.
165             */
166            private int getNeuesY(int y, int richtung) {
167                    int neuesY = y;
168                    
169                    switch (richtung) {
170                    case OBEN: neuesY -= 2;
171                                    break;
172                    case UNTEN: neuesY += 2;
173                    }
174                    
175                    return neuesY;
176            }
177            /**
178             * Gibt den Inhalt dieses Spielfelds auf dem Bildschirm aus.
179             * Die Werte haben folgende Bedeutung:
180             * <ul>
181             * <li>0 = kein gültiges Spielfeld.</li>
182             * <li>1 = Feld mit einem Spielstein.</li>
183             * <li>2 = Feld ohne Spielstein.</li>
184             * </ul>
185             */
186            public void ausgeben() {
187                    for (int x = 0; x < spielfeld.length; x++) {
188                            for (int y = 0; y < spielfeld[x].length; y++) {
189                                    System.out.print(spielfeld[x][y]);
190                            }
191                            System.out.println();
192                    }
193                    System.out.println();
194            }
195            
196            /**
197             * Gibt genau dann true zurück, wenn eine Spielfigur 
198             * an der Position (x,y) vorhanden ist.
199             */
200            public boolean isBesetzt(int x, int y) {
201                    return spielfeld[x][y] == BESETZT;
202            }
203    
204            /**
205             * Gibt ein Feld mit allen Werten für die Sprungrichtungen zurück.
206             */
207            public int [] getRichtungen() {
208                    // es wird immer ein Kopie zurückgegeben, um zu verhindern,
209                    // das die Werte innerhalb des Feldes richtungen verändert werden
210                    return Arrays.copyOf(richtungen, richtungen.length); // JDK 1.6 nötig
211            }
212    }