- changed the player approach a bit (using a listener now)

- implemented basic networking (caution, still buggy!)
This commit is contained in:
Dorian Zedler 2020-02-25 02:10:57 +01:00
parent d17ad194e1
commit b36b0f549f
7 changed files with 408 additions and 50 deletions

View file

@ -113,4 +113,13 @@ public class ButtonRow extends JPanel {
super.paint(g); 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);
}
}
} }

View file

@ -19,13 +19,15 @@
package de.itsblue.ConnectFour; package de.itsblue.ConnectFour;
import java.awt.*; import java.awt.*;
import java.io.Serializable;
import javax.swing.*; import javax.swing.*;
import de.itsblue.ConnectFour.Plate.PlateType; import de.itsblue.ConnectFour.Plate.PlateType;
import de.itsblue.ConnectFour.player.Player; 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 Player players[] = new Player[2];
private GameType gameType;
enum GameType { enum GameType {
Local, Local,
RemoteServer, RemoteServer,
@ -75,50 +79,48 @@ public class ConnectFour extends JFrame {
// finish up // finish up
this.pack(); this.pack();
this.setVisible(true); this.setVisible(true);
this.startNewGame(GameType.Local);
} }
public void startNewGame(GameType type) { public void startNewGame(GameType type) {
this.gameType = type;
switch (type) { switch (type) {
case Local: { case Local: {
this.players[0] = new LocalPlayer(this, this.buttonRow, PlateType.O); this.players[0] = new LocalPlayer(this.buttonRow, PlateType.O);
this.players[1] = new LocalPlayer(this, this.buttonRow, PlateType.X); this.players[0].addMoveListener(this);
this.players[1] = new LocalPlayer(this.buttonRow, PlateType.X);
this.players[1].addMoveListener(this);
this.player = 0; this.player = 0;
this.players[player].setIsMyTurn(true); this.players[player].setIsMyTurn(true);
break; break;
} }
default: 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);
break; 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 * Function to switch the player
*/ */
@ -145,6 +147,29 @@ public class ConnectFour extends JFrame {
return this.getSize().height < this.getSize().width; 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 * Override validate in order to resacle the components when the window is
* rescaled * rescaled
@ -167,6 +192,19 @@ public class ConnectFour extends JFrame {
} }
public static void main(final String[] args) { 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]");
} }
} }

View file

@ -34,14 +34,11 @@ public class LocalPlayer extends Player implements ActionListener {
/** /**
* Constructor * Constructor
* *
* @param playingInGame The game the Player is plaing in The funtion
* <code>insertNextPlate()</code> of the game will
* be called when a move is done.
* @param controlledByButtonRow The button row used to control the player. * @param controlledByButtonRow The button row used to control the player.
* @param usingPlateType The type of plate the player is using. * @param usingPlateType The type of plate the player is using.
*/ */
public LocalPlayer(ConnectFour playingInGame, ButtonRow controlledByButtonRow, PlateType usingPlateType) { public LocalPlayer(ButtonRow controlledByButtonRow, PlateType usingPlateType) {
super(playingInGame, controlledByButtonRow, usingPlateType); super(controlledByButtonRow, usingPlateType);
} }
/** /**

View file

@ -19,6 +19,9 @@
package de.itsblue.ConnectFour.player; package de.itsblue.ConnectFour.player;
import de.itsblue.ConnectFour.Plate.*; import de.itsblue.ConnectFour.Plate.*;
import java.util.ArrayList;
import de.itsblue.ConnectFour.*; import de.itsblue.ConnectFour.*;
/** /**
@ -29,12 +32,6 @@ import de.itsblue.ConnectFour.*;
*/ */
public abstract class Player { public abstract class Player {
/**
* The game the player is playing in. The funtion <code>insertNextPlate()</code>
* of the game will be called when a move is done.
*/
private ConnectFour playingInGame;
/** /**
* The button row used to control the game. * The button row used to control the game.
*/ */
@ -45,6 +42,11 @@ public abstract class Player {
*/ */
public PlateType usingPlateType; public PlateType usingPlateType;
/**
* An array containing all move listeners
*/
ArrayList<PlayerMoveListener> playerMoveListeners = new ArrayList<PlayerMoveListener>();
/** /**
* Whether it is this player's turn * Whether it is this player's turn
*/ */
@ -53,14 +55,10 @@ public abstract class Player {
/** /**
* Constructor * Constructor
* *
* @param playingInGame The game the Player is plaing in The funtion
* <code>insertNextPlate()</code> of the game
* will be called when a move is done.
* @param gameControllingButtonRow The button row used to control the game. * @param gameControllingButtonRow The button row used to control the game.
* @param usingPlateType The type of plate the player is using. * @param usingPlateType The type of plate the player is using.
*/ */
public Player(ConnectFour playingInGame, ButtonRow gameControllingButtonRow, PlateType usingPlateType) { public Player(ButtonRow gameControllingButtonRow, PlateType usingPlateType) {
this.playingInGame = playingInGame;
this.gameControllingButtonRow = gameControllingButtonRow; this.gameControllingButtonRow = gameControllingButtonRow;
this.usingPlateType = usingPlateType; this.usingPlateType = usingPlateType;
} }
@ -82,7 +80,30 @@ public abstract class Player {
* @param column the column to insert the plate into * @param column the column to insert the plate into
*/ */
public void doMove(int column) { public void doMove(int column) {
if (this.isMyTurn) if (this.isMyTurn){
this.playingInGame.insertNextPlate(column); 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);
} }
} }

View file

@ -0,0 +1,5 @@
package de.itsblue.ConnectFour.player;
public abstract interface PlayerMoveListener {
public abstract void movePerformed(int column, Player src);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}
}