2
* This program is free software; you can redistribute it and/or modify
3
* it under the terms of the GNU General Public License as published by
4
* the Free Software Foundation; either version 2 of the License, or
5
* (at your option) any later version.
6
* This program is distributed in the hope that it will be useful,
7
* but WITHOUT ANY WARRANTY; without even the implied warranty of
8
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
* GNU General Public License for more details.
10
* You should have received a copy of the GNU General Public License
11
* along with this program; if not, write to the Free Software
12
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
package games.strategy.common.player.ai;
17
import java.util.HashSet;
19
import java.util.Stack;
22
* Utility class implementing AI game algorithms.
24
* Currently, minimax and alpha-beta algorithms are implemented.
26
* @author Lane Schwartz
27
* @version $LastChangedDate: 2008-01-05 06:33:48 +0800 (Sat, 05 Jan 2008) $
28
* @see "Chapter 6 of Artificial Intelligence, 2nd ed. by Stuart Russell & Peter Norvig"
30
public class AIAlgorithm
33
public static <Play> Stack<Play> depthFirstSearch(GameState<Play> state, int maxDepth)
35
Stack<Play> stack = new Stack<Play>();
37
if (state.gameIsOver())
39
//System.out.println("The given state is a solution!");
44
//System.out.println("Starting with " + state);
45
Set<GameState<Play>> visitedStates = new HashSet<GameState<Play>>();
46
visitedStates.add(state);
47
return dfs(state, visitedStates, stack, 0, maxDepth);
49
} catch (StackOverflowError e) {
54
private static <Play> Stack<Play> dfs(GameState<Play> state, Set<GameState<Play>> visitedStates, Stack<Play> plays, int depth, int maxDepth)
57
int playsSoFar = plays.size();
61
int childCounter = -1;
63
// Find all of the possible next states
64
for (GameState<Play> child : state.successors())
68
// Have we seen this child state before?
69
if (! visitedStates.contains(child))
71
//System.out.println("Considering child " + child + " #"+childCounter + " at depth " + depth + " created by move " + child.getMove());
73
// Mark that we've now seen this child state
74
//System.out.println("We have now seen " + child + " at depth " + depth + " created by move " + child.getMove());
75
visitedStates.add(child);
77
// Is the child state a win state?
78
if (child.gameIsOver())
80
//System.out.println("Success! at level " + depth + " " + child);
81
plays.push(child.getMove());
86
plays = dfs(child, visitedStates, plays, depth+1, maxDepth);
87
if (plays.size() > playsSoFar)
89
//System.out.println("Pushing play at " + depth + " " + child);
90
plays.push(child.getMove());
95
// else System.out.println("HAVE already seen " + child + " #"+childCounter + " now at depth " + depth+ " created by move " + child.getMove());
103
* Find the optimal next play to perform from the given game state,
104
* using the alpha-beta algorithm.
106
* @param <Play> class capable of representing a game play
107
* @param state current game state
108
* @return the optimal next play
110
public static <Play> Play alphaBetaSearch(GameState<Play> state)
112
Pair<Float,Play> m = maxValue(state, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
113
return m.getSecond();
119
private static <Play> Pair<Float,Play> maxValue(GameState<Play> state, float alpha, float beta)
121
float value = Float.NEGATIVE_INFINITY;
122
Play bestMove = null;
124
for (GameState<Play> s : state.successors())
125
{ Play a = s.getMove();
128
minValue = s.getUtility();
130
minValue = minValue(s, alpha, beta).getFirst();
131
if (minValue > value)
137
return new Pair<Float,Play>(value,bestMove);
142
return new Pair<Float,Play>(value,bestMove);
146
private static <Play> Pair<Float,Play> minValue(GameState<Play> state, float alpha, float beta)
148
float value = Float.POSITIVE_INFINITY;
149
Play bestMove = null;
151
for (GameState<Play> s : state.successors())
152
{ Play a = s.getMove();
155
maxValue = s.getUtility();
157
maxValue = maxValue(s, alpha, beta).getFirst();
158
if (maxValue < value)
164
return new Pair<Float,Play>(value,bestMove);
169
return new Pair<Float,Play>(value,bestMove);
176
* Find the optimal next play to perform from the given game state,
177
* using the minimax algorithm.
179
* @param <Play> class capable of representing a game play
180
* @param state current game state
181
* @return the optimal next play
183
public static <Play> Play minimaxSearch(GameState<Play> state)
185
Pair<Float,Play> m = maxValue(state);
186
return m.getSecond();
190
private static <Play> Pair<Float,Play> maxValue(GameState<Play> state)
192
float value = Float.NEGATIVE_INFINITY;
193
Play bestMove = null;
195
for (GameState<Play> s : state.successors())
197
Play a = s.getMove();
201
minValue = s.getUtility();
203
minValue = minValue(s).getFirst();
206
if (minValue > value)
213
return new Pair<Float,Play>(value,bestMove);
218
private static <Play> Pair<Float,Play> minValue(GameState<Play> state)
220
float value = Float.POSITIVE_INFINITY;
221
Play bestMove = null;
223
for (GameState<Play> s : state.successors())
225
Play a = s.getMove();
229
maxValue = s.getUtility();
231
maxValue = maxValue(s).getFirst();
233
if (maxValue < value)
241
return new Pair<Float,Play>(value,bestMove);
248
static class Pair<First,Second> {
249
private First m_first;
250
private Second m_second;
252
Pair(First first, Second second)
258
First getFirst() { return m_first; }
259
Second getSecond() { return m_second; }