|
1 | | -package aima.gui.fx.demo.search.games; |
2 | | - |
3 | | -import java.util.Observable; |
4 | | - |
5 | | -import aima.gui.fx.framework.IntegrableApplication; |
6 | | -import javafx.beans.binding.Bindings; |
7 | | -import javafx.geometry.HPos; |
8 | | -import javafx.geometry.Insets; |
9 | | -import javafx.scene.control.Button; |
10 | | -import javafx.scene.control.ComboBox; |
11 | | -import javafx.scene.control.Label; |
12 | | -import javafx.scene.control.Separator; |
13 | | -import javafx.scene.control.ToolBar; |
14 | | -import javafx.scene.layout.BorderPane; |
15 | | -import javafx.scene.layout.ColumnConstraints; |
16 | | -import javafx.scene.layout.GridPane; |
17 | | -import javafx.scene.layout.Pane; |
18 | | -import javafx.scene.layout.RowConstraints; |
19 | | -import javafx.scene.paint.Color; |
20 | | -import javafx.scene.shape.Circle; |
21 | | - |
22 | | -/** |
23 | | - * Starter of the JavaFX version of the Connect Four game with integrated |
24 | | - * artificial intelligence player. It uses Minimax search with Alpha-Beta |
25 | | - * pruning and an iterative deepening strategy to meet the specified time |
26 | | - * constraints. |
27 | | - * |
28 | | - * @author Ruediger Lunde |
29 | | - */ |
30 | | -public class ConnectFourApp extends IntegrableApplication { |
31 | | - |
32 | | - public static void main(String[] args) { |
33 | | - launch(args); |
34 | | - } |
35 | | - |
36 | | - private ConnectFourModel model; |
37 | | - |
38 | | - // create attributes for all parts of the application which need to be |
39 | | - // accessed by listeners, inner classes, and helper methods |
40 | | - |
41 | | - private Button clearBtn; |
42 | | - private ComboBox<String> strategyCombo; |
43 | | - private ComboBox<String> timeCombo; |
44 | | - private Button proposeBtn; |
45 | | - private Label statusBar; |
46 | | - |
47 | | - private Button[] colBtns; |
48 | | - private Circle[] disks; |
49 | | - |
50 | | - @Override |
51 | | - public String getTitle() { |
52 | | - return "Connect Four App"; |
53 | | - } |
54 | | - |
55 | | - @Override |
56 | | - public Pane createRootPane() { |
57 | | - model = new ConnectFourModel(); |
58 | | - BorderPane root = new BorderPane(); |
59 | | - |
60 | | - ToolBar toolBar = new ToolBar(); |
61 | | - toolBar.setStyle("-fx-background-color: rgb(0, 0, 200)"); |
62 | | - clearBtn = new Button("Clear"); |
63 | | - clearBtn.setOnAction(ev -> model.initGame()); |
64 | | - |
65 | | - strategyCombo = new ComboBox<String>(); |
66 | | - strategyCombo.getItems().addAll("Iterative Deepening Alpha-Beta", "Advanced Alpha-Beta"); |
67 | | - strategyCombo.getSelectionModel().select(0); |
68 | | - timeCombo = new ComboBox<String>(); |
69 | | - timeCombo.getItems().addAll("2sec", "4sec", "6sec", "8sec"); |
70 | | - timeCombo.getSelectionModel().select(1); |
71 | | - |
72 | | - proposeBtn = new Button("Propose Move"); |
73 | | - proposeBtn.setOnAction(ev -> model.proposeMove((timeCombo.getSelectionModel().getSelectedIndex() + 1) * 2, |
74 | | - strategyCombo.getSelectionModel().getSelectedIndex())); |
75 | | - toolBar.getItems().addAll(clearBtn, new Separator(), strategyCombo, timeCombo, proposeBtn); |
76 | | - root.setTop(toolBar); |
77 | | - |
78 | | - final int rows = model.getRows(); |
79 | | - final int cols = model.getCols(); |
80 | | - |
81 | | - BorderPane boardPane = new BorderPane(); |
82 | | - ColumnConstraints colCons = new ColumnConstraints(); |
83 | | - colCons.setPercentWidth(100.0 / cols); |
84 | | - |
85 | | - GridPane btnPane = new GridPane(); |
86 | | - GridPane diskPane = new GridPane(); |
87 | | - btnPane.setHgap(10); |
88 | | - btnPane.setPadding(new Insets(10, 10, 0, 10)); |
89 | | - btnPane.setStyle("-fx-background-color: rgb(0, 50, 255)"); |
90 | | - diskPane.setPadding(new Insets(10, 10, 10, 10)); |
91 | | - diskPane.setStyle("-fx-background-color: rgb(0, 50, 255)"); |
92 | | - |
93 | | - colBtns = new Button[cols]; |
94 | | - for (int i = 0; i < cols; i++) { |
95 | | - Button colBtn = new Button("" + (i + 1)); |
96 | | - colBtn.setId("" + (i + 1)); |
97 | | - colBtn.setMaxWidth(Double.MAX_VALUE); |
98 | | - colBtn.setOnAction(ev -> { |
99 | | - String id = ((Button) ev.getSource()).getId(); |
100 | | - int col = Integer.parseInt(id); |
101 | | - model.makeMove(col - 1); |
102 | | - }); |
103 | | - colBtns[i] = colBtn; |
104 | | - btnPane.add(colBtn, i, 0); |
105 | | - GridPane.setHalignment(colBtn, HPos.CENTER); |
106 | | - btnPane.getColumnConstraints().add(colCons); |
107 | | - diskPane.getColumnConstraints().add(colCons); |
108 | | - } |
109 | | - boardPane.setTop(btnPane); |
110 | | - |
111 | | - diskPane.setMinSize(0, 0); |
112 | | - diskPane.setPrefSize(cols * 100, rows * 100); |
113 | | - |
114 | | - RowConstraints rowCons = new RowConstraints(); |
115 | | - rowCons.setPercentHeight(100.0 / rows); |
116 | | - for (int i = 0; i < rows; i++) |
117 | | - diskPane.getRowConstraints().add(rowCons); |
118 | | - |
119 | | - disks = new Circle[rows * cols]; |
120 | | - for (int i = 0; i < rows * cols; i++) { |
121 | | - Circle disk = new Circle(30); |
122 | | - disk.radiusProperty().bind(Bindings.min(Bindings.divide(diskPane.widthProperty(), cols * 3), |
123 | | - Bindings.divide(diskPane.heightProperty(), rows * 3))); |
124 | | - disks[i] = disk; |
125 | | - diskPane.add(disk, i % cols, i / cols); |
126 | | - GridPane.setHalignment(disk, HPos.CENTER); |
127 | | - } |
128 | | - boardPane.setCenter(diskPane); |
129 | | - root.setCenter(boardPane); |
130 | | - statusBar = new Label(); |
131 | | - statusBar.setMaxWidth(Double.MAX_VALUE); |
132 | | - statusBar.setStyle("-fx-background-color: rgb(0, 0, 200); -fx-font-size: 20"); |
133 | | - root.setBottom(statusBar); |
134 | | - model.addObserver(this::update); |
135 | | - return root; |
136 | | - } |
137 | | - |
138 | | - @Override |
139 | | - public void initialize() { |
140 | | - update(null, null); |
141 | | - |
142 | | - } |
143 | | - |
144 | | - @Override |
145 | | - public void finalize() { |
146 | | - // Nothing to do here... |
147 | | - } |
148 | | - |
149 | | - /** Updates the view after the model state has changed. */ |
150 | | - private void update(Observable o, Object arg) { |
151 | | - final int cols = model.getCols(); |
152 | | - for (int i = 0; i < disks.length; i++) { |
153 | | - String player = model.getPlayerAt(i / cols, i % cols); |
154 | | - Color color = Color.DARKBLUE; |
155 | | - if (player != null) |
156 | | - color = player.equals("red") ? Color.RED : Color.YELLOW; |
157 | | - else { |
158 | | - for (String p : model.getPlayers()) |
159 | | - if (model.isWinPositionFor(i / cols, i % cols, p)) |
160 | | - color = Color.BLACK; |
161 | | - } |
162 | | - disks[i].setFill(color); |
163 | | - } |
164 | | - |
165 | | - String statusText; |
166 | | - if (!model.isGameOver()) { |
167 | | - String toMove = (String) model.getPlayerForNextMove(); |
168 | | - statusText = "Next move: " + toMove; |
169 | | - statusBar.setTextFill(toMove.equals("red") ? Color.RED : Color.YELLOW); |
170 | | - } else { |
171 | | - String winner = model.getWinner(); |
172 | | - if (winner != null) |
173 | | - statusText = "Color " + winner + " has won. Congratulations!"; |
174 | | - else |
175 | | - statusText = "No winner :-("; |
176 | | - statusBar.setTextFill(Color.WHITE); |
177 | | - } |
178 | | - if (model.searchMetrics != null) |
179 | | - statusText += " " + model.searchMetrics; |
180 | | - statusBar.setText(statusText); |
181 | | - } |
182 | | - |
183 | | -} |
| 1 | +package aima.gui.fx.demo.search.games; |
| 2 | + |
| 3 | +import java.util.Observable; |
| 4 | + |
| 5 | +import aima.gui.fx.framework.IntegrableApplication; |
| 6 | +import javafx.beans.binding.Bindings; |
| 7 | +import javafx.geometry.HPos; |
| 8 | +import javafx.geometry.Insets; |
| 9 | +import javafx.scene.control.Button; |
| 10 | +import javafx.scene.control.ComboBox; |
| 11 | +import javafx.scene.control.Label; |
| 12 | +import javafx.scene.control.Separator; |
| 13 | +import javafx.scene.control.ToolBar; |
| 14 | +import javafx.scene.layout.BorderPane; |
| 15 | +import javafx.scene.layout.ColumnConstraints; |
| 16 | +import javafx.scene.layout.GridPane; |
| 17 | +import javafx.scene.layout.Pane; |
| 18 | +import javafx.scene.layout.RowConstraints; |
| 19 | +import javafx.scene.paint.Color; |
| 20 | +import javafx.scene.shape.Circle; |
| 21 | + |
| 22 | +/** |
| 23 | + * Starter of the JavaFX version of the Connect Four game with integrated |
| 24 | + * artificial intelligence player. It uses Minimax search with Alpha-Beta |
| 25 | + * pruning and an iterative deepening strategy to meet the specified time |
| 26 | + * constraints. |
| 27 | + * |
| 28 | + * @author Ruediger Lunde |
| 29 | + */ |
| 30 | +public class ConnectFourApp extends IntegrableApplication { |
| 31 | + |
| 32 | + public static void main(String[] args) { |
| 33 | + launch(args); |
| 34 | + } |
| 35 | + |
| 36 | + private ConnectFourModel model; |
| 37 | + |
| 38 | + // create attributes for all parts of the application which need to be |
| 39 | + // accessed by listeners, inner classes, and helper methods |
| 40 | + |
| 41 | + private Button clearBtn; |
| 42 | + private ComboBox<String> strategyCombo; |
| 43 | + private ComboBox<String> timeCombo; |
| 44 | + private Button proposeBtn; |
| 45 | + private Label statusBar; |
| 46 | + |
| 47 | + private Button[] colBtns; |
| 48 | + private Circle[] disks; |
| 49 | + |
| 50 | + @Override |
| 51 | + public String getTitle() { |
| 52 | + return "Connect Four App"; |
| 53 | + } |
| 54 | + |
| 55 | + @Override |
| 56 | + public Pane createRootPane() { |
| 57 | + model = new ConnectFourModel(); |
| 58 | + BorderPane root = new BorderPane(); |
| 59 | + |
| 60 | + ToolBar toolBar = new ToolBar(); |
| 61 | + toolBar.setStyle("-fx-background-color: rgb(0, 0, 200)"); |
| 62 | + clearBtn = new Button("Clear"); |
| 63 | + clearBtn.setOnAction(ev -> model.initGame()); |
| 64 | + |
| 65 | + strategyCombo = new ComboBox<String>(); |
| 66 | + strategyCombo.getItems().addAll("Iterative Deepening Alpha-Beta", "Advanced Alpha-Beta"); |
| 67 | + strategyCombo.getSelectionModel().select(0); |
| 68 | + timeCombo = new ComboBox<String>(); |
| 69 | + timeCombo.getItems().addAll("2sec", "4sec", "6sec", "8sec"); |
| 70 | + timeCombo.getSelectionModel().select(1); |
| 71 | + |
| 72 | + proposeBtn = new Button("Propose Move"); |
| 73 | + proposeBtn.setOnAction(ev -> model.proposeMove((timeCombo.getSelectionModel().getSelectedIndex() + 1) * 2, |
| 74 | + strategyCombo.getSelectionModel().getSelectedIndex())); |
| 75 | + toolBar.getItems().addAll(clearBtn, new Separator(), strategyCombo, timeCombo, proposeBtn); |
| 76 | + root.setTop(toolBar); |
| 77 | + |
| 78 | + final int rows = model.getRows(); |
| 79 | + final int cols = model.getCols(); |
| 80 | + |
| 81 | + BorderPane boardPane = new BorderPane(); |
| 82 | + ColumnConstraints colCons = new ColumnConstraints(); |
| 83 | + colCons.setPercentWidth(100.0 / cols); |
| 84 | + |
| 85 | + GridPane btnPane = new GridPane(); |
| 86 | + GridPane diskPane = new GridPane(); |
| 87 | + btnPane.setHgap(10); |
| 88 | + btnPane.setPadding(new Insets(10, 10, 0, 10)); |
| 89 | + btnPane.setStyle("-fx-background-color: rgb(0, 50, 255)"); |
| 90 | + diskPane.setPadding(new Insets(10, 10, 10, 10)); |
| 91 | + diskPane.setStyle("-fx-background-color: rgb(0, 50, 255)"); |
| 92 | + |
| 93 | + colBtns = new Button[cols]; |
| 94 | + for (int i = 0; i < cols; i++) { |
| 95 | + Button colBtn = new Button("" + (i + 1)); |
| 96 | + colBtn.setId("" + (i + 1)); |
| 97 | + colBtn.setMaxWidth(Double.MAX_VALUE); |
| 98 | + colBtn.setOnAction(ev -> { |
| 99 | + String id = ((Button) ev.getSource()).getId(); |
| 100 | + int col = Integer.parseInt(id); |
| 101 | + model.makeMove(col - 1); |
| 102 | + }); |
| 103 | + colBtns[i] = colBtn; |
| 104 | + btnPane.add(colBtn, i, 0); |
| 105 | + GridPane.setHalignment(colBtn, HPos.CENTER); |
| 106 | + btnPane.getColumnConstraints().add(colCons); |
| 107 | + diskPane.getColumnConstraints().add(colCons); |
| 108 | + } |
| 109 | + boardPane.setTop(btnPane); |
| 110 | + |
| 111 | + diskPane.setMinSize(0, 0); |
| 112 | + diskPane.setPrefSize(cols * 100, rows * 100); |
| 113 | + |
| 114 | + RowConstraints rowCons = new RowConstraints(); |
| 115 | + rowCons.setPercentHeight(100.0 / rows); |
| 116 | + for (int i = 0; i < rows; i++) |
| 117 | + diskPane.getRowConstraints().add(rowCons); |
| 118 | + |
| 119 | + disks = new Circle[rows * cols]; |
| 120 | + for (int i = 0; i < rows * cols; i++) { |
| 121 | + Circle disk = new Circle(30); |
| 122 | + disk.radiusProperty().bind(Bindings.min(Bindings.divide(diskPane.widthProperty(), cols * 3), |
| 123 | + Bindings.divide(diskPane.heightProperty(), rows * 3))); |
| 124 | + disks[i] = disk; |
| 125 | + diskPane.add(disk, i % cols, i / cols); |
| 126 | + GridPane.setHalignment(disk, HPos.CENTER); |
| 127 | + } |
| 128 | + boardPane.setCenter(diskPane); |
| 129 | + root.setCenter(boardPane); |
| 130 | + statusBar = new Label(); |
| 131 | + statusBar.setMaxWidth(Double.MAX_VALUE); |
| 132 | + statusBar.setStyle("-fx-background-color: rgb(0, 0, 200); -fx-font-size: 20"); |
| 133 | + root.setBottom(statusBar); |
| 134 | + model.addObserver(this::update); |
| 135 | + return root; |
| 136 | + } |
| 137 | + |
| 138 | + @Override |
| 139 | + public void initialize() { |
| 140 | + update(null, null); |
| 141 | + |
| 142 | + } |
| 143 | + |
| 144 | + @Override |
| 145 | + public void finalize() { |
| 146 | + // Nothing to do here... |
| 147 | + } |
| 148 | + |
| 149 | + /** Updates the view after the model state has changed. */ |
| 150 | + private void update(Observable o, Object arg) { |
| 151 | + final int cols = model.getCols(); |
| 152 | + for (int i = 0; i < disks.length; i++) { |
| 153 | + String player = model.getPlayerAt(i / cols, i % cols); |
| 154 | + Color color = Color.DARKBLUE; |
| 155 | + if (player != null) |
| 156 | + color = player.equals("red") ? Color.RED : Color.YELLOW; |
| 157 | + else { |
| 158 | + for (String p : model.getPlayers()) |
| 159 | + if (model.isWinPositionFor(i / cols, i % cols, p)) |
| 160 | + color = Color.BLACK; |
| 161 | + } |
| 162 | + disks[i].setFill(color); |
| 163 | + } |
| 164 | + |
| 165 | + String statusText; |
| 166 | + if (!model.isGameOver()) { |
| 167 | + String toMove = (String) model.getPlayerForNextMove(); |
| 168 | + statusText = "Next move: " + toMove; |
| 169 | + statusBar.setTextFill(toMove.equals("red") ? Color.RED : Color.YELLOW); |
| 170 | + } else { |
| 171 | + String winner = model.getWinner(); |
| 172 | + if (winner != null) |
| 173 | + statusText = "Color " + winner + " has won. Congratulations!"; |
| 174 | + else |
| 175 | + statusText = "No winner :-("; |
| 176 | + statusBar.setTextFill(Color.WHITE); |
| 177 | + } |
| 178 | + if (model.searchMetrics != null) |
| 179 | + statusText += " " + model.searchMetrics; |
| 180 | + statusBar.setText(statusText); |
| 181 | + } |
| 182 | + |
| 183 | +} |
0 commit comments