I made this game equivalent to tictactoe but with a slight twist called find the X between two players using socket programming with Java. Don't know where I went wrong in trying to find the X but for some odd reason my count variable never updates allowing the victory message to never pop up.
Basically the game is as follows, the dealer starts by placing the X then the spotter has to guess where it is by placing the O. Still quite new to Java and socket programming, I followed a few Java socket programming examples which is what led me to the code as shown below:
The client and server uses the .startsWith()
function to fetch the necessary inputs for the corresponding areas. Thus, by using keywords such as "MOVE" alongside with Integer.parseInt(variable.substring())
it is able to get the location on the board.
Server code
package com.findthex;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executors;
public class FindTheXServer {
public static void main(String[] args) throws Exception {
try (var listener = new ServerSocket(4444)) {
System.out.println("Find the X Server is Running...");
var pool = Executors.newFixedThreadPool(200);
while (true) {
Game game = new Game();
pool.execute(game.new Player(listener.accept(), "Dealer"));
pool.execute(game.new Player(listener.accept(), "Spotter"));
}
}
}
}
class Game {
private Player[] board = new Player[3];
Player currentPlayer;
public boolean hasWinner() {
return (board[0] != null || board[1] != null && board[2] != null);
}
public synchronized void move(int location, Player player) {
if (player != currentPlayer) {
throw new IllegalStateException("Not your turn");
} else if (player.opponent == null) {
throw new IllegalStateException("You don't have an opponent yet");
}
board[location] = currentPlayer;
currentPlayer = currentPlayer.opponent;
}
class Player implements Runnable {
String choice;
int rounds, dealerScore, spotterScore, count;
Player opponent;
Socket socket;
Scanner input;
PrintWriter output;
public Player(Socket socket, String choice) {
this.socket = socket;
this.choice = choice;
this.rounds = 1;
this.dealerScore = 0;
this.spotterScore = 0;
this.count = 0;
}
@Override
public void run() {
try {
while (true) {
setup();
processCommands();
count = 1;
rounds = rounds + 1;
if (rounds == 6 ) break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (opponent != null && opponent.output != null) {
opponent.output.println("OTHER_PLAYER_LEFT");
}
try {socket.close();} catch (IOException e) {}
}
}
private void setup() throws IOException {
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(), true);
output.println("Welcome " + choice);
if (choice.equals("Dealer")) {
currentPlayer = this;
output.println("Waiting for opponent to connect.");
} else {
opponent = currentPlayer;
opponent.opponent = this;
opponent.output.println("Game has started! Your move.");
}
}
private void processCommands() {
while (input.hasNextLine()) {
var command = input.nextLine();
if (command.startsWith("Quit")) {
return;
} else if (command.startsWith("Move")) {
processMoveCommand(Integer.parseInt(command.substring(5)));
}
}
}
private void processMoveCommand(int location) {
try {
move(location, this);
output.println("Valid_move");
opponent.output.println("Opponent_picked " + location);
if (count == 1 && hasWinner()) {
output.println("Victory");
opponent.output.println("Defeat");
count = 0;
}
} catch (IllegalStateException e) {
output.println("MESSAGE " + e.getMessage());
}
}
}
}
Client side
package com.findthex;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class FindTheXClient {
private JFrame frame = new JFrame("Find the X");
private JLabel messageLabel = new JLabel("...");
private Square[] board = new Square[3];
private Square currentSquare;
private Socket socket;
private Scanner in;
private PrintWriter out;
public FindTheQueenClient(String serverAddress) throws Exception {
socket = new Socket(serverAddress, 4444);
in = new Scanner(socket.getInputStream());
out = new PrintWriter(socket.getOutputStream(), true);
messageLabel.setBackground(Color.lightGray);
frame.getContentPane().add(messageLabel, BorderLayout.SOUTH);
var boardPanel = new JPanel();
boardPanel.setBackground(Color.black);
boardPanel.setLayout(new GridLayout(1, 3, 3, 3));
for (var i = 0; i < board.length; i++) {
final int j = i;
board[i] = new Square();
board[i].addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
currentSquare = board[j];
out.println("Move " + j);
}
});
boardPanel.add(board[i]);
}
frame.getContentPane().add(boardPanel, BorderLayout.CENTER);
}
public void play() throws Exception {
try {
var response = in.nextLine();
var choice = response.substring(8);
var opponentChoice = choice.equals("Dealer") ? 'O' : 'X';
frame.setTitle("Find the X: Player " + choice);
while (in.hasNextLine()) {
response = in.nextLine();
if (response.startsWith("Valid_move")) {
messageLabel.setText("Valid move, please wait");
currentSquare.setText('X');
currentSquare.repaint();
} else if (response.startsWith("Opponent_picked")) {
var loc = Integer.parseInt(response.substring(16));
board[loc].setText(opponentChoice);
board[loc].repaint();
messageLabel.setText("Opponent moved, your turn");
} else if (response.startsWith("MESSAGE")) {
messageLabel.setText((response.substring(8)));
} else if (response.startsWith("Victory")) {
JOptionPane.showMessageDialog(frame, "Winner Winner Chicken Dinner!");
break;
} else if (response.startsWith("Defeat")) {
JOptionPane.showMessageDialog(frame, "Sorry you lost");
break;
} else if (response.startsWith("OTHER_PLAYER_LEFT")) {
JOptionPane.showMessageDialog(frame, "Other player left");
break;
}
}
out.println("Quit");
} catch (Exception e) {
e.printStackTrace();
} finally {
socket.close();
frame.dispose();
}
}
static class Square extends JPanel {
JLabel label = new JLabel();
public Square() {
setBackground(Color.white);
setLayout(new GridLayout());
label.setFont(new Font("Arial", Font.BOLD, 40));
add(label);
}
public void setText(char text) {
label.setForeground(text == 'X' ? Color.BLUE : Color.RED);
label.setText(text + "");
}
}
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Pass the server IP as the sole command line argument.");
return;
package com.findthex;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executors;
public class FindTheXServer {
public static void main(String[] args) throws Exception {
try (var listener = new ServerSocket(4444)) {
System.out.println("Find the X Server is Running...");
var pool = Executors.newFixedThreadPool(200);
while (true) {
Game game = new Game();
pool.execute(game.new Player(listener.accept(), "Dealer"));
pool.execute(game.new Player(listener.accept(), "Spotter"));
}
}
}
}
class Game {
private Player[] board = new Player[3];
Player currentPlayer;
public boolean hasWinner() {
return (board[0] != null || board[1] != null && board[2] != null);
}
public synchronized void move(int location, Player player) {
if (player != currentPlayer) {
throw new IllegalStateException("Not your turn");
} else if (player.opponent == null) {
throw new IllegalStateException("You don't have an opponent yet");
}
board[location] = currentPlayer;
currentPlayer = currentPlayer.opponent;
}
class Player implements Runnable {
String choice;
int rounds, dealerScore, spotterScore, count;
Player opponent;
Socket socket;
Scanner input;
PrintWriter output;
public Player(Socket socket, String choice) {
this.socket = socket;
this.choice = choice;
this.rounds = 1;
this.dealerScore = 0;
this.spotterScore = 0;
this.count = 0;
}
@Override
public void run() {
try {
while (true) {
setup();
processCommands();
count = 1;
rounds = rounds + 1;
if (rounds == 6 ) break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (opponent != null && opponent.output != null) {
opponent.output.println("OTHER_PLAYER_LEFT");
}
try {socket.close();} catch (IOException e) {}
}
}
private void setup() throws IOException {
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(), true);
output.println("Welcome " + choice);
if (choice.equals("Dealer")) {
currentPlayer = this;
output.println("Waiting for opponent to connect.");
} else {
opponent = currentPlayer;
opponent.opponent = this;
opponent.output.println("Game has started! Your move.");
}
}
private void processCommands() {
while (input.hasNextLine()) {
var command = input.nextLine();
if (command.startsWith("Quit")) {
return;
} else if (command.startsWith("Move")) {
processMoveCommand(Integer.parseInt(command.substring(5)));
}
}
}
private void processMoveCommand(int location) {
try {
move(location, this);
output.println("Valid_move");
opponent.output.println("Opponent_picked " + location);
if (count == 1 && hasWinner()) {
output.println("Victory");
opponent.output.println("Defeat");
count = 0;
}
} catch (IllegalStateException e) {
output.println("MESSAGE " + e.getMessage());
}
}
}
}
}
FindTheXClient client = new FindTheXClient(args[0]);
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setSize(320, 320);
client.frame.setVisible(true);
client.frame.setResizable(false);
client.play();
}
}
I was also trying to adjust the GridLayout
but ended up with | |
layout instead of three boxes in the middle so it can be a bit more presentable to the players. Also trying to implement 6 rounds and
so the game is best out of 5, however, count
and rounds
never seem to update the my run()
function in the server code.
To run the code I used "127.0.0.1" as my serverAddress in IntelliJ.
A major possibility could also be due to the my win condition in the hasWinner()
function.
Main problems:
-
count
androunds
doesn't seem as though they update during therun()
function on server side. -
I can't seem to understand why for some strange reason, it only updates the Dealer's window and not the Spotter's window in Java Swing.
-
Win condition logic isn't quite there for me, after trying to pull all nighters, can't seem to figure out what exactly would be ideal.
-
Layout of the grid doesn't seem to work as expected with
| |
instead of three boxes in the middle.