Skip to content

Commit 9a5705b

Browse files
Initial Parallel Version using deep copies and MCTS
0 parents  commit 9a5705b

File tree

22 files changed

+483
-0
lines changed

22 files changed

+483
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Ignore Gradle project-specific cache directory
2+
.gradle
3+
4+
# Ignore Gradle build output directory
5+
build

app/bin/main/javagamesolver/App.class

2.18 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
526 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
711 Bytes
Binary file not shown.

app/build.gradle

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* This file was generated by the Gradle 'init' task.
3+
*
4+
* This generated file contains a sample Java application project to get you started.
5+
* For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.8/userguide/building_java_projects.html in the Gradle documentation.
6+
*/
7+
8+
plugins {
9+
// Apply the application plugin to add support for building a CLI application in Java.
10+
id 'application'
11+
}
12+
13+
repositories {
14+
// Use Maven Central for resolving dependencies.
15+
mavenCentral()
16+
}
17+
18+
dependencies {
19+
// Use JUnit Jupiter for testing.
20+
testImplementation libs.junit.jupiter
21+
22+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
23+
24+
// This dependency is used by the application.
25+
implementation libs.guava
26+
implementation "io.vavr:vavr:0.10.5"
27+
}
28+
29+
// Apply a specific Java toolchain to ease working on different environments.
30+
java {
31+
toolchain {
32+
languageVersion = JavaLanguageVersion.of(21)
33+
}
34+
}
35+
36+
application {
37+
// Define the main class for the application.
38+
mainClass = 'javagamesolver.App'
39+
}
40+
41+
tasks.named('test') {
42+
// Use JUnit Platform for unit tests.
43+
useJUnitPlatform()
44+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* This source file was generated by the Gradle 'init' task
3+
*/
4+
package javagamesolver;
5+
6+
import java.util.List;
7+
import java.util.Scanner;
8+
9+
import javagamesolver.games.Connect_Four;
10+
import javagamesolver.interfaces.Game;
11+
import javagamesolver.strategies.MCTS;
12+
import javagamesolver.interfaces.GameStrategy;
13+
14+
public class App {
15+
public String getGreeting() {
16+
return "Hello World!";
17+
}
18+
19+
public static void main(String[] args) {
20+
Game game = new Connect_Four();
21+
GameStrategy strategy = new MCTS();
22+
Scanner scanner = new Scanner(System.in);
23+
System.out.println("Started game!");
24+
while (true) {
25+
game = strategy.make_move(game);
26+
27+
System.out.println("Board \n" + game.toString());
28+
29+
if (game.is_winning_state()) {
30+
System.out.println("You lost!");
31+
break;
32+
}
33+
34+
if (game.is_game_over()) {
35+
System.out.println("Tie!");
36+
break;
37+
}
38+
39+
System.out.println("Enter column to drop piece in: ");
40+
int col = scanner.nextInt();
41+
42+
game = game.player_move(col);
43+
44+
System.out.println("Board: \n" + game.toString());
45+
46+
if (game.is_winning_state()) {
47+
System.out.println("You Won!");
48+
break;
49+
}
50+
51+
if (game.is_game_over()) {
52+
System.out.println("Tie!");
53+
break;
54+
}
55+
}
56+
57+
scanner.close();
58+
}
59+
}
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
package javagamesolver.games;
2+
3+
import java.util.Arrays;
4+
import java.util.LinkedList;
5+
import java.util.List;
6+
import java.util.stream.Stream;
7+
8+
import javagamesolver.interfaces.Game;
9+
import javagamesolver.interfaces.GamePlayer;
10+
11+
public class Connect_Four implements Game {
12+
13+
private record PiecePosition(int row, int col) {
14+
}
15+
16+
private enum GamePiece {
17+
EMPTY, PLAYER1, PLAYER2
18+
};
19+
20+
private final GamePiece[][] board;
21+
22+
private final boolean player1Turn;
23+
24+
private GamePiece[][] deepCopyBoard(GamePiece[][] board) {
25+
GamePiece[][] new_board = new GamePiece[board.length][];
26+
27+
for (int i = 0; i < new_board.length; i++) {
28+
new_board[i] = Arrays.copyOf(board[i], board[i].length);
29+
}
30+
31+
return new_board;
32+
}
33+
34+
private Connect_Four(GamePiece[][] board, boolean player1Turn) {
35+
this.board = deepCopyBoard(board);
36+
this.player1Turn = player1Turn;
37+
}
38+
39+
public Connect_Four() {
40+
GamePiece[][] board = new GamePiece[6][7];
41+
for (GamePiece[] row : board) {
42+
Arrays.fill(row, GamePiece.EMPTY);
43+
}
44+
45+
this.board = board;
46+
this.player1Turn = true;
47+
48+
System.out.println(this.toString());
49+
}
50+
51+
/*
52+
* Copy existing board and create new connect four game with pos being played
53+
*/
54+
private Game set_position(Connect_Four game, PiecePosition pos) {
55+
GamePiece piece = game.player1Turn ? GamePiece.PLAYER1 : GamePiece.PLAYER2;
56+
Connect_Four new_game = new Connect_Four(game.board, !game.player1Turn);
57+
new_game.board[pos.row][pos.col] = piece;
58+
return new_game;
59+
}
60+
61+
@Override
62+
public List<Game> possible_games() {
63+
int num_col = this.board[0].length;
64+
int row_len = this.board.length;
65+
List<PiecePosition> valid_moves = new LinkedList<PiecePosition>();
66+
for (int col = 0; col < num_col; col++) {
67+
for (int row = row_len - 1; row >= 0; row--) {
68+
if (board[row][col] == GamePiece.EMPTY) {
69+
valid_moves.add(new PiecePosition(row, col));
70+
break;
71+
}
72+
}
73+
}
74+
75+
List<Game> games = new LinkedList<Game>();
76+
for (PiecePosition move : valid_moves) {
77+
games.add(set_position(this, move));
78+
}
79+
80+
return games;
81+
}
82+
83+
@Override
84+
public boolean is_game_over() {
85+
if (this.possible_games().isEmpty()) {
86+
return true;
87+
}
88+
89+
return this.is_winning_state();
90+
}
91+
92+
private boolean horizontal_win(GamePiece playerPiece) {
93+
for (GamePiece[] row : this.board) {
94+
int count = 0;
95+
96+
for (GamePiece piece : row) {
97+
if (piece == playerPiece) {
98+
++count;
99+
if (count >= 4) {
100+
return true;
101+
}
102+
} else {
103+
count = 0;
104+
}
105+
}
106+
}
107+
108+
return false;
109+
}
110+
111+
// This could be implemented by transposing the board and calling horizontal win
112+
// but that requires more compute resources
113+
private boolean vertical_win(GamePiece piece) {
114+
int col_len = this.board[0].length;
115+
int row_len = this.board.length;
116+
for (int col = 0; col < col_len; col++) {
117+
int count = 0;
118+
for (int row = 0; row < row_len; row++) {
119+
if (board[row][col] == piece) {
120+
++count;
121+
if (count >= 4) {
122+
return true;
123+
}
124+
} else {
125+
count = 0;
126+
}
127+
}
128+
}
129+
130+
return false;
131+
}
132+
133+
private boolean check_diagonal_up(GamePiece piece, int r, int c) {
134+
int col_len = this.board[0].length;
135+
int count = 0;
136+
for (int row = r, col = c; col < col_len && row >= 0; row--, col++) {
137+
if (board[row][col] == piece) {
138+
++count;
139+
if (count >= 4) {
140+
return true;
141+
}
142+
} else {
143+
count = 0;
144+
}
145+
146+
}
147+
148+
return false;
149+
}
150+
151+
private boolean check_diagonal_down(GamePiece piece, int r, int c) {
152+
int col_len = this.board[0].length;
153+
int row_len = this.board.length;
154+
155+
int count = 0;
156+
for (int row = r, col = c; col < col_len && row < row_len; row++, col++) {
157+
if (board[row][col] == piece) {
158+
++count;
159+
if (count >= 4) {
160+
return true;
161+
}
162+
} else {
163+
count = 0;
164+
}
165+
166+
}
167+
168+
return false;
169+
}
170+
171+
private boolean diagonal_win(GamePiece piece) {
172+
int col_len = this.board[0].length;
173+
int row_len = this.board.length;
174+
175+
// Check all first column diagonals
176+
for (int row = 0; row < row_len; row++) {
177+
if (check_diagonal_up(piece, row, 0) ||
178+
check_diagonal_up(piece, row, col_len)) {
179+
return true;
180+
}
181+
182+
}
183+
184+
// Check bottom row diagonals
185+
for (int col = 1; col < col_len; col++) {
186+
if (check_diagonal_up(piece, row_len - 1, col) ||
187+
check_diagonal_down(piece, 0, col)) {
188+
return true;
189+
}
190+
}
191+
192+
return false;
193+
}
194+
195+
@Override
196+
public boolean is_winning_state() {
197+
// Check if the last move won the game
198+
GamePiece piece = this.player1Turn ? GamePiece.PLAYER2 : GamePiece.PLAYER1;
199+
return this.horizontal_win(piece) || this.vertical_win(piece) || this.diagonal_win(piece);
200+
}
201+
202+
@Override
203+
public int reward_value(GamePlayer player) {
204+
// Check if the last player to move won
205+
boolean winningState = is_winning_state();
206+
if (winningState) {
207+
return this.player_turn() == player ? -1 : 1;
208+
}
209+
210+
return 0;
211+
}
212+
213+
@Override
214+
public String toString() {
215+
StringBuilder sb = new StringBuilder();
216+
217+
for (GamePiece[] row : this.board) {
218+
for (GamePiece piece : row) {
219+
switch (piece) {
220+
case GamePiece.EMPTY -> sb.append("_ ");
221+
case GamePiece.PLAYER1 -> sb.append("1 ");
222+
case GamePiece.PLAYER2 -> sb.append("2 ");
223+
}
224+
}
225+
sb.append("\n");
226+
}
227+
228+
return sb.toString();
229+
}
230+
231+
@Override
232+
public GamePlayer player_turn() {
233+
return this.player1Turn ? GamePlayer.PLAYER1 : GamePlayer.PLAYER2;
234+
}
235+
236+
@Override
237+
public Game player_move(int col) {
238+
int row_len = this.board.length;
239+
for (int row = row_len - 1; row >= 0; row--) {
240+
if (this.board[row][col] == GamePiece.EMPTY) {
241+
return set_position(this, new PiecePosition(row, col));
242+
}
243+
}
244+
245+
return this;
246+
}
247+
248+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package javagamesolver.interfaces;
2+
3+
import java.util.List;
4+
5+
public interface Game {
6+
public List<Game> possible_games();
7+
8+
public boolean is_game_over();
9+
10+
public boolean is_winning_state();
11+
12+
public int reward_value(GamePlayer player);
13+
14+
public String toString();
15+
16+
public GamePlayer player_turn();
17+
18+
// Todo Refactor so that it takes a map and add a "read_move" method that reads
19+
// from stdin
20+
public Game player_move(int col);
21+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package javagamesolver.interfaces;
2+
3+
public enum GamePlayer {
4+
PLAYER1, PLAYER2
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package javagamesolver.interfaces;
2+
3+
public interface GameStrategy {
4+
public Game make_move(Game game);
5+
}

0 commit comments

Comments
 (0)