mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
connect4 with minimax AI
This commit is contained in:
114
game/src/main/java/org/toop/game/Connect4/Connect4.java
Normal file
114
game/src/main/java/org/toop/game/Connect4/Connect4.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package org.toop.game.Connect4;
|
||||
|
||||
import org.toop.game.TurnBasedGame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Connect4 extends TurnBasedGame {
|
||||
private int movesLeft;
|
||||
|
||||
public Connect4() {
|
||||
super(6, 7, 2);
|
||||
movesLeft = board.length;
|
||||
}
|
||||
|
||||
public Connect4(Connect4 other) {
|
||||
super(other);
|
||||
movesLeft = other.movesLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Move[] getLegalMoves() {
|
||||
final ArrayList<Move> legalMoves = new ArrayList<>();
|
||||
final char currentValue = getCurrentValue();
|
||||
|
||||
for (int i = 0; i < columnSize; i++) {
|
||||
if (board[i] == EMPTY) {
|
||||
legalMoves.add(new Move(i, currentValue));
|
||||
}
|
||||
}
|
||||
return legalMoves.toArray(new Move[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State play(Move move) {
|
||||
assert move != null;
|
||||
assert move.position() >= 0 && move.position() < board.length;
|
||||
assert move.value() == getCurrentValue();
|
||||
|
||||
int lowestEmptySpot = move.position();
|
||||
for (int i = 0; i < rowSize; i++) {
|
||||
int checkMovePosition = move.position() + columnSize * i;
|
||||
if (checkMovePosition < board.length) {
|
||||
if (board[checkMovePosition] == EMPTY) {
|
||||
lowestEmptySpot = checkMovePosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
board[lowestEmptySpot] = move.value();
|
||||
movesLeft--;
|
||||
|
||||
if (checkForWin()) {
|
||||
return State.WIN;
|
||||
}
|
||||
|
||||
nextTurn();
|
||||
|
||||
|
||||
return State.NORMAL;
|
||||
}
|
||||
|
||||
private boolean checkForWin() {
|
||||
char[][] boardGrid = makeBoardAGrid();
|
||||
|
||||
for (int row = 0; row < rowSize; row++) {
|
||||
for (int col = 0; col < columnSize; col++) {
|
||||
char cell = boardGrid[row][col];
|
||||
if (cell == ' ' || cell == 0) continue;
|
||||
|
||||
if (col + 3 < columnSize &&
|
||||
cell == boardGrid[row][col + 1] &&
|
||||
cell == boardGrid[row][col + 2] &&
|
||||
cell == boardGrid[row][col + 3]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (row + 3 < rowSize &&
|
||||
cell == boardGrid[row + 1][col] &&
|
||||
cell == boardGrid[row + 2][col] &&
|
||||
cell == boardGrid[row + 3][col]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (row + 3 < rowSize && col + 3 < columnSize &&
|
||||
cell == boardGrid[row + 1][col + 1] &&
|
||||
cell == boardGrid[row + 2][col + 2] &&
|
||||
cell == boardGrid[row + 3][col + 3]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (row + 3 < rowSize && col - 3 >= 0 &&
|
||||
cell == boardGrid[row + 1][col - 1] &&
|
||||
cell == boardGrid[row + 2][col - 2] &&
|
||||
cell == boardGrid[row + 3][col - 3]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public char[][] makeBoardAGrid() {
|
||||
char[][] boardGrid = new char[rowSize][columnSize];
|
||||
for (int i = 0; i < rowSize*columnSize; i++) {
|
||||
boardGrid[i / columnSize][i % columnSize] = board[i]; //boardGrid[y -> row] [x -> column]
|
||||
}
|
||||
return boardGrid;
|
||||
}
|
||||
|
||||
private char getCurrentValue() {
|
||||
return currentTurn == 0 ? 'X' : 'O';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
game/src/main/java/org/toop/game/Connect4/Connect4AI.java
Normal file
63
game/src/main/java/org/toop/game/Connect4/Connect4AI.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package org.toop.game.Connect4;
|
||||
|
||||
import org.toop.game.AI;
|
||||
import org.toop.game.Game;
|
||||
import org.toop.game.tictactoe.TicTacToe;
|
||||
|
||||
public class Connect4AI extends AI<Connect4> {
|
||||
|
||||
|
||||
public Game.Move findBestMove(Connect4 game, int depth) {
|
||||
assert game != null;
|
||||
assert depth >= 0;
|
||||
|
||||
final Game.Move[] legalMoves = game.getLegalMoves();
|
||||
|
||||
if (legalMoves.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int bestScore = -depth;
|
||||
Game.Move bestMove = null;
|
||||
|
||||
for (final Game.Move move : legalMoves) {
|
||||
final int score = getMoveScore(game, depth, move, true);
|
||||
|
||||
if (score > bestScore) {
|
||||
bestMove = move;
|
||||
bestScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)];
|
||||
}
|
||||
|
||||
private int getMoveScore(Connect4 game, int depth, Game.Move move, boolean maximizing) {
|
||||
final Connect4 copy = new Connect4(game);
|
||||
final Game.State state = copy.play(move);
|
||||
|
||||
switch (state) {
|
||||
case Game.State.DRAW: return 0;
|
||||
case Game.State.WIN: return maximizing? depth + 1 : -depth - 1;
|
||||
}
|
||||
|
||||
if (depth <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
final Game.Move[] legalMoves = copy.getLegalMoves();
|
||||
int score = maximizing? depth + 1 : -depth - 1;
|
||||
|
||||
for (final Game.Move next : legalMoves) {
|
||||
if (maximizing) {
|
||||
score = Math.min(score, getMoveScore(copy, depth - 1, next, false));
|
||||
} else {
|
||||
score = Math.max(score, getMoveScore(copy, depth - 1, next, true));
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user