diff --git a/src/de/itsblue/ConnectFour/ButtonRow.java b/src/de/itsblue/ConnectFour/ButtonRow.java index b1ed645..299c8f5 100644 --- a/src/de/itsblue/ConnectFour/ButtonRow.java +++ b/src/de/itsblue/ConnectFour/ButtonRow.java @@ -113,4 +113,13 @@ public class ButtonRow extends JPanel { super.paint(g); } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + for (int i = 0; i < this.buttonCount; i++) { + this.inputButtons[i].setEnabled(enabled); + } + } } diff --git a/src/de/itsblue/ConnectFour/ConnectFour.java b/src/de/itsblue/ConnectFour/ConnectFour.java index 5556ae7..10dc4d5 100644 --- a/src/de/itsblue/ConnectFour/ConnectFour.java +++ b/src/de/itsblue/ConnectFour/ConnectFour.java @@ -19,13 +19,15 @@ package de.itsblue.ConnectFour; import java.awt.*; +import java.io.Serializable; + import javax.swing.*; import de.itsblue.ConnectFour.Plate.PlateType; import de.itsblue.ConnectFour.player.Player; -import de.itsblue.ConnectFour.player.LocalPlayer; +import de.itsblue.ConnectFour.player.*; -public class ConnectFour extends JFrame { +public class ConnectFour extends JFrame implements PlayerMoveListener { /** * */ @@ -39,6 +41,8 @@ public class ConnectFour extends JFrame { private Player players[] = new Player[2]; + private GameType gameType; + enum GameType { Local, RemoteServer, @@ -75,50 +79,48 @@ public class ConnectFour extends JFrame { // finish up this.pack(); this.setVisible(true); - - this.startNewGame(GameType.Local); } public void startNewGame(GameType type) { + this.gameType = type; switch (type) { case Local: { - this.players[0] = new LocalPlayer(this, this.buttonRow, PlateType.O); - this.players[1] = new LocalPlayer(this, this.buttonRow, PlateType.X); + this.players[0] = new LocalPlayer(this.buttonRow, PlateType.O); + this.players[0].addMoveListener(this); + this.players[1] = new LocalPlayer(this.buttonRow, PlateType.X); + this.players[1].addMoveListener(this); this.player = 0; this.players[player].setIsMyTurn(true); break; } + + case RemoteClient: { + this.players[0] = new LocalPlayer(this.buttonRow, null); + this.players[1] = new RemotePlayerClient(this.buttonRow, "localhost", this.players[0]); + + this.players[0].addMoveListener(this); + this.players[1].addMoveListener(this); - default: break; + } + + case RemoteServer: { + this.players[0] = new LocalPlayer(this.buttonRow, PlateType.O); + this.players[1] = new RemotePlayerServer(this.buttonRow, PlateType.X, this.players[0]); + + this.players[0].addMoveListener(this); + this.players[1].addMoveListener(this); + + this.player = 0; + this.players[player].setIsMyTurn(true); + this.players[1].setIsMyTurn(false); + + break; + } } } - /** - * Function to handle the next plate insertion - * - * @param column The column to insert the plate into - */ - public void insertNextPlate(int column) { - - String res; - - res = this.gameBoard.insertPlate(new Plate(this.players[player].usingPlateType), column); - - if (res == "err") { - // beep in case of error - Toolkit.getDefaultToolkit().beep(); - } else if (res != "ok" && res != "err") { - PlateType winnerType = PlateType.valueOf(res); - System.out.println("A player won: " + winnerType); - } - - switchPlayer(); - - } - - /** * Function to switch the player */ @@ -145,6 +147,29 @@ public class ConnectFour extends JFrame { return this.getSize().height < this.getSize().width; } + /** + * Catch player moves + */ + @Override + public void movePerformed(int column, Player src) { + if(this.players[this.player].equals(src) || this.gameType == GameType.RemoteClient) { + String res; + + res = this.gameBoard.insertPlate(new Plate(src.usingPlateType), column); + + if (res == "err") { + // beep in case of error + Toolkit.getDefaultToolkit().beep(); + } else if (res != "ok" && res != "err") { + PlateType winnerType = PlateType.valueOf(res); + System.out.println("A player won: " + winnerType); + } + + if(this.gameType != GameType.RemoteClient) + switchPlayer(); + } + } + /** * Override validate in order to resacle the components when the window is * rescaled @@ -167,6 +192,19 @@ public class ConnectFour extends JFrame { } public static void main(final String[] args) { - System.out.println(new ConnectFour()); + ConnectFour game; + System.out.println(game = new ConnectFour()); + + System.out.println("argc: " + args.length); + System.out.println("args[0]: " + args[0]); + + if(args.length <= 0) + game.startNewGame(GameType.Local); + else if(args[0].equals("server")) + game.startNewGame(GameType.RemoteServer); + else if(args[0].equals("client")) + game.startNewGame(GameType.RemoteClient); + else + System.out.println("Usage: java ConnectFour.java [server|client]"); } } \ No newline at end of file diff --git a/src/de/itsblue/ConnectFour/player/LocalPlayer.java b/src/de/itsblue/ConnectFour/player/LocalPlayer.java index 67303c0..f42a505 100644 --- a/src/de/itsblue/ConnectFour/player/LocalPlayer.java +++ b/src/de/itsblue/ConnectFour/player/LocalPlayer.java @@ -34,14 +34,11 @@ public class LocalPlayer extends Player implements ActionListener { /** * Constructor * - * @param playingInGame The game the Player is plaing in The funtion - * insertNextPlate() of the game will - * be called when a move is done. * @param controlledByButtonRow The button row used to control the player. * @param usingPlateType The type of plate the player is using. */ - public LocalPlayer(ConnectFour playingInGame, ButtonRow controlledByButtonRow, PlateType usingPlateType) { - super(playingInGame, controlledByButtonRow, usingPlateType); + public LocalPlayer(ButtonRow controlledByButtonRow, PlateType usingPlateType) { + super(controlledByButtonRow, usingPlateType); } /** diff --git a/src/de/itsblue/ConnectFour/player/Player.java b/src/de/itsblue/ConnectFour/player/Player.java index 8f9d5ef..84ed464 100644 --- a/src/de/itsblue/ConnectFour/player/Player.java +++ b/src/de/itsblue/ConnectFour/player/Player.java @@ -19,6 +19,9 @@ package de.itsblue.ConnectFour.player; import de.itsblue.ConnectFour.Plate.*; + +import java.util.ArrayList; + import de.itsblue.ConnectFour.*; /** @@ -29,12 +32,6 @@ import de.itsblue.ConnectFour.*; */ public abstract class Player { - /** - * The game the player is playing in. The funtion insertNextPlate() - * of the game will be called when a move is done. - */ - private ConnectFour playingInGame; - /** * The button row used to control the game. */ @@ -45,6 +42,11 @@ public abstract class Player { */ public PlateType usingPlateType; + /** + * An array containing all move listeners + */ + ArrayList playerMoveListeners = new ArrayList(); + /** * Whether it is this player's turn */ @@ -53,14 +55,10 @@ public abstract class Player { /** * Constructor * - * @param playingInGame The game the Player is plaing in The funtion - * insertNextPlate() of the game - * will be called when a move is done. * @param gameControllingButtonRow The button row used to control the game. * @param usingPlateType The type of plate the player is using. */ - public Player(ConnectFour playingInGame, ButtonRow gameControllingButtonRow, PlateType usingPlateType) { - this.playingInGame = playingInGame; + public Player(ButtonRow gameControllingButtonRow, PlateType usingPlateType) { this.gameControllingButtonRow = gameControllingButtonRow; this.usingPlateType = usingPlateType; } @@ -82,7 +80,30 @@ public abstract class Player { * @param column the column to insert the plate into */ public void doMove(int column) { - if (this.isMyTurn) - this.playingInGame.insertNextPlate(column); + if (this.isMyTurn){ + System.out.println("[LOG] " + Plate.getColor(this.usingPlateType) + " is doing a move in col: " + column); + for (PlayerMoveListener playerMoveListener : playerMoveListeners) { + playerMoveListener.movePerformed(column, this); + } + } + } + + /** + * Function to add a move listener + * + * @param listener the listener to add + */ + public void addMoveListener(PlayerMoveListener listener) { + this.playerMoveListeners.add(listener); + } + + /** + * Function to remove a move listener + * + * @param listener the listener to remove + */ + public void removeMoveListener(PlayerMoveListener listener) { + if(this.playerMoveListeners.contains(listener)) + this.playerMoveListeners.remove(listener); } } \ No newline at end of file diff --git a/src/de/itsblue/ConnectFour/player/PlayerMoveListener.java b/src/de/itsblue/ConnectFour/player/PlayerMoveListener.java new file mode 100644 index 0000000..fc30a2c --- /dev/null +++ b/src/de/itsblue/ConnectFour/player/PlayerMoveListener.java @@ -0,0 +1,5 @@ +package de.itsblue.ConnectFour.player; + +public abstract interface PlayerMoveListener { + public abstract void movePerformed(int column, Player src); +} \ No newline at end of file diff --git a/src/de/itsblue/ConnectFour/player/RemotePlayerClient.java b/src/de/itsblue/ConnectFour/player/RemotePlayerClient.java new file mode 100644 index 0000000..0c96fde --- /dev/null +++ b/src/de/itsblue/ConnectFour/player/RemotePlayerClient.java @@ -0,0 +1,148 @@ +/* + Connect four - written in java + Copyright (C) 2020 Oliver Schappacher and Dorian Zedler + + This program 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 program 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package de.itsblue.ConnectFour.player; + +import java.awt.event.*; +import java.util.Scanner; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import de.itsblue.ConnectFour.Plate.*; +import de.itsblue.ConnectFour.*; + +/** + * LocalPlayer is a class meant for usage with de.itsblue.ConnectFour. It is + * used for a player controlled by the local buttons in the button row. + * + * @author Dorian Zedler + */ +public class RemotePlayerClient extends Player implements PlayerMoveListener { + + private Socket socket; + private Scanner in; + private PrintWriter out; + + private Player opponent; + + /** + * Constructor + * + * @param controlledByButtonRow The button row used to control the player. + */ + public RemotePlayerClient(ButtonRow gameControllingButtonRow, String serverAddress, Player opponent) { + super(gameControllingButtonRow, null); + + this.opponent = opponent; + opponent.addMoveListener(this); + + try { + socket = new Socket(serverAddress, 4444); + in = new Scanner(socket.getInputStream()); + out = new PrintWriter(socket.getOutputStream(), true); + System.out.println("Connected to Connect4 server"); + + // get our plate type + String response = in.nextLine(); + if (response.split(" ")[0].equals("setUsingPlateType")) { + this.usingPlateType = PlateType.valueOf(response.split(" ")[1]); + this.opponent.usingPlateType = this.usingPlateType.equals(PlateType.O) ? PlateType.X : PlateType.O; + } + + // check if it is out turn + response = in.nextLine(); + if (response.split(" ")[0].equals("setIsMyTurn")) { + this.setIsMyTurn(response.split(" ")[1].equals("true")); + opponent.setIsMyTurn(response.split(" ")[1].equals("false")); + } + + // listen to the socket + ExecutorService pool = Executors.newFixedThreadPool(200); + pool.execute(this.new ClientListener(in, this)); + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + public void handleResponse(String response) { + System.out.println("[CLIENT]GOT: " + response); + + if(response.startsWith("setIsMyTurn")) { + this.setIsMyTurn(response.split(" ")[1].equals("true")); + opponent.setIsMyTurn(response.split(" ")[1].equals("false")); + } else if(response.startsWith("movePerformed")) { + this.doMove(Integer.parseInt(response.split(" ")[1])); + } + } + + /** + * Function to set wether it is this player's turn + */ + @Override + public void setIsMyTurn(boolean isMyTurn) { + super.setIsMyTurn(isMyTurn); + + if (isMyTurn) + this.gameControllingButtonRow.setEnabled(false); + else + this.gameControllingButtonRow.setEnabled(true); + + } + + @Override + public void movePerformed(int column, Player src) { + try { + this.out.println("movePerformed " + column); + } catch (Exception e) { + e.printStackTrace(); + } + } + + class ClientListener implements Runnable { + + RemotePlayerClient parent; + Scanner in; + + public ClientListener(Scanner in, RemotePlayerClient parent) { + this.parent = parent; + this.in = in; + } + + @Override + public void run() { + // listen to the socket + try { + while (in.hasNextLine()) { + String response = in.nextLine(); + + this.parent.handleResponse(response); + + } + out.println("QUIT"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } +} \ No newline at end of file diff --git a/src/de/itsblue/ConnectFour/player/RemotePlayerServer.java b/src/de/itsblue/ConnectFour/player/RemotePlayerServer.java new file mode 100644 index 0000000..3498d02 --- /dev/null +++ b/src/de/itsblue/ConnectFour/player/RemotePlayerServer.java @@ -0,0 +1,140 @@ +/* + Connect four - written in java + Copyright (C) 2020 Oliver Schappacher and Dorian Zedler + + This program 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 program 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package de.itsblue.ConnectFour.player; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Arrays; +import java.util.Scanner; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import de.itsblue.ConnectFour.Plate.*; +import de.itsblue.ConnectFour.*; + +/** + * LocalPlayer is a class meant for usage with de.itsblue.ConnectFour. It is + * used for a player controlled by the local buttons in the button row. + * + * @author Dorian Zedler + */ +public class RemotePlayerServer extends Player implements PlayerMoveListener { + + private ServerSocket listener; + private Socket clientSocket; + private Scanner in; + private PrintWriter out; + + private Player opponent; + + /** + * Constructor + * + * @param controlledByButtonRow The button row used to control the player. + * @param usingPlateType The type of plate the player is using. + */ + public RemotePlayerServer(ButtonRow gameControllingButtonRow, PlateType usingPlateType, Player opponent) { + super(gameControllingButtonRow, usingPlateType); + + this.opponent = opponent; + opponent.addMoveListener(this); + + try { + this.listener = new ServerSocket(4444); + System.out.println("Connect4 Server is Running..."); + + this.clientSocket = listener.accept(); + this.in = new Scanner(this.clientSocket.getInputStream()); + this.out = new PrintWriter(this.clientSocket.getOutputStream(), true); + + System.out.println("Client connected"); + + out.println("setUsingPlateType " + this.opponent.usingPlateType.name()); + out.println("setIsMyTurn " + (this.opponent.isMyTurn ? "true":"false")); + + ExecutorService pool = Executors.newFixedThreadPool(200); + pool.execute(this.new ServerListener(in, this)); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void handleResponse(String response) { + System.out.println("GOT: " + response); + if(response.startsWith("movePerformed")) { + this.doMove(Integer.parseInt(response.split(" ")[1])); + } + } + + /** + * Function to set wether it is this player's turn + */ + @Override + public void setIsMyTurn(boolean isMyTurn) { + super.setIsMyTurn(isMyTurn); + + if(isMyTurn) + this.gameControllingButtonRow.setEnabled(false); + else + this.gameControllingButtonRow.setEnabled(true); + + out.println("setIsMyTurn " + (this.isMyTurn ? "false":"true")); + } + + @Override + public void movePerformed(int column, Player src) { + System.out.println("movePerformed"); + try { + this.out.println("movePerformed " + column); + } catch (Exception e) { + e.printStackTrace(); + } + } + + class ServerListener implements Runnable { + + RemotePlayerServer parent; + Scanner in; + + public ServerListener(Scanner in, RemotePlayerServer parent) { + this.parent = parent; + this.in = in; + } + + @Override + public void run() { + // listen to the socket + try { + while (in.hasNextLine()) { + String response = in.nextLine(); + + this.parent.handleResponse(response); + + } + out.println("QUIT"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } +} \ No newline at end of file