-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
/* | ||
* Copyright (C) 2022 Lamberto Colazzo | ||
* | ||
* This file is part of the ConnectX software developed for the | ||
* Intern ship of the course "Information technology", University of Bologna | ||
* A.Y. 2021-2022. | ||
* | ||
* ConnectX is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details; see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
package connectx.MinMaxPlayer; | ||
|
||
import connectx.CXPlayer; | ||
import connectx.CXBoard; | ||
import connectx.CXGameState; | ||
import connectx.CXCell; | ||
import java.util.TreeSet; | ||
import java.util.Random; | ||
import java.util.Arrays; | ||
import java.util.concurrent.TimeoutException; | ||
|
||
/** | ||
* Software player only a bit smarter than random. | ||
* <p> | ||
* It can detect a single-move win or loss. In all the other cases behaves | ||
* randomly. | ||
* </p> | ||
*/ | ||
public class MinMaxPlayer implements CXPlayer { | ||
private Random rand; | ||
private CXGameState myWin; | ||
private CXGameState yourWin; | ||
private int TIMEOUT; | ||
private long START; | ||
|
||
/* Default empty constructor */ | ||
public MFPlayer() { | ||
} | ||
|
||
public void initPlayer(int M, int N, int K, boolean first, int timeout_in_secs) { | ||
// New random seed for each game | ||
rand = new Random(System.currentTimeMillis()); | ||
myWin = first ? CXGameState.WINP1 : CXGameState.WINP2; | ||
yourWin = first ? CXGameState.WINP2 : CXGameState.WINP1; | ||
TIMEOUT = timeout_in_secs; | ||
} | ||
|
||
/** | ||
* Selects a free colum on game board. | ||
* <p> | ||
* Selects a winning column (if any), otherwise selects a column (if any) | ||
* that prevents the adversary to win with his next move. If both previous | ||
* cases do not apply, selects a random column. | ||
* </p> | ||
*/ | ||
public int selectColumn(CXBoard B) { | ||
START = System.currentTimeMillis(); // Save starting time | ||
|
||
Integer[] L = B.getAvailableColumns(); | ||
int save = L[rand.nextInt(L.length)]; // Save a random column | ||
|
||
try { | ||
for (int i : L) { | ||
|
||
} | ||
} catch (TimeoutException e) { | ||
System.err.println("Timeout! Random column selected"); | ||
return save; | ||
} | ||
} | ||
|
||
|
||
private void checktime() throws TimeoutException { | ||
if ((System.currentTimeMillis() - START) / 1000.0 >= TIMEOUT * (99.0 / 100.0)) | ||
throw new TimeoutException(); | ||
} | ||
|
||
public int minimax(CXBoard board, Integer[] L, int depth, int player, int alpha, int beta) throws TimeoutException { | ||
// Check if the game is over or if the depth limit has been reached | ||
if (B.gameState() == yourWin || depth == 0) { | ||
return board.evaluate(player); | ||
} | ||
|
||
int bestScore; | ||
|
||
// If it's the maximizing player's turn, initialize the best score to the smallest possible value | ||
if (board.getCurrentPlayer() == player) { | ||
bestScore = Intzeger.MIN_VALUE; | ||
|
||
// Iterate over all possible moves and recursively evaluate each one | ||
for (int i : L) { | ||
board.markColumn(i); | ||
int score = minimax(board, board.getAvaibleColumns(), depth - 1, player, alpha, beta); | ||
board.unmarkColumn(); | ||
bestScore = Math.max(bestScore, score); | ||
alpha = Math.max(alpha, score); | ||
if (beta <= alpha) { | ||
break; // Beta cutoff | ||
} | ||
} | ||
} | ||
// If it's the minimizing player's turn, initialize the best score to the largest possible value | ||
else { | ||
bestScore = Integer.MAX_VALUE; | ||
|
||
// Iterate over all possible moves and recursively evaluate each one | ||
for (int i : L) { | ||
board.markColumn(i); | ||
int score = minimax(board, board.getAvaibleColumns(),depth - 1, player, alpha, beta); | ||
board.unmarkColumn(); | ||
bestScore = Math.min(bestScore, score); | ||
beta = Math.min(beta, score); | ||
if (beta <= alpha) { | ||
break; // Alpha cutoff | ||
} | ||
} | ||
} | ||
|
||
return bestScore; | ||
} | ||
|
||
/** | ||
* Check if we can win in a single move | ||
* | ||
* Returns the winning column if there is one, otherwise -1 | ||
*/ | ||
private int singleMoveWin(CXBoard B, Integer[] L) throws TimeoutException { | ||
for(int i : L) { | ||
checktime(); // Check timeout at every iteration | ||
CXGameState state = B.markColumn(i); | ||
if (state == myWin) | ||
return i; // Winning column found: return immediately | ||
B.unmarkColumn(); | ||
} | ||
return -1; | ||
} | ||
|
||
/** | ||
* Check if we can block adversary's victory | ||
* | ||
* Returns a blocking column if there is one, otherwise a random one | ||
*/ | ||
private int singleMoveBlock(CXBoard B, Integer[] L) throws TimeoutException { | ||
TreeSet<Integer> T = new TreeSet<Integer>(); // We collect here safe column indexes | ||
|
||
for(int i : L) { | ||
checktime(); | ||
T.add(i); // We consider column i as a possible move | ||
B.markColumn(i); | ||
|
||
int j; | ||
boolean stop; | ||
|
||
for(j = 0, stop=false; j < L.length && !stop; j++) { | ||
//try {Thread.sleep((int)(0.2*1000*TIMEOUT));} catch (Exception e) {} // Uncomment to test timeout | ||
checktime(); | ||
if(!B.fullColumn(L[j])) { | ||
CXGameState state = B.markColumn(L[j]); | ||
if (state == yourWin) { | ||
T.remove(i); // We ignore the i-th column as a possible move | ||
stop = true; // We don't need to check more | ||
} | ||
B.unmarkColumn(); // | ||
} | ||
} | ||
B.unmarkColumn(); | ||
} | ||
|
||
if (T.size() > 0) { | ||
Integer[] X = T.toArray(new Integer[T.size()]); | ||
return X[rand.nextInt(X.length)]; | ||
} else { | ||
return L[rand.nextInt(L.length)]; | ||
} | ||
} | ||
|
||
public String playerName() { | ||
return "MinMaxPlayer"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
java -cp . connectx/CXPlayerTester -r 5 5 5 4 connectx.L1.L1 connectx.L0.L0 | ||
:: Usage: CXPlayerTester [OPTIONS] <M> <N> <X> <CXPlayer class> <CXPlayer class> | ||
:: OPTIONS: | ||
:: -t <timeout> Timeout in seconds. Default: 10 | ||
:: -r <rounds> Number of rounds. Default: 1 | ||
:: -v Verbose. Default: false |