Skip to content

Commit

Permalink
aggiunti client con GUI
Browse files Browse the repository at this point in the history
NB:
- per l'esecuzione serve un JDK con javafx integrato (tipo jdk 1.8)
- ho notato che il random player si spacca per un NullPointerException. Forse è colpa del micro refactor che avevo fatto al metodo checkMove() di [GameAshtonTablut](https://github.com/Gionnino9000/Gionnino9000/blob/93ddf293b3e3fc55e39d811af0ecf5c96390cf1e/Tablut/src/it/unibo/ai/didattica/competition/tablut/domain/GameAshtonTablut.java#L138), in cui avevo rimosso l'utilizzo delle eccezioni
  • Loading branch information
mikyll committed Apr 18, 2024
1 parent 93ddf29 commit f76d419
Show file tree
Hide file tree
Showing 19 changed files with 1,597 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package it.unibo.ai.didattica.competition.tablut.client;

import java.io.IOException;
import java.net.UnknownHostException;

public class TablutHumanBlackClientGui {
public static void main(String[] args) throws UnknownHostException, ClassNotFoundException, IOException {
TablutHumanClientGui.main(new String[]{"Black", "PlayerB", "60", "localhost"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package it.unibo.ai.didattica.competition.tablut.client;

import it.unibo.ai.didattica.competition.tablut.gui.client.Controller;
import it.unibo.ai.didattica.competition.tablut.gui.client.GameInfo;
import it.unibo.ai.didattica.competition.tablut.gui.client.GameInfoDialog;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Alert.AlertType;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.ConnectException;
import java.util.Optional;

/**
* Class for the Ashton Tablut Client with Graphical User Interface.
*
* @author Michele Righi
* (<a href="https://github.com/mikyll">GitHub</a>,
* <a href="https://www.linkedin.com/in/michele-righi/">LinkedIn</a>)
*/
public class TablutHumanClientGui extends Application {
private static String[] args;
private Controller controller;

@Override
public void start(Stage primaryStage) {
boolean invalidArg = false; // Flag that signals if one or more args are invalid
String parameters[] = new String[4];
Optional<GameInfo> gameInfo;

for(int i = 0; i < 4; i++) {
parameters[i] = "";
}

if(args.length >= 1) {
if(GameInfo.validateSide(args[0]))
parameters[0] = args[0];
else invalidArg = true;
}
if(!invalidArg && args.length >= 2) {
if(GameInfo.validateUsername(args[1]))
parameters[1] = args[1];
else invalidArg = true;
}
if(!invalidArg && args.length >= 3) {
if(GameInfo.validateTimeout(args[2]))
parameters[2] = args[2];
else invalidArg = true;
}
if(!invalidArg && args.length >= 4) {
if(GameInfo.validateServerAddress(args[3])) {
parameters[3] = args[3];
}
else invalidArg = true;
}

if(!invalidArg && args.length == 4) {
gameInfo = Optional.of(new GameInfo(parameters[0], parameters[1], parameters[2], parameters[3]));
} else {
GameInfoDialog gameInfoDialog = new GameInfoDialog("Game Info Dialog", "Please enter game details",
parameters[0], parameters[1], parameters[2], parameters[3]);
gameInfo = gameInfoDialog.showAndWait();

}

if(!gameInfo.isPresent()) {
System.exit(0);
}

try {
FXMLLoader fxmlLoader = new FXMLLoader(this.getClass().getResource(Controller.RESOURCE_PATH + "views/viewHumanGuiClient.fxml"));
controller = new Controller(gameInfo.get());
fxmlLoader.setController(controller);
Scene scene = new Scene(fxmlLoader.load(), 464, 500);
scene.getStylesheets().add(this.getClass().getResource(Controller.RESOURCE_PATH + "styles/style.css").toString());
primaryStage.setTitle("Tablut");
primaryStage.setResizable(false);
primaryStage.setScene(scene);

primaryStage.show();
} catch (IOException e) {
if(e instanceof ConnectException) {
Controller.alert(AlertType.ERROR, "Error Dialog", "Connection Error", e.getMessage());
}
e.printStackTrace();
}
}

@Override
public void stop(){
if(controller != null) {
// Stop the client thread on exit
controller.stopClient();
}
}

public static void main(String[] arguments) {
args = arguments;

launch(); // Run the JavaFX thread
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package it.unibo.ai.didattica.competition.tablut.client;

public class TablutHumanWhiteClientGui {
public static void main(String[] args) {
TablutHumanClientGui.main(new String[]{"White", "PlayerW", "60", "localhost"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package it.unibo.ai.didattica.competition.tablut.gui.client;

import it.unibo.ai.didattica.competition.tablut.domain.State;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;

import java.util.ArrayList;
import java.util.List;

/**
* Auxiliary class which provides the allowed destination for a selected pawn.
*
* @author Michele Righi
* (<a href="https://github.com/mikyll">GitHub</a>,
* <a href="https://www.linkedin.com/in/michele-righi/">LinkedIn</a>)
*
* @comment Usage:
* <ol>
* <li>Create an AllowedMoves object (<code>new AllowedMoves(...)</code>) for a given state,
* passing the row and columns of the selected pawn;</li>
* <li>Call <code>getHighlight(...)</code> to get the highlight shape and add it to the JavaFX node;</li>
* <li>Call <code>getAllowedDestinations()</code> to get the list of coordinates where the user can move the selected pawn.</li>
* </ol>
*/
public class AllowedMoves {
private Rectangle verticalHighlight;
private Rectangle horizontalHighlight;
private List<Coordinate> allowedDestinations;

public AllowedMoves(State state, int row, int col) {
this(state, row, col, 48.0);
}
/**
* Create 2 rectangles that highlight the allowed moves only (the cells where the user can move the selected pawn),
* and a list containing the coordinates of the allowed moves for the selected pawn.
* @param state represents the current state of the game
* @param row represents the row (number) of the selected cell
* @param col represents the column (letter) of the selected cell
* @param cellSize is the size of the cell from the selected pawn
*
* @comment the AND/OR chain inside the if conditions is horrible and gives eyes cancer,
* if anyone could find a better solution feel free to edit it :)
*/
public AllowedMoves(State state, int row, int col, double cellSize) {
allowedDestinations = new ArrayList<Coordinate>();

int yVerRect = row;
int xHorRect = col;
int heightVerRect = 1;
int widthHorRect = 1;

// Vertical (same col): top ^
for(int y = row-1; y >= 0; y--) {
if (col == 0 && y == 5 ||
col == 1 && y == 4 ||
col == 3 && y == 0 ||
col == 4 && (y == 4 || y == 1) ||
col == 5 && y == 0 ||
col == 7 && y == 4 ||
col == 8 && y == 5)
break;
if(state.getPawn(y, col).equalsPawn("O")) {
heightVerRect++;
yVerRect--;
allowedDestinations.add(new Coordinate(y, col));
}
else break;
}
// Vertical (same col): bottom v
for(int y = row+1; y < state.getBoard().length; y++) {
if (col == 0 && y == 3 ||
col == 1 && y == 4 ||
col == 3 && y == 8 ||
col == 4 && (y == 4 || y == 7) ||
col == 5 && y == 8 ||
col == 7 && y == 4 ||
col == 8 && y == 3)
break;
if(state.getPawn(y, col).equalsPawn("O")) {
heightVerRect++;
allowedDestinations.add(new Coordinate(y, col));
}
else break;
}
// Horizontal (same row): left <-
for(int x = col-1; x >= 0; x--) {
if (row == 0 && x == 5 ||
row == 1 && x == 4 ||
row == 3 && x == 0 ||
row == 4 && (x == 4 || x == 1) ||
row == 5 && x == 0 ||
row == 7 && x == 4 ||
row == 8 && x == 5)
break;
if(state.getPawn(row, x).equalsPawn("O")) {
widthHorRect++;
xHorRect--;
allowedDestinations.add(new Coordinate(row, x));
}
else break;
}
// Horizontal (same row): right ->
for(int x = col+1; x < state.getBoard().length; x++) {
if (row == 0 && x == 3 ||
row == 1 && x == 4 ||
row == 3 && x == 8 ||
row == 4 && (x == 4 || x == 7) ||
row == 5 && x == 8 ||
row == 7 && x == 4 ||
row == 8 && x == 3)
break;
if(state.getPawn(row, x).equalsPawn("O")) {
widthHorRect++;
allowedDestinations.add(new Coordinate(row, x));
}
else break;
}

verticalHighlight = new Rectangle();
verticalHighlight.setWidth(cellSize);
verticalHighlight.setHeight(cellSize * heightVerRect);
verticalHighlight.setLayoutX(col * cellSize);
verticalHighlight.setLayoutY(yVerRect * cellSize);

horizontalHighlight = new Rectangle();
horizontalHighlight.setWidth(cellSize * widthHorRect);
horizontalHighlight.setHeight(cellSize);
horizontalHighlight.setLayoutX(xHorRect * cellSize);
horizontalHighlight.setLayoutY(row * cellSize);
}

public Shape getHighlight() {
return getHighlight(0.0, Paint.valueOf("lime"));
}
public Shape getHighlight(double offset) {
return getHighlight(offset, Paint.valueOf("lime"));
}
public Shape getHighlight(Paint color) {
return getHighlight(0.0, color);
}
/**
* Creates a shape which combines the 2 highlight rectangles
* @param borderOffset X and Y offset (default is 0.0)
* @param color is the color fill of the rectangles (default is "lime")
* @return a Shape
*/
public Shape getHighlight(double borderOffset, Paint color) {
Shape result = Shape.union(verticalHighlight, horizontalHighlight);
result.setOpacity(0.5);
result.setMouseTransparent(true);
result.setFill(color);
result.setLayoutX(borderOffset);
result.setLayoutY(borderOffset);
return result;
}

/**
* Provide a list containing the coordinates of the allowed destinations for the selected pawn.
* @return a list of Coordinate objects
*/
public List<Coordinate> getAllowedDestinations() {
return allowedDestinations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package it.unibo.ai.didattica.competition.tablut.gui.client;

import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;

/**
* Auxiliary class with methods to build the board coordinates.
*
* @author Michele Righi
* (<a href="https://github.com/mikyll">GitHub</a>,
* <a href="https://www.linkedin.com/in/michele-righi/">LinkedIn</a>)
*/
public class BoardCoordinates {
public static VBox createVerticalCoordinates(int cellCount, double cellSize, double coordSize) {
VBox vboxCoordinates = new VBox();
vboxCoordinates.setPrefHeight(cellSize);
HBox.setMargin(vboxCoordinates, new Insets(0.0, 1.0, 0.0, 1.0));
vboxCoordinates.setAlignment(Pos.TOP_CENTER);

for(int i = 0; i < cellCount; i++) {
Label lRow = new Label("" + (i+1));
lRow.setFont(Font.font("System", FontWeight.BOLD, 20));
lRow.setPrefWidth(coordSize);
lRow.setPrefHeight(cellSize);
lRow.setMinWidth(Control.USE_PREF_SIZE);
lRow.setMinHeight(Control.USE_PREF_SIZE);
lRow.setAlignment(Pos.CENTER);

vboxCoordinates.getChildren().add(lRow);
}

return vboxCoordinates;
}

public static HBox createHorizontalCoordinates(int cellCount, double cellSize, double coordSize) {
HBox hboxCoordinates = new HBox();
hboxCoordinates.setPrefWidth(cellSize);
HBox.setMargin(hboxCoordinates, new Insets(1.0, 0.0, 1.0, 0.0));
hboxCoordinates.setAlignment(Pos.CENTER_LEFT);

for(int i = 0; i < cellCount; i++) {
Label lCol = new Label("" + ((char)(i+65)));
lCol.setFont(Font.font("System", FontWeight.BOLD, 20));
lCol.setPrefWidth(cellSize);
lCol.setPrefHeight(coordSize);
lCol.setMinWidth(Control.USE_PREF_SIZE);
lCol.setMinHeight(Control.USE_PREF_SIZE);
lCol.setAlignment(Pos.CENTER);

hboxCoordinates.getChildren().add(lCol);
}

return hboxCoordinates;
}
}
Loading

0 comments on commit f76d419

Please sign in to comment.