Skip to content

Commit f19e521

Browse files
committed
Tic-tac-toe game added to fx gui.
1 parent c224327 commit f19e521

File tree

5 files changed

+197
-11
lines changed

5 files changed

+197
-11
lines changed

aima-gui/src/main/java/aima/gui/fx/demo/IntegratedAimaApp.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import aima.gui.fx.demo.search.MapColoringApp;
44
import aima.gui.fx.demo.search.games.EightPuzzleApp;
5+
import aima.gui.fx.demo.search.games.TicTacToeApp;
56
import aima.gui.prog.agent.NondeterministicVacuumEnvironmentProg;
67
import aima.gui.prog.agent.TrivialVacuumProg;
78
import aima.gui.prog.logic.DPLLProg;
@@ -52,15 +53,21 @@ public void start(Stage primaryStage) throws Exception {
5253

5354
protected void defineContent(IntegratedAppPaneBuilder builder) {
5455
builder.defineTitle("Integrated FX AIMA App");
55-
builder.registerApp(SimulatedAnnealingMaximumFinderApp.class);
56-
builder.registerApp(GeneticMaximumFinderApp.class);
57-
builder.registerApp(NQueensSearchApp.class);
58-
builder.registerApp(EightPuzzleApp.class);
59-
builder.registerApp(ConnectFourApp.class);
6056

6157
builder.registerApp(VacuumAgentApp.class);
6258
builder.registerApp(RouteFindingAgentApp.class);
59+
6360
builder.registerApp(MapColoringApp.class);
61+
builder.registerApp(NQueensSearchApp.class);
62+
63+
builder.registerApp(EightPuzzleApp.class);
64+
builder.registerApp(ConnectFourApp.class);
65+
builder.registerApp(TicTacToeApp.class);
66+
67+
builder.registerApp(SimulatedAnnealingMaximumFinderApp.class);
68+
builder.registerApp(GeneticMaximumFinderApp.class);
69+
70+
6471

6572
builder.registerProg(GeneticMaximumFinderProg.class);
6673
builder.registerProg(NQueensSearchProg.class);

aima-gui/src/main/java/aima/gui/fx/demo/search/NQueensSearchApp.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ else if (simPaneCtrl.getParamValue(PARAM_INIT_CONFIG).equals("Random"))
9898
else
9999
config = Config.QUEENS_IN_FIRST_ROW;
100100
experiment.initExperiment(config);
101-
stateViewCtrl.updateBoard(experiment.getBoard());
101+
stateViewCtrl.update(experiment.getBoard());
102102
}
103103

104104
@Override
@@ -134,7 +134,7 @@ private void updateStateView(NQueensBoard board, Metrics metrics) {
134134
* Must be called by the GUI thread!
135135
*/
136136
private void updateStateViewLater(NQueensBoard board, Metrics metrics) {
137-
stateViewCtrl.updateBoard(board);
137+
stateViewCtrl.update(board);
138138
simPaneCtrl.setStatus(metrics.toString());
139139
}
140140
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package aima.gui.fx.demo.search.games;
2+
3+
import aima.core.environment.tictactoe.TicTacToeGame;
4+
import aima.core.environment.tictactoe.TicTacToeState;
5+
import aima.core.search.adversarial.AdversarialSearch;
6+
import aima.core.search.adversarial.AlphaBetaSearch;
7+
import aima.core.search.adversarial.IterativeDeepeningAlphaBetaSearch;
8+
import aima.core.search.adversarial.MinimaxSearch;
9+
import aima.core.search.framework.Metrics;
10+
import aima.core.util.datastructure.XYLocation;
11+
import aima.gui.fx.framework.IntegrableApplication;
12+
import javafx.beans.binding.Bindings;
13+
import javafx.event.ActionEvent;
14+
import javafx.geometry.Pos;
15+
import javafx.scene.control.*;
16+
import javafx.scene.layout.*;
17+
import javafx.scene.text.Font;
18+
19+
/**
20+
* Simple graphical Tic-tac-toe game application. It demonstrates the Minimax
21+
* algorithm for move selection as well as alpha-beta pruning.
22+
*
23+
* @author Ruediger Lunde
24+
*/
25+
public class TicTacToeApp extends IntegrableApplication {
26+
27+
public static void main(String[] args) {
28+
launch(args);
29+
}
30+
31+
private Button clearBtn;
32+
private ComboBox<String> strategyCombo;
33+
private Button proposeBtn;
34+
private Label statusLabel;
35+
36+
private Button[] squareBtns = new Button[9];
37+
TicTacToeGame game;
38+
TicTacToeState currState;
39+
Metrics searchMetrics;
40+
41+
42+
@Override
43+
public String getTitle() {
44+
return "Tic-tac-toe App";
45+
}
46+
47+
/** Simple pane to control the game. */
48+
@Override
49+
public Pane createRootPane() {
50+
BorderPane root = new BorderPane();
51+
52+
ToolBar toolBar = new ToolBar();
53+
clearBtn = new Button("Clear");
54+
clearBtn.setOnAction(ev -> initialize());
55+
56+
strategyCombo = new ComboBox<String>();
57+
strategyCombo.getItems().addAll("Minimax",
58+
"Alpha-Beta", "Iterative Deepening Alpha-Beta",
59+
"Iterative Deepening Alpha-Beta (log)");
60+
strategyCombo.getSelectionModel().select(0);
61+
62+
proposeBtn = new Button("Propose Move");
63+
proposeBtn.setOnAction(ev -> proposeMove());
64+
toolBar.getItems().addAll(clearBtn, new Separator(), strategyCombo, proposeBtn);
65+
root.setTop(toolBar);
66+
67+
StackPane stateViewPane = new StackPane();
68+
GridPane gridPane = new GridPane();
69+
gridPane.maxWidthProperty().bind(Bindings.min(stateViewPane.widthProperty(), stateViewPane.heightProperty())
70+
.subtract(20));
71+
gridPane.maxHeightProperty().bind(Bindings.min(stateViewPane.widthProperty(), stateViewPane.heightProperty())
72+
.subtract(20));
73+
RowConstraints c1 = new RowConstraints();
74+
c1.setPercentHeight(100.0 / 3);
75+
ColumnConstraints c2 = new ColumnConstraints();
76+
c2.setPercentWidth(100.0 / 3);
77+
gridPane.setHgap(10);
78+
gridPane.setVgap(10);
79+
for (int i = 0; i < 3; i++) {
80+
gridPane.getRowConstraints().add(c1);
81+
gridPane.getColumnConstraints().add(c2);
82+
}
83+
Font font = Font.font(40);
84+
for (int i = 0; i < 9; i++) {
85+
Button btn = new Button();
86+
btn.setOnAction(this::handleSquareButtonEvent);
87+
btn.setFont(font);
88+
btn.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
89+
squareBtns[i] = btn;
90+
gridPane.add(btn, i % 3, i / 3);
91+
}
92+
stateViewPane.getChildren().add(gridPane);
93+
root.setCenter(stateViewPane);
94+
95+
statusLabel = new Label();
96+
statusLabel.setMaxWidth(Double.MAX_VALUE);
97+
statusLabel.setMaxWidth(Double.MAX_VALUE);
98+
statusLabel.setAlignment(Pos.CENTER);
99+
statusLabel.setFont(Font.font(16));
100+
root.setBottom(statusLabel);
101+
return root;
102+
}
103+
104+
@Override
105+
public void initialize() {
106+
game = new TicTacToeGame();
107+
currState = game.getInitialState();
108+
searchMetrics = null;
109+
update();
110+
}
111+
112+
@Override
113+
public void finalize() {
114+
// nothing to do here...
115+
}
116+
117+
/** Updates square texts according to game state and also the status bar. */
118+
private void update() {
119+
for (int i = 0; i < 9; i++) {
120+
String val = currState.getValue(i % 3, i / 3);
121+
if (val == TicTacToeState.EMPTY)
122+
val = "";
123+
squareBtns[i].setText(val);
124+
}
125+
proposeBtn.setDisable(game.isTerminal(currState));
126+
127+
// update status...
128+
String statusText;
129+
if (game.isTerminal(currState))
130+
if (game.getUtility(currState, TicTacToeState.X) == 1)
131+
statusText = "X has won :-)";
132+
else if (game.getUtility(currState, TicTacToeState.O) == 1)
133+
statusText = "O has won :-)";
134+
else
135+
statusText = "No winner...";
136+
else
137+
statusText = "Next move: " + game.getPlayer(currState);
138+
if (searchMetrics != null)
139+
statusText += " " + searchMetrics;
140+
statusLabel.setText(statusText);
141+
}
142+
143+
/** Uses adversarial search for selecting the next action. */
144+
private void proposeMove() {
145+
AdversarialSearch<TicTacToeState, XYLocation> search;
146+
XYLocation action;
147+
switch (strategyCombo.getSelectionModel().getSelectedIndex()) {
148+
case 0:
149+
search = MinimaxSearch.createFor(game);
150+
break;
151+
case 1:
152+
search = AlphaBetaSearch.createFor(game);
153+
break;
154+
case 2:
155+
search = IterativeDeepeningAlphaBetaSearch.createFor(game, 0.0,
156+
1.0, 1000);
157+
break;
158+
default:
159+
search = IterativeDeepeningAlphaBetaSearch.createFor(game, 0.0,
160+
1.0, 1000);
161+
((IterativeDeepeningAlphaBetaSearch<?, ?, ?>) search)
162+
.setLogEnabled(true);
163+
}
164+
action = search.makeDecision(currState);
165+
searchMetrics = search.getMetrics();
166+
currState = game.getResult(currState, action);
167+
update();
168+
}
169+
170+
/** Handles user moves. */
171+
private void handleSquareButtonEvent(ActionEvent ae) {
172+
for (int i = 0; i < 9; i++)
173+
if (ae.getSource() == squareBtns[i])
174+
currState = game.getResult(currState,
175+
new XYLocation(i % 3, i / 3));
176+
searchMetrics = null;
177+
update();
178+
}
179+
}

aima-gui/src/main/java/aima/gui/fx/views/EightPuzzleViewCtrl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
*/
2121
public class EightPuzzleViewCtrl {
2222

23-
private GridPane gridPane = new GridPane();
2423
private Button[] tileBtns = new Button[9];
2524
private EightPuzzleBoard board;
2625

@@ -29,8 +28,9 @@ public class EightPuzzleViewCtrl {
2928
* instance which is responsible for Eight Puzzle tile positioning on the grid.
3029
*/
3130
public EightPuzzleViewCtrl(StackPane viewRoot) {
31+
GridPane gridPane = new GridPane();
3232
viewRoot.getChildren().add(gridPane);
33-
viewRoot.setAlignment(Pos.BOTTOM_CENTER);
33+
viewRoot.setAlignment(Pos.CENTER);
3434
gridPane.maxWidthProperty().bind(Bindings.min(viewRoot.widthProperty(), viewRoot.heightProperty()).subtract(20));
3535
gridPane.maxHeightProperty().bind(Bindings.min(viewRoot.widthProperty(), viewRoot.heightProperty()).subtract(10));
3636
RowConstraints c1 = new RowConstraints();
@@ -43,7 +43,7 @@ public EightPuzzleViewCtrl(StackPane viewRoot) {
4343
gridPane.getRowConstraints().add(c1);
4444
gridPane.getColumnConstraints().add(c2);
4545
}
46-
Font font = Font.font(30);
46+
Font font = Font.font(40);
4747
for (int i = 0; i < 9; i++) {
4848
Button btn = new Button();
4949
btn.setOnAction(this::handleButtonEvent);

aima-gui/src/main/java/aima/gui/fx/views/NQueensViewCtrl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public NQueensViewCtrl(StackPane viewRoot) {
3636
}
3737

3838
/** Updates the view. */
39-
public void updateBoard(NQueensBoard board) {
39+
public void update(NQueensBoard board) {
4040
int size = board.getSize();
4141
if (queens.length != size * size) {
4242
gridPane.getChildren().clear();

0 commit comments

Comments
 (0)