Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ private NavigationManager() {
private static final Stack<ScreenNavigationEntry> screenStack = new Stack<>();

public static Scene init() throws IOException {
// شاشة البداية

current = new ScreenNavigationEntry(Screens.PRIMARY, null, null);

Parent root = initRoot(current.screen().getName());
scene = new Scene(root, 640, 480);

// طبق الثيم على الـ Scene الرئيسي وفعل listener لأي تغيير مستقبلي

ThemeManager.init(scene);

return scene;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package com.boredxgames.tictactoeclient.domain.model;

import com.boredxgames.tictactoeclient.domain.services.game.GameBoard;

/**
* @author Tasneem
*/
public class Move {
private final int row;

private final int row;
private final int col;

public Move(int row, int col) {
this.row = row;
this.col = col;
}

public int getRow() {
return row;
}

public int getCol() {
return col;
}
public int getRow() { return row; }
public int getCol() { return col; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Enum.java to edit this template
*/
package com.boredxgames.tictactoeclient.domain.services;

/**
*
* @author sheri
*/
public enum AIDifficulty {
EASY,
MEDIUM,
HARD
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.boredxgames.tictactoeclient.domain.services;

import com.boredxgames.tictactoeclient.domain.services.game.GameBoard;

import java.util.List;
import java.util.Random;

/**
*
* @author sheri
*/
public class AIService {
private static final Random random = new Random();
private static AIDifficulty diffiulty = AIDifficulty.EASY;

public static int[] nextMove(GameBoard board, AIDifficulty difficulty) {
if (board.getAvailableMoves().isEmpty()) return null;

return switch (difficulty) {
case EASY -> randomMove(board);
case MEDIUM -> mediumMove(board);
case HARD -> hardMove(board);
};
}

public static AIDifficulty getDiffiulty() {
return diffiulty;
}

public static void setDiffiulty(AIDifficulty diffiulty) {
AIService.diffiulty = diffiulty;
}



private static int[] randomMove(GameBoard board) {
List<int[]> moves = board.getAvailableMoves();
return moves.get(random.nextInt(moves.size()));
}

private static int[] mediumMove(GameBoard board) {
if (random.nextBoolean()) return hardMove(board);
return randomMove(board);
}

private static int[] hardMove(GameBoard board) {
return MinimaxEngine.bestMove(board, board.getCurrentPlayer());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.boredxgames.tictactoeclient.domain.services;


import com.boredxgames.tictactoeclient.domain.services.game.GameBoard;

/**
*
* @author sheri
*/
public class BoardUtils {
public static GameBoard copy(GameBoard original) {
GameBoard copy = new GameBoard(original.getCurrentPlayer());
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
char cell = original.getCellValue(i, j);
if (cell != GameBoard.EMPTY) copy.forceMove(i, j, cell);
}
return copy;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
*/
package com.boredxgames.tictactoeclient.domain.services;

import com.boredxgames.tictactoeclient.domain.services.game.GameBoard;

/**
*
* @author sheri
*/
public class MinimaxEngine {

public static int[] bestMove(GameBoard board, char player) {
int bestScore = Integer.MIN_VALUE;
int[] bestMove = null;

char opponent = (player == GameBoard.PLAYER_X) ? GameBoard.PLAYER_O : GameBoard.PLAYER_X;

for (int[] move : board.getAvailableMoves()) {
GameBoard copy = BoardUtils.copy(board);
copy.forceMove(move[0], move[1], player);

int score = minimax(copy, false, 0, player, opponent);
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
}
return bestMove;
}

private static int minimax(GameBoard board, boolean isMax, int depth, char player, char opponent) {
if (board.checkWin(player)) {
return 10 - depth;
}
if (board.checkWin(opponent)) {
return depth - 10;
}
if (board.getAvailableMoves().isEmpty()) {
return 0;
}

int best = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE;

for (int[] move : board.getAvailableMoves()) {
GameBoard copy = BoardUtils.copy(board);
copy.forceMove(move[0], move[1], isMax ? player : opponent);
int score = minimax(copy, !isMax, depth + 1, player, opponent);
best = isMax ? Math.max(best, score) : Math.min(best, score);
}

return best;
}
}
Loading