Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
72caf83
Merge branch 'board_func' of https://github.com/cody-malcolm/Ataxx in…
avril2121van Apr 14, 2021
ee19730
Merge pull request #29 board_func -> dev
AvrilLopez Apr 14, 2021
da3a675
Switch to gridpane
cody-malcolm Apr 15, 2021
8b512ac
Merge pull request #30 board_func -> dev
AvrilLopez Apr 15, 2021
c8767e2
More spacing work
cody-malcolm Apr 15, 2021
d8550a8
Merge branch 'dev' of github.com:cody-malcolm/Ataxx into dev
cody-malcolm Apr 15, 2021
243b71f
README and some more comments
mariya0900 Apr 15, 2021
fac2056
Explicit turn indicators
cody-malcolm Apr 15, 2021
5647623
Merge branch 'dev' of github.com:cody-malcolm/Ataxx into dev
cody-malcolm Apr 15, 2021
08f8e88
Merge pull request #31 board_func -> dev
AvrilLopez Apr 15, 2021
d43ed12
Handling game over state on client side, bugfixes
cody-malcolm Apr 15, 2021
a194ef9
Merge branch 'dev' of github.com:cody-malcolm/Ataxx into dev
cody-malcolm Apr 15, 2021
72ae644
Merge pull request #32 board_func -> dev
AvrilLopez Apr 15, 2021
3ff4148
Implement replay and new game features
cody-malcolm Apr 15, 2021
9242d0d
Merge branch 'dev' of github.com:cody-malcolm/Ataxx into dev
cody-malcolm Apr 15, 2021
28ab967
gameid label bugfix
cody-malcolm Apr 15, 2021
51c5d2b
Comments to ClientListener and GameController
mariya0900 Apr 15, 2021
851403e
Merge branch 'dev' of https://github.com/cody-malcolm/Ataxx into dev
mariya0900 Apr 15, 2021
8e11b8c
Merge branch 'dev' of https://github.com/cody-malcolm/Ataxx into dev
avril2121van Apr 15, 2021
9222955
bugfix
cody-malcolm Apr 15, 2021
cae6c33
README update( gameInPlay screenshot)
mariya0900 Apr 15, 2021
1022f79
client side controller comments
cody-malcolm Apr 15, 2021
f38f2a2
comments
avril2121van Apr 15, 2021
846b79e
Merge branch 'dev' of https://github.com/cody-malcolm/Ataxx into dev
avril2121van Apr 15, 2021
24d2ec7
update server controller comments
cody-malcolm Apr 15, 2021
c9c621a
Merge branch 'dev' of github.com:cody-malcolm/Ataxx into dev
cody-malcolm Apr 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ Mariya Anashkina 100746854

Cody Malcolm 10075739

##Project information
## Project information
This application consists of a Server and at least 2 Clients to facilitate the game Ataxx.

###Getting familiar with Ataxx
### Getting familiar with Ataxx
Ataxx is an abstract strategy board game that involves play by two parties on a 7x7 tiled grid.

Each player begins with 2 pieces. The game starts with the 4 pieces on the 4 corners of the board.
Expand All @@ -26,7 +26,7 @@ The object of the game is for a player to secure a greater number of tiles than
To visualize the game, please visit: https://youtu.be/uFyg66dlD2s?t=31


##How to run
## How to run
After cloning or unzipping the project, navigate to the root directory in a terminal. Then:
The server can be started with the following command: ```gradle start```.
The server takes 6 instructions which are described during startup, when an unsupported instruction is received, and when
Expand All @@ -39,7 +39,26 @@ If application has been started correctly, the player will see the screen below.
Hence, if the client is running on the same machine as the server, host IP address can be left empty.

![Alt text](./demos/welcome_screen.png?raw=true "Welcome screen").

## Functionality
* The server supports multiple clients. To start the game, both clients should be connected to the same IP address.
By clicking 'Find the game' button, the player will either be automatically added to the existing game, or he/she will
start a new one, waiting for a second player to connect.

![Alt text](./demos/findGame.png?raw=true "Finding the game").

* The server is the sole source of authority of the game state. It's robust against a modified request from the client.

* Each game is assigned to a unique gameID, allowing the client to spectate said game.

* Username validation happens on the first welcome screen (can't be "", "-" or contain ' \ ' due to the Board string representation)
![Alt text](./demos/username.png?raw=true "Invalid username").

* During the game, the GameID and the number of squares occupied by each player is shown
![Alt text](./demos/gameInPlay.png?raw=true "Game in play").




## Credit
For the layout of the README we took inspiration from Cody's assignment2: https://github.com/cody-malcolm/FileSharer
Expand Down
Binary file modified demos/findGame.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/gameInPlay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/username.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demos/welcome_screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 3 additions & 4 deletions src/main/java/org/amc/ataxx/GameLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ public static boolean validateMove(String board, Pair<Integer, Integer> source,
}

public static boolean isSquareEmpty(String board, Pair<Integer, Integer> square) {
if (getSquare(board, square) == '-')
if (getSquare(board, square) == '-') {
return true;
}
return false;

}
/**
*Returns all square positions that are adjacent to the current square.
Expand Down Expand Up @@ -151,7 +151,7 @@ public static boolean boardFull(String board) {
}
}

return false;
return true;
}


Expand Down Expand Up @@ -213,7 +213,6 @@ public static char checkForWinner(String board) {
if (boardFull(board)){
int numSquares1 = 0;
int numSquares2 = 0;
String[] rows = board.split("\\/");
for (int i = 0; i <= 6; i++) {
for (int j = 0; j <= 6; j++) {
Pair<Integer, Integer> square = new Pair<>(i, j);
Expand Down
125 changes: 110 additions & 15 deletions src/main/java/org/amc/ataxx/client/ClientListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,67 +17,98 @@
public class ClientListener extends Thread {
/** The User associated with the controller */
private User user;

/** Flag to indicate if the Game Scene is being shown yet */
private boolean showingGameScene = false;
/** The Stage the Application is displaying on */
final private Stage stage;
/** The Controller for the Game scene */
private GameController gameController;
/** The Controller for the Splash scene */
private SplashController splashController;

/** The socket used for communications with the server */
private Socket socket = null;
/** The Reader for the input stream */
private BufferedReader in = null;
/** The Writer for the output stream */
private PrintWriter out = null;

/** The Host IP */
private static String HOST = "localhost";
/** The port to use */
final private static int PORT = 25436;

/** The access code to establish a connection */
final private String ACCESS_CODE = "arstdhneio";
/** Flag to indicate if the connection attempt should be aborted due to Application closure */
private boolean abortConnectionAttempt = false;

/**
* Constructor for ClientListener
*
* @param username client's username
* @param stage the stage the application is using
* @param host server's IP
* @param splashController the Controller for the splash screen
*/
public ClientListener(String username, Stage stage, String host, SplashController splashController) {
// set host if one was entered
if (!"".equals(host)) {
this.HOST = host;
}
// initialize user
this.user = new User(username, '0');
// store params
this.stage = stage;
this.splashController = splashController;
}

/**
* Method for establishing the connection with the server
*
* @return true if connected to server
*/
private boolean establishConnection() {
// initialize flag and attempt counter
boolean connected = false;
int attempts = 1;

while (!connected && attempts < 20 && !this.abortConnectionAttempt) {
try {
try { // to connect
this.socket = new Socket(HOST, PORT);
connected = true;
} catch (UnknownHostException e) {
String message = "Attempt " + attempts++ + ": Unknown host: " + HOST;
splashController.giveFeedback(message);
System.err.println(message);
Utils.sleep(1250);
Utils.sleep(1250); // wait to try again
} catch (IOException e) {
String message = "Attempt " + attempts++ + ": Error when attempting to connect to " + HOST;
splashController.giveFeedback(message);
System.err.println(message);
Utils.sleep(1250);
Utils.sleep(1250); // wait to try again
}
}
return connected;
}

/**
* Method performing something similar to TCP handshake
* @return true if successful
*/
private boolean performHandshake() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
} catch (IOException e) {
return false;
}

// send the required details to the server
out.println("CODE\\" + ACCESS_CODE);
out.println("NAME\\" + this.user.getUsername());
return true;
}

/**
* This is what is executed by the thread after we call start() in Main
*/
@Override
public void run() {
if (!establishConnection()) {
Expand All @@ -94,12 +125,14 @@ public void run() {
return;
}

// user is connected now so disable connect button and update flag
this.splashController.disableConnect();
boolean disconnected = false;
splashController.giveFeedback("Successfully connected to " + HOST);

while (!disconnected) {
try {
disconnected = processResponse(in.readLine());
disconnected = processResponse(in.readLine()); // read in responses
} catch (SocketException e) {
disconnected = true;
} catch (IOException e) {
Expand All @@ -108,16 +141,21 @@ public void run() {
}
}

/**
* Handling server responses
*
* @param response from the server
* @return false if the response was not null
*/
private boolean processResponse(String response) {

if (null == response) {
return true;
}

// System.out.println(response); // just during development

String type = response.substring(0, response.indexOf('\\'));

// parse the type of response and send to appropriate handler
if (type.regionMatches(true, 0, "GAME", 0, 4)) {
handleGameResponse(response);
} else if (type.regionMatches(true, 0, "MSG", 0, 3)) {
Expand All @@ -133,40 +171,83 @@ private boolean processResponse(String response) {
return false;
}

/**
* Getting gameInfo from the server. Acts as a trigger to start a new game, if the game is finished.
* Info responses are only sent when someone (player or spectator) joins the game, or a replay is requested by
* both players.
*
* @param response from the server
*/
private void handleInfoResponse(String response) {
String[] args = response.split("\\\\");
// update display names and gameId
user.setDisplayNames(args[0], args[1]);
user.setGameId(args[2]);

// start a new game if requested by both players
if (user.getGameFinished()) {
user.prepNewGame();
}
}

/**
* Initializing GameController
*
* @return true
*/
private boolean showGameScene() {
gameController = new GameController(this.user, this);
return true;
}

/**
* Handles a chat message
*
* @param message the message to display, with 'd'efault styling
*/
private void handleChatMessage(String message) {
gameController.processMessage(message, 'd');
}

/**
* Handles an error message
*
* @param message the message to display, with 'b'old styling
*/
private void handleErrorMessage(String message) {
gameController.processMessage(message, 'b');
}

/**
* Handles a normal message
*
* @param message the message to display, 'i'talicized
*/
private void handleNormalMessage(String message) {
gameController.processMessage(message, 'i');
}

/**
* Handles game response from the server
*
* @param response the game response
*/
private void handleGameResponse(String response) {
// could make this conditional on response if the server later sends information before a game is requested

// updateScene(); ??
// split the response
String[] args = response.split("\\\\");

// get the active player and this user's key (server sole source of authority)
char activePlayer = args[4].charAt(0);
char key = args[5].charAt(0);

// update the board
this.user.setBoard(args[3]);

// update the game status trackers
boolean gameActive = args[7].equals("true");
boolean gameFinished = args[8].equals("true");
this.user.setGameActive(gameActive);
this.user.setGameFinished(gameFinished);

if (!gameActive) {
splashController.giveFeedback("Waiting for opponent...");
Expand All @@ -178,7 +259,7 @@ private void handleGameResponse(String response) {
showingGameScene = showGameScene();
}

if (args[2].equals("none")) {
if (args[2].equals("none")) { // args2 is the move requested, is none at start of game and for resign
this.gameController.refreshBoard(args[3], activePlayer, key, this.user.getDisplayNames(), this.user.getGameId());
} else {
gameController.handleMove(args[1], args[2], args[3], activePlayer, key, this.user.getDisplayNames());
Expand All @@ -193,17 +274,31 @@ private void handleGameResponse(String response) {

}

/**
* Sending request to the server
*
* @param request the request to send
*/
public void sendRequest(String request) {
// guard to prevent NullPointerException when in process of attempting to connect
if (null != out) {
out.println(request);
}
}

/**
* Getter for stage
*
* @return this.stage
*/

public Stage getStage() {
return this.stage;
}

/**
* Closing the socket
*/
public void closeSocket() {
try {
this.socket.close();
Expand Down
Loading