LoginSignup
Willpower
@Willpower

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

2つのクライアントで同期ができません…

解決したいこと

Javaでオセロゲームを作っています。
クライアント側で実装しているはずの「あなたの番です」「相手の番です」の表示がでません。
また、盤面情報がマッチングしたプレイヤー同士で異なります。

発生している問題・エラー

サーバ)

サーバーが起動しました!
クライアント #0 が接続されました。
クライアント #0 のプレイヤー名: Name
クライアント #0 の制限時間: 180
クライアント #0 のプレイヤー名: Name, 制限時間: 180
クライアント #0 の対戦相手が見つかりませんでした。
クライアント #1 が接続されました。
クライアント #1 のプレイヤー名: Hiriko
クライアント #1 の制限時間: 300
クライアント #1 のプレイヤー名: Hiriko, 制限時間: 300
対戦相手が見つかりました: クライアント #1 とクライアント #0
対戦中: Hiriko vs. Name
クライアント #0 の移動: 34

先手

押されたボタンは対局を開始
Name
180
受信: 相手の名前=Hiriko, 制限時間=240, 手番=0
gameTurn: 0, playerTurn: 0
押されたボタンは34
gameTurn: 1, playerTurn: 0

後手

押されたボタンは対局を開始
Hiriko
300
受信: 相手の名前=Name, 制限時間=240, 手番=1
gameTurn: 0, playerTurn: 1

本来であれば後手のgameTurnも変わっているはずです…

該当するソースコード

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientTest extends JFrame {
    public Client client;

    static int w = 1500;
    static int h = 900;

    private JPanel p_login, p_game, p_end;
    private JLabel l_title, l_1, l_2, l_3;
    private JTextField tf_name;
    private JButton b_start, b_ok;
    private JComboBox<String> comboBox;
    private ThreadX tg;

    // クライアントのメインウィンドウの初期化
    ClientTest() {
        l_title = new JLabel("オセロゲーム");
        l_title.setFont(new Font("明朝体", Font.PLAIN, 110));
        l_1 = new JLabel("名前を入力してください.");
        l_1.setFont(new Font("明朝体", Font.PLAIN, 40));
        l_2 = new JLabel("希望時間");
        l_2.setFont(new Font("明朝体", Font.PLAIN, 40));
        l_3 = new JLabel();
        l_3.setFont(new Font("明朝体", Font.PLAIN, 20));

        tf_name = new JTextField();

        comboBox = new JComboBox<>();
        comboBox.addItem("3分");
        comboBox.addItem("4分");
        comboBox.addItem("5分");
        comboBox.addItem("6分");
        comboBox.addItem("7分");
        comboBox.addItem("8分");
        comboBox.addItem("9分");
        comboBox.addItem("10分");

        b_start = new JButton("対戦を開始");
        b_start.setActionCommand("対局を開始");
        b_start.addActionListener(new ButtonListener());
        b_ok = new JButton("OK");
        b_ok.setActionCommand("OK");
        b_ok.addActionListener(new ButtonListener());

        p_login = new JPanel();
        p_game = new JPanel();
        p_end = new JPanel();

        p_login.setLayout(null);
        p_login.add(l_title);
        p_login.add(l_1);
        p_login.add(tf_name);
        p_login.add(l_2);
        p_login.add(comboBox);
        p_login.add(l_3);
        p_login.add(b_start);
        p_login.add(b_ok);

        l_title.setBounds(400, 10, 1000, 120);
        l_1.setBounds(w / 3, h / 10, w / 2, 180);
        tf_name.setBounds(w / 3 + 30, 230, w / 4, 30);
        l_2.setBounds(w / 3 + 30, 280 - 30, w / 4, 180);
        comboBox.setBounds(w / 2 + 60, h / 3 + 20, w / 15, 40);
        b_start.setBounds(w / 3 + 30, 430, w / 4, 60);
        l_3.setBounds(530, h / 2, w / 3, 180);
        b_ok.setBounds(w / 2 + 60, 520, w / 15, 40);

        b_ok.setVisible(false);

        add(p_login);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(w, h);
        setTitle("ネットワーク対戦型オセロゲーム");
        setVisible(true);
    }

    // ゲーム画面のクラス
    private class Game extends JFrame implements MouseListener {
        private JLabel l_my_name, l_opponent_name, l_vs;
        private JLabel l_my_color, l_turn, l_time;
        private JButton[] buttonArray;
        private ImageIcon blackIcon, whiteIcon, boardIcon;
        private javax.swing.Timer timer;
        private boolean flag_timeout = false;
        private boolean flag_my_timeup = false;
        private boolean flag_opponent_timeup = false;
        private String[] grids;
        int row;
        private Field game;
        private Player player;

        private JButton b_timeout, b_op_timeup;
        private JLabel waitingLabel, turnLabel; // ターン表示ラベルを追加

        // ゲーム画面の初期化
        Game(Field game, Player player) {
            this.game = game;
            this.player = player;
            grids = game.getGrids();
            row = game.getRow();

            whiteIcon = new ImageIcon("White.jpg");
            blackIcon = new ImageIcon("Black.jpg");
            boardIcon = new ImageIcon("Green.jpg");

            l_my_name = new JLabel(player.getMyName());
            l_my_name.setFont(new Font("明朝体", Font.PLAIN, 80));
            l_opponent_name = new JLabel(player.getOpponentName());
            l_opponent_name.setFont(new Font("明朝体", Font.PLAIN, 80));
            l_vs = new JLabel("vs");
            l_vs.setFont(new Font("明朝体", Font.PLAIN, 50));
            l_my_color = new JLabel();
            l_my_color.setFont(new Font("明朝体", Font.PLAIN, 50));
            l_turn = new JLabel();
            l_turn.setFont(new Font("明朝体", Font.PLAIN, 50));
            l_time = new JLabel();
            l_time.setFont(new Font("明朝体", Font.PLAIN, 50));

            buttonArray = new JButton[8 * 8];
            for (int i = 0; i < 64; i++) {
                if (grids[i].equals("0")) {
                    buttonArray[i] = new JButton(blackIcon);
                } else if (grids[i].equals("1")) {
                    buttonArray[i] = new JButton(whiteIcon);
                } else {
                    buttonArray[i] = new JButton(boardIcon);
                }
                p_game.add(buttonArray[i]);
                int x = (i % row) * 54;
                int y = (i / row) * 54;
                buttonArray[i].setBounds(x + 490, y + 150, 54, 54);
                buttonArray[i].addMouseListener(this);
                buttonArray[i].setActionCommand(Integer.toString(i));
            }

            b_timeout = new JButton("timeout");
            p_game.add(b_timeout);
            b_timeout.setBounds(1100, 700, 80, 40);
            b_timeout.addMouseListener(this);
            b_timeout.setActionCommand("timeout");

            b_op_timeup = new JButton("op_timeup");
            p_game.add(b_op_timeup);
            b_op_timeup.setBounds(1100, 740, 80, 40);
            b_op_timeup.addMouseListener(this);
            b_op_timeup.setActionCommand("op_timeup");

            waitingLabel = new JLabel("対戦相手の入力を待っています...", JLabel.CENTER);
            waitingLabel.setFont(new Font("明朝体", Font.PLAIN, 40));
            waitingLabel.setBounds(400, 600, 700, 100);
            p_game.add(waitingLabel);
            waitingLabel.setVisible(false);

            turnLabel = new JLabel("", JLabel.CENTER);
            turnLabel.setFont(new Font("明朝体", Font.PLAIN, 40));
            turnLabel.setBounds(600, 300, 300, 100); // 表示位置を確認
            turnLabel.setVisible(true); // ラベルが可視化されているか確認

            p_game.add(turnLabel);

            p_game.setLayout(null);
            p_game.add(l_my_name);
            p_game.add(l_vs);
            p_game.add(l_opponent_name);
            p_game.add(l_my_color);
            p_game.add(l_turn);
            p_game.add(l_time);

            l_my_name.setBounds(178, 0, 500, 100);
            l_vs.setBounds(678, 15, 100, 60);
            l_opponent_name.setBounds(778, 0, 500, 100);
            l_my_color.setBounds(60, 400, 500, 100);
            l_turn.setBounds(510, 700, 500, 100);
            l_time.setBounds(1000, 400, 500, 100);

            l_my_name.setText(player.getMyName());
            l_opponent_name.setText(player.getOpponentName());

            if (player.getTurn() == 0) {
                l_my_color.setText("あなたは黒です");
            } else {
                l_my_color.setText("あなたは白です");
            }

            timer = new javax.swing.Timer(1000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    updateTimer();
                }
            });
            timer.start();

            // 初期ターン表示の更新
            updateTurnLabel();
        }

        // 盤面を更新する
        private void updateBoard() {
            for (int i = 0; i < row * row; i++) {
                switch (grids[i]) {
                    case "-1":
                        buttonArray[i].setIcon(boardIcon);
                        break;
                    case "0":
                        buttonArray[i].setIcon(blackIcon);
                        break;
                    case "1":
                        buttonArray[i].setIcon(whiteIcon);
                        break;
                    default:
                }
            }
        }

        // タイマーを更新する
        private void updateTimer() {
            // 自分の手番の場合のみ時間を減らす
            if (game.getTurn() == player.getTurn()) {
                player.updateTime(1);
            }
            l_time.setText("残り時間 " + player.getTime() / 60 + "m" + player.getTime() % 60 + "s");
            if (player.isTimeUp()) {
                timer.stop();
                flagMyTimeup();
                l_time.setVisible(false);
                endGame(isTimeout(), myTimeup(), opponentTimeup(), game.countBlack(), game.countWhite());
            }
        }

        // ターン表示を更新する
        private void updateTurnLabel() {
            SwingUtilities.invokeLater(() -> {
                int gameTurn = game.getTurn();
                int playerTurn = player.getTurn();
                System.out.println("gameTurn: " + gameTurn + ", playerTurn: " + playerTurn); // デバッグ出力

                if (gameTurn == playerTurn) {
                    turnLabel.setText("あなたの番です");
                } else {
                    turnLabel.setText("相手の番です");
                }
                turnLabel.setVisible(true);
            });
        }

        private void flagTimeout() {
            flag_timeout = true;
        }

        private void flagMyTimeup() {
            flag_my_timeup = true;
        }

        private void flagOpponentTimeup() {
            flag_opponent_timeup = true;
        }

        public boolean isTimeout() {
            return flag_timeout;
        }

        public boolean myTimeup() {
            return flag_my_timeup;
        }

        public boolean opponentTimeup() {
            return flag_opponent_timeup;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            JButton b_placed = (JButton) e.getComponent();
            String command = b_placed.getActionCommand();

            System.out.println("押されたボタンは" + command);

            if ("timeout".equals(command)) {
                timer.stop();
                flagTimeout();
                l_time.setVisible(false);
                endGame(isTimeout(), myTimeup(), opponentTimeup(), game.countBlack(), game.countWhite());
            } else if ("op_timeup".equals(command)) {
                timer.stop();
                flagOpponentTimeup();
                l_time.setVisible(false);
                endGame(isTimeout(), myTimeup(), opponentTimeup(), game.countBlack(), game.countWhite());
            } else {
                handleMove(command);
            }
        }

        // プレイヤーの移動を処理する
        private void handleMove(String command) {
            if (game.getTurn() != player.getTurn()) {
                return; // 自分の手番でない場合は無視
            }

            int position = Integer.parseInt(command);
            if (game.putStone(position)) { // 有効な手かどうかを確認
                game.getGrids();
                updateBoard();
                client.send(position);

                game.changeTurn();
                updateTurnLabel();
                waitingLabel.setVisible(true);
                p_game.revalidate();
                p_game.repaint();

                new Thread(() -> {
                    int receive = client.receive();
                    boolean turnChanged = client.receiveBoolean(); // ターン変更情報を受信
                    SwingUtilities.invokeLater(() -> {
                        waitingLabel.setVisible(false);
                        if (0 <= receive && receive <= 63) {
                            game.putStone(receive);
                            if (turnChanged) {
                                game.changeTurn();
                                updateTurnLabel();
                            }
                        } else if (receive == 99) {
                            if (turnChanged) {
                                game.changeTurn();
                                updateTurnLabel();
                            }
                            if (!game.putPossibility()) {
                                endGame(false, false, false, game.countBlack(), game.countWhite());
                            }
                        } else if (receive == 100) {
                            endGame(false, false, true, game.countBlack(), game.countWhite());
                        }
                        updateBoard();
                        if (!game.putPossibility()) {
                            game.changeTurn();
                            updateTurnLabel();
                            client.send(99);
                        }
                    });
                }).start();
            } else {
                System.out.println("無効な手です");
            }
        }

        @Override
        public void mouseEntered(MouseEvent e) {}

        @Override
        public void mouseExited(MouseEvent e) {}

        @Override
        public void mousePressed(MouseEvent e) {}

        @Override
        public void mouseReleased(MouseEvent e) {}
    }

    // ゲーム終了画面のクラス
    private class GameEnd extends JFrame {
        private JLabel l_game_end, l_win_lose, l_count_black, l_count_white, l_timeout, l_my_timeup, l_opponent_timeup;
        private JButton b_same_player, b_random_player, b_exit;

        GameEnd(boolean flag_timeout, boolean flag_my_timeup, boolean flag_opponent_timeup, int black, int white) {
            l_game_end = new JLabel("ゲーム終了");
            l_game_end.setFont(new Font("明朝体", Font.PLAIN, 80));
            l_count_black = new JLabel();
            l_count_black.setFont(new Font("明朝体", Font.PLAIN, 40));
            l_count_white = new JLabel();
            l_count_white.setFont(new Font("明朝体", Font.PLAIN, 40));
            l_timeout = new JLabel("対戦相手の接続が切れました");
            l_timeout.setFont(new Font("明朝体", Font.PLAIN, 40));
            l_win_lose = new JLabel("勝利");
            l_win_lose.setFont(new Font("明朝体", Font.PLAIN, 80));
            l_my_timeup = new JLabel("制限時間が無くなりました");
            l_my_timeup.setFont(new Font("明朝体", Font.PLAIN, 40));
            l_opponent_timeup = new JLabel("対戦相手の制限時間が無くなりました");
            l_opponent_timeup.setFont(new Font("明朝体", Font.PLAIN, 40));

            b_same_player = new JButton("同じ相手と再戦する");
            b_same_player.setActionCommand("同じ相手と再戦する");
            b_same_player.addActionListener(new ButtonListener());
            b_random_player = new JButton("再戦する");
            b_random_player.setActionCommand("再戦する");
            b_random_player.addActionListener(new ButtonListener());
            b_exit = new JButton("アプリを終了する");
            b_exit.setActionCommand("アプリを終了する");
            b_exit.addActionListener(new ButtonListener());

            p_end.setLayout(null);
            p_end.add(l_game_end);
            p_end.add(l_count_black);
            p_end.add(l_count_white);
            p_end.add(l_win_lose);
            p_end.add(l_timeout);
            p_end.add(l_my_timeup);
            p_end.add(l_opponent_timeup);
            p_end.add(b_same_player);
            p_end.add(b_random_player);
            p_end.add(b_exit);

            l_game_end.setBounds(w / 3, 10, w / 3, 80);
            l_count_black.setBounds(640, 110, 400, 60);
            l_count_white.setBounds(640, 170, 400, 60);
            l_win_lose.setBounds(640, 270, 200, 90);
            l_timeout.setBounds(450, 360, 800, 60);
            l_my_timeup.setBounds(480, 360, 800, 60);
            l_opponent_timeup.setBounds(380, 360, 800, 60);
            b_same_player.setBounds(520, 440, 400, 60);
            b_random_player.setBounds(520, 520, 400, 60);
            b_exit.setBounds(520, 600, 400, 60);

            l_timeout.setVisible(false);
            l_my_timeup.setVisible(false);
            l_opponent_timeup.setVisible(false);

            if (flag_timeout) {
                b_same_player.setEnabled(false);
                l_timeout.setVisible(true);
            }
            if (flag_my_timeup) {
                l_my_timeup.setVisible(true);
            }
            if (flag_opponent_timeup) {
                l_opponent_timeup.setVisible(true);
            }

            l_count_black.setText("● × " + black);
            l_count_white.setText("〇 × " + white);

            if (flag_timeout || flag_opponent_timeup) {
                l_win_lose.setText("勝利");
            } else if (false) {
                l_win_lose.setText("引分");
            } else if (flag_my_timeup) {
                l_win_lose.setText("敗北");
            }
        }
    }

    private class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JButton theButton = (JButton) e.getSource();
            String command = theButton.getActionCommand();

            System.out.println("押されたボタンは" + command);

            if ("OK".equals(command)) {
                l_3.setVisible(false);
                b_ok.setVisible(false);
                b_start.setEnabled(true);
            }
            if ("対局を開始".equals(command)) {
                if (tf_name.getText().equals("")) {
                    l_3.setVisible(true);
                    b_ok.setVisible(true);
                    l_3.setText("名前が空欄です.");
                } else {
                    b_start.setEnabled(false);
                    l_3.setVisible(true);
                    l_3.setText("対戦相手を探しています.");

                    SwingUtilities.invokeLater(() -> {
                        l_3.setText("対戦相手を探しています.");
                    });

                    new Thread(() -> {
                        System.out.println(tf_name.getText());
                        System.out.println((comboBox.getSelectedIndex() + 3) * 60);

                        client = new Client();
                        client.inque(tf_name.getText(), (comboBox.getSelectedIndex() + 3) * 60);

                        SwingUtilities.invokeLater(() -> {
                            if (client.getTime() == 0) {
                                l_3.setVisible(true);
                                b_ok.setVisible(true);
                                l_3.setText("<html>サーバーが満員です.<br><html>時間を置いて<br><html>もう一度試してください.");
                            } else {
                                Player newPlayer = new Player(client.getTime(), client.getMyName(), client.getOpponentName(), client.getTurn());
                                p_login.setVisible(false);
                                add(p_game);
                                startGame(newPlayer);
                            }
                        });
                    }).start();
                }
            }
            if ("同じ相手と再戦する".equals(command)) {
                client.send(101);
                p_end.setVisible(false);
                p_game.setVisible(true);
                if (client != null) {
                    Player newPlayer = new Player(client.getTime(), client.getMyName(), client.getOpponentName(), client.getTurn());
                    startGame(newPlayer);
                }
            }
            if ("再戦する".equals(command)) {
                client.send(102);
                setVisible(false);
                EventQueue.invokeLater(() -> new ClientTest());
            }
            if ("アプリを終了する".equals(command)) {
                client.send(103);
                System.exit(0);
            }
        }
    }

    private class ThreadX extends Thread {
        Player player;

        ThreadX(Player player) {
            this.player = player;
        }

        public void run() {
            new Game(new Field(), player);
        }
    }

    // ゲームを開始する
    private void startGame(Player player) {
        tg = new ThreadX(player);
        tg.start();
    }

    // ゲームを終了する
    private void endGame(boolean flag_timeout, boolean flag_my_timeup, boolean flag_opponent_timeup, int black, int white) {
        p_game.setVisible(false);
        p_end.setVisible(true);
        add(p_end);
        new GameEnd(flag_timeout, flag_my_timeup, flag_opponent_timeup, black, white);
    }

    // メインメソッド
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> new ClientTest());
    }
}

Java

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

class Client {
    private String server; // サーバーのIPアドレス
    private int port; // サーバーのポート番号

    private Socket socket; // クライアントソケット
    private BufferedOutputStream os; // 出力ストリームのバッファリング
    private DataOutputStream dos; // データ出力ストリーム
    private BufferedInputStream is; // 入力ストリームのバッファリング
    private DataInputStream dis; // データ入力ストリーム

    private String opponentName; // 対戦相手の名前
    private String myName; // 自分の名前
    private int time; // 持ち時間
    private int opponentTime; // 対戦相手の持ち時間
    private int turn; // 自分のターン

    Client() {
        server = "127.0.0.1"; // ローカルホストを指定
        port = 10000; // ポート番号を指定
        initializeConnection(); // 接続を初期化
    }

    // 接続を初期化するメソッド
    private void initializeConnection() {
        try {
            socket = new Socket(server, port); // サーバーに接続
            os = new BufferedOutputStream(socket.getOutputStream()); // 出力ストリームを初期化
            dos = new DataOutputStream(os); // データ出力ストリームを初期化
            is = new BufferedInputStream(socket.getInputStream()); // 入力ストリームを初期化
            dis = new DataInputStream(is); // データ入力ストリームを初期化
        } catch (IOException e) {
            e.printStackTrace(); // エラーが発生した場合、スタックトレースを表示
        }
    }

    // 名前と持ち時間をサーバーに送信するメソッド
    void inque(String s_name, int i_time) {
        try {
            myName = s_name; // 自分の名前を設定
            time = i_time; // 自分の持ち時間を設定

            dos.writeUTF(s_name); // 名前を送信
            dos.writeInt(i_time); // 持ち時間を送信
            dos.flush(); // ストリームをフラッシュ

            // サーバーから相手の情報を受信
            opponentName = dis.readUTF(); // 相手の名前を受信
            opponentTime = dis.readInt(); // 相手の持ち時間を受信
            time = (time + opponentTime) / 2; // 持ち時間の平均を計算
            turn = dis.readInt(); // 手番を受信

            System.out.println("受信: 相手の名前=" + opponentName + ", 制限時間=" + time + ", 手番=" + turn); // デバッグ情報を表示
        } catch (IOException e) {
            e.printStackTrace(); // エラーが発生した場合、スタックトレースを表示
        }
    }

    // サーバーにデータを送信するメソッド
    void send(int num) {
        try {
            if (0 <= num && num <= 63) {
                dos.writeInt(num); // 有効なデータを送信
                dos.flush(); // ストリームをフラッシュ
            } else {
                if (num >= 99) {
                    dos.writeInt(num); // 特殊なデータを送信
                    dos.flush(); // ストリームをフラッシュ
                } else {
                    throw new NumberException("サーバーが処理できない数値を送信しようとしています。"); // 無効なデータを送信しようとした場合に例外をスロー
                }
            }
        } catch (IOException e) {
            e.printStackTrace(); // エラーが発生した場合、スタックトレースを表示
        }
    }

    // データ受信
    int receive() {
        int num = -1; // 初期値を設定
        try {
            num = dis.readInt(); // データを受信
            if (num < 0 || (num > 63 && num < 99)) {
                throw new NumberException("サーバーより異常な値が返されました。"); // 無効なデータを受信した場合に例外をスロー
            }
            System.out.println("サーバーから受信: " + num); // デバッグ情報を表示
        } catch (IOException e) {
            e.printStackTrace(); // エラーが発生した場合、スタックトレースを表示
        }
        return num; // 受信したデータを返す
    }

    // boolean値を受信するメソッドを追加
    boolean receiveBoolean() {
        boolean value = false; // 初期値を設定
        try {
            value = dis.readBoolean(); // boolean値を受信
            System.out.println("サーバーから受信: " + value); // デバッグ情報を表示
        } catch (IOException e) {
            e.printStackTrace(); // エラーが発生した場合、スタックトレースを表示
        }
        return value; // 受信したboolean値を返す
    }

    // 持ち時間を取得するメソッド
    int getTime() {
        return time;
    }

    // 相手の名前を取得するメソッド
    String getOpponentName() {
        return opponentName;
    }

    // 自分の名前を取得するメソッド
    String getMyName() {
        return myName;
    }

    // 自分のターンを取得するメソッド
    int getTurn() {
        return turn;
    }

    // 接続を閉じるメソッド
    void close() {
        try {
            if (dis != null) dis.close(); // 入力ストリームをクローズ
            if (dos != null) dos.close(); // 出力ストリームをクローズ
            if (socket != null) socket.close(); // ソケットをクローズ
        } catch (IOException e) {
            e.printStackTrace(); // エラーが発生した場合、スタックトレースを表示
        }
    }
}

// 数値に関する例外クラス
class NumberException extends RuntimeException {
    public NumberException(String msg) {
        super(msg);
    }
}

Java

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

public class Field {
    private int row = 8; // オセロ盤の縦横マス数(2の倍数のみ)
    private String[] grids = new String[row * row]; // 局面情報
    private int counter_black, counter_white; // 石のカウンタ用
    private Integer turn; // 手番

    // コンストラクタ
    public Field() {
        turn = 0; // 黒が先手
        for (int i = 0; i < row * row; i++) {
            grids[i] = "-1"; // 初めは石が置かれていない
        }
        int center = row * row / 2;
        grids[center - row / 2 - 1] = "0";
        grids[center + row / 2] = "0";
        grids[center - row / 2] = "1";
        grids[center + row / 2 - 1] = "1";
    }

    // 黒を数える
    public Integer countBlack() {
        counter_black = 0; // カウンターをリセット
        for (String grid : grids) {
            if (grid.equals("0")) counter_black++;
        }
        return counter_black;
    }

    // 白を数える
    public Integer countWhite() {
        counter_white = 0; // カウンターをリセット
        for (String grid : grids) {
            if (grid.equals("1")) counter_white++;
        }
        return counter_white;
    }

    // 勝敗を判断
    public String checkWinner() {
        countBlack();
        countWhite();
        if (counter_black > counter_white) {
            return "black";
        } else if (counter_white > counter_black) {
            return "white";
        } else {
            return "draw";
        }
    }

    // 手番情報を取得
    public Integer getTurn() {
        return turn;
    }

    // 局面情報を取得
    public String[] getGrids() {
        return grids;
    }

    // 手番を変更
    public void changeTurn() {
        turn = 1 - turn;
    }

    // 対局終了を判断
    public boolean isGameover() {
        for (String grid : grids) {
            if (grid.equals("-1")) {
                return false;
            }
        }
        return true;
    }

    // (操作を)局面に反映
    public boolean putStone(Integer i) {
        int x = i / 8;
        int y = i % 8;
        if (i < 0 || i >= 64 || !grids[i].equals("-1")) {
            return false; // 置けない場合はfalseを返す
        }

        boolean validMove = false; // 有効な手かどうか
        int opponent = 1 - turn; // 相手プレイヤーの石
        int[] dx = { -1, 0, 1, -1, 1, -1, 0, 1 }; // 探索方向(x)
        int[] dy = { -1, -1, -1, 0, 0, 1, 1, 1 }; // 探索方向(y)
        List<Point> flippingPoints = new ArrayList<>(); // ひっくり返す石の位置リスト

        // 全方向を探索
        for (int d = 0; d < 8; d++) {
            int nx = x + dx[d], ny = y + dy[d];
            int count = 0;
            while (nx >= 0 && ny >= 0 && nx < 8 && ny < 8 && grids[8 * nx + ny].equals(String.valueOf(opponent))) {
                nx += dx[d];
                ny += dy[d];
                count++;
            }
            if (count > 0 && nx >= 0 && ny >= 0 && nx < 8 && ny < 8 && grids[8 * nx + ny].equals(String.valueOf(turn))) {
                validMove = true;
                while (nx != x || ny != y) {
                    nx -= dx[d];
                    ny -= dy[d];
                    flippingPoints.add(new Point(nx, ny));
                }
            }
        }

        // 有効な手の場合、石をひっくり返す
        if (validMove) {
            for (Point p : flippingPoints) {
                grids[8 * p.x + p.y] = turn.toString();
            }
            grids[8 * x + y] = turn.toString();
        }
        return validMove;
    }

    // 有効な手があるかどうかを判断
    public boolean putPossibility() {
        for (int i = 0; i < 64; i++) {
            int x = i / 8;
            int y = i % 8;
            if (i < 0 || i >= 64 || !grids[i].equals("-1")) {
                continue;
            }

            int opponent = 1 - turn; // 相手プレイヤーの石
            int[] dx = { -1, 0, 1, -1, 1, -1, 0, 1 }; // 探索方向(x)
            int[] dy = { -1, -1, -1, 0, 0, 1, 1, 1 }; // 探索方向(y)

            // 全方向を探索
            for (int d = 0; d < 8; d++) {
                int nx = x + dx[d], ny = y + dy[d];
                int count = 0;
                while (nx >= 0 && ny >= 0 && nx < 8 && ny < 8 && grids[8 * nx + ny].equals(String.valueOf(opponent))) {
                    nx += dx[d];
                    ny += dy[d];
                    count++;
                }
                if (count > 0 && nx >= 0 && ny >= 0 && nx < 8 && ny < 8 && grids[8 * nx + ny].equals(String.valueOf(turn))) {
                    return true;
                }
            }
        }
        return false;
    }

    // 縦横のマス数を取得
    public int getRow() {
        return row;
    }
}

Java

public class Player {
    private int time; // 持ち時間
    private String myName; // プレイヤーの名前
    private String opponentName; // 対戦相手の名前
    private int turn; // プレイヤーの手番

    // コンストラクタ
    public Player(int time, String myName, String opponentName, int turn) {
        this.time = time;
        this.myName = myName;
        this.opponentName = opponentName;
        this.turn = turn;
    }

    // 名前の取得
    public String getMyName() {
        return myName;
    }

    // 相手の名前の取得
    public String getOpponentName() {
        return opponentName;
    }

    // 手番の取得
    public int getTurn() {
        return turn;
    }

    // 残り時間の取得
    public int getTime() {
        return time;
    }

    // 時間の更新
    public void updateTime(int seconds) {
        time -= seconds;
    }

    // 時間切れかどうかの判定
    public boolean isTimeUp() {
        return time <= 0;
    }
}

Java

import java.io.*;
import java.net.*;

class MyServer {
    private static final int maxConnection = 100; // 最大接続数
    private static ServerSocket serverSocket; // サーバーソケット
    private static Socket[] clients = new Socket[maxConnection]; // クライアントソケットの配列
    private static ClientProcThread[] clientThreads = new ClientProcThread[maxConnection]; // クライアントスレッドの配列
    private static boolean[] connectionFlags = new boolean[maxConnection]; // 接続状態を示すフラグ
    private static boolean[] readyFlags = new boolean[maxConnection]; // 準備完了状態を示すフラグ
    private static boolean[] matchedFlags = new boolean[maxConnection]; // 対戦相手が見つかったかを示すフラグ
    private static String[] playerNames = new String[maxConnection]; // プレイヤー名の配列
    private static int[] timeLimits = new int[maxConnection]; // 持ち時間の配列
    private static Field[] fields = new Field[maxConnection / 2]; // 対戦ごとのField配列

    public static void main(String[] args) {
        try {
            serverSocket = new ServerSocket(10000); // ポート番号10000でサーバーソケットを作成
            System.out.println("サーバーが起動しました!");
            while (true) {
                Socket client = serverSocket.accept(); // クライアントの接続を受け入れ
                int clientNumber = findAvailableClientNumber(); // 利用可能なクライアント番号を取得
                if (clientNumber == -1) {
                    rejectClient(client); // クライアントが満員の場合は拒否
                } else {
                    acceptClient(client, clientNumber); // クライアントを受け入れ
                }
            }
        } catch (IOException e) {
            System.err.println("サーバーの起動に失敗しました: " + e.getMessage());
        } finally {
            closeServerSocket(); // サーバーソケットを閉じる
        }
    }

    // 利用可能なクライアント番号を探す
    private static int findAvailableClientNumber() {
        for (int i = 0; i < maxConnection; i++) {
            if (!connectionFlags[i]) {
                return i;
            }
        }
        return -1;
    }

    // 対戦相手のクライアント番号を取得
    public static int getOpponentNumber(int number) {
        return number % 2 == 0 ? number + 1 : number - 1;
    }

    // クライアントを拒否する
    private static void rejectClient(Socket client) {
        try (DataOutputStream out = new DataOutputStream(client.getOutputStream())) {
            out.writeInt(0); // 拒否のシグナルを送信
        } catch (IOException e) {
            System.err.println("クライアントの拒否に失敗しました: " + e.getMessage());
        } finally {
            try {
                client.close(); // クライアントソケットを閉じる
            } catch (IOException e) {
                System.err.println("クライアントソケットのクローズに失敗しました: " + e.getMessage());
            }
        }
    }

    // クライアントを受け入れる
    private static void acceptClient(Socket client, int clientNumber) throws IOException {
        clients[clientNumber] = client; // クライアントソケットを保存
        connectionFlags[clientNumber] = true; // 接続フラグを設定
        DataInputStream in = new DataInputStream(client.getInputStream()); // 入力ストリームを作成
        clientThreads[clientNumber] = new ClientProcThread(clientNumber, client, in); // クライアントスレッドを作成
        clientThreads[clientNumber].start(); // クライアントスレッドを開始
        System.out.println("クライアント #" + clientNumber + " が接続されました。");
    }

    // プレイヤーの準備完了を通知する
    public static void notifyPlayerReady(int clientNumber, String playerName, int timeLimit) {
        readyFlags[clientNumber] = true; // 準備完了フラグを設定
        playerNames[clientNumber] = playerName; // プレイヤー名を保存
        timeLimits[clientNumber] = timeLimit; // 持ち時間を保存
        System.out.println("クライアント #" + clientNumber + " のプレイヤー名: " + playerName + ", 制限時間: " + timeLimit);

        int opponentNumber = findWaitingOpponent(clientNumber); // 対戦相手を探す
        if (opponentNumber != -1) {
            matchedFlags[clientNumber] = true; // 対戦相手が見つかったフラグを設定
            matchedFlags[opponentNumber] = true; // 対戦相手が見つかったフラグを設定
            try {
                // 新しく共有のFieldを作成
                Field sharedField = new Field();
                fields[clientNumber / 2] = sharedField; // 対戦のFieldを保存

                // クライアントスレッドにFieldを渡す
                clientThreads[clientNumber].setField(sharedField);
                clientThreads[opponentNumber].setField(sharedField);

                sendInitialDataToBothPlayers(clientNumber, opponentNumber); // 両プレイヤーに初期データを送信
                printGameLog(clientNumber, opponentNumber); // ゲームログを出力
            } catch (IOException e) {
                System.err.println("初期データの送信に失敗しました: " + e.getMessage());
            }
        } else {
            System.out.println("クライアント #" + clientNumber + " の対戦相手が見つかりませんでした。");
        }
    }

    // 待機中の対戦相手を探す
    private static int findWaitingOpponent(int clientNumber) {
        for (int i = 0; i < maxConnection; i++) {
            if (i != clientNumber && connectionFlags[i] && readyFlags[i] && !matchedFlags[i]) {
                System.out.println("対戦相手が見つかりました: クライアント #" + clientNumber + " とクライアント #" + i);
                return i;
            }
        }
        return -1;
    }

    // 両プレイヤーに初期データを送信
    private static void sendInitialDataToBothPlayers(int clientNumber, int opponentNumber) throws IOException {
        String playerName = playerNames[clientNumber];
        int timeLimit = timeLimits[clientNumber];
        String opponentName = playerNames[opponentNumber];
        int opponentTimeLimit = timeLimits[opponentNumber];

        sendNameTime(playerName, timeLimit, opponentNumber, 0); // clientNumber is black (先手)
        sendNameTime(opponentName, opponentTimeLimit, clientNumber, 1); // opponentNumber is white (後手)
    }

    // プレイヤーに名前と時間を送信
    public static void sendNameTime(String name, int time, int number, int turn) throws IOException {
        if (connectionFlags[number] && clients[number] != null) {
            DataOutputStream out = new DataOutputStream(clients[number].getOutputStream());
            out.writeUTF(name);
            out.writeInt(time);
            out.writeInt(turn);
            out.flush();
        }
    }

    // 盤面情報を送信
    public static void sendBoardInfo(int boardInfo, int number, boolean turnChanged) throws IOException {
        if (connectionFlags[number] && clients[number] != null) {
            DataOutputStream out = new DataOutputStream(clients[number].getOutputStream());
            out.writeInt(boardInfo);
            out.writeBoolean(turnChanged);
            out.flush();
        }
    }

    // 接続エラーを処理
    public static void handleConnectionError(int number) {
        int opponentNumber = getOpponentNumber(number);
        try {
            if (connectionFlags[opponentNumber] && clients[opponentNumber] != null) {
                try (DataOutputStream out = new DataOutputStream(clients[opponentNumber].getOutputStream())) {
                    out.writeUTF("対戦相手が切断されました。");
                    out.flush();
                }
            }
        } catch (IOException e) {
            System.err.println("切断された対戦相手に通知するのに失敗しました: " + e.getMessage());
        }
        setClientInactive(number);
        setClientInactive(opponentNumber);
    }

    // クライアントを非アクティブに設定
    public static void setClientInactive(int number) {
        connectionFlags[number] = false;
        readyFlags[number] = false;
        matchedFlags[number] = false;
        playerNames[number] = null;
        timeLimits[number] = 0;
        try {
            if (clients[number] != null) {
                clients[number].close();
                clients[number] = null;
                System.out.println("クライアント #" + number + " の接続を閉じました");
            }
        } catch (IOException e) {
            System.err.println("クライアント #" + number + "のリソースを閉じるのに失敗しました: " + e.getMessage());
        }
    }

    // サーバーソケットを閉じる
    private static void closeServerSocket() {
        try {
            if (serverSocket != null) {
                serverSocket.close();
            }
        } catch (IOException e) {
            System.err.println("サーバーソケットのクローズに失敗しました: " + e.getMessage());
        }
    }

    // ゲームログを出力
    public static void printGameLog(int myNum, int oppNum) {
        String playerName1 = clientThreads[myNum].getPlayerName();
        String playerName2 = clientThreads[oppNum].getPlayerName();
        System.out.printf("対戦中: %s vs. %s%n", playerName1, playerName2);
    }
}

Java

import java.io.*;
import java.net.*;
class ClientProcThread extends Thread {
    private int number; // クライアント番号
    private String playerName; // プレイヤー名
    private Socket incoming; // クライアントのソケット
    private DataInputStream myIn; // 入力ストリーム
    private boolean running = true; // スレッドが実行中かどうかを示すフラグ
    private Field field; // 共有のFieldオブジェクト

    // コンストラクタ
    public ClientProcThread(int n, Socket i, DataInputStream in) {
        number = n; // クライアント番号を設定
        incoming = i; // クライアントのソケットを設定
        myIn = in; // 入力ストリームを設定
    }

    // スレッドのメイン処理
    public void run() {
        try {
            // クライアントからプレイヤー名を受信
            playerName = myIn.readUTF();
            System.out.println("クライアント #" + number + " のプレイヤー名: " + playerName);

            // クライアントから制限時間を受信
            int timeLimit = myIn.readInt();
            System.out.println("クライアント #" + number + " の制限時間: " + timeLimit);

            // プレイヤーが準備完了したことをサーバーに通知
            MyServer.notifyPlayerReady(number, playerName, timeLimit);

            // クライアントからのデータ受信と処理をループ
            while (running) {
                // クライアントからプレイヤーの移動を受信
                int move = myIn.readInt();
                System.out.println("クライアント #" + number + " の移動: " + move);

                // 有効な移動の場合、相手プレイヤーに盤面情報とターン変更情報を送信
                if (move >= 0 && move <= 63) {
                    synchronized (field) {
                        if (field.putStone(move)) {
                            MyServer.sendBoardInfo(move, getOpponentNumber(), true); // ターン変更あり
                            field.changeTurn();
                        }
                    }
                } else if (move >= 99) {
                    MyServer.sendBoardInfo(move, getOpponentNumber(), false); // ターン変更なし
                    running = false; // セッション終了
                }
            }
        } catch (IOException e) {
            // 例外が発生した場合のエラーメッセージを出力
            System.out.println("クライアント #" + number + " の処理中にエラーが発生しました: " + e.getMessage());
            // 接続エラーをサーバーに通知
            MyServer.handleConnectionError(number);
        } finally {
            // リソースをクローズ
            closeResources();
        }
    }

    // 対戦相手のクライアント番号を取得
    private int getOpponentNumber() {
        return MyServer.getOpponentNumber(number); // 対戦相手の番号を計算
    }

    // プレイヤー名を取得
    public String getPlayerName() {
        return playerName;
    }

    // Fieldオブジェクトを設定
    public void setField(Field field) {
        this.field = field;
    }

    // リソースをクローズ
    private void closeResources() {
        try {
            myIn.close(); // 入力ストリームをクローズ
            incoming.close(); // クライアントのソケットをクローズ
        } catch (IOException e) {
            // リソースを閉じる際にエラーが発生した場合のメッセージを出力
            System.out.println("クライアント #" + number + " のリソースを閉じる際にエラーが発生しました");
        }
    }
}

自分で試したこと

上記の結果のように、標準出力に出すようにし、どこまで確実に操作が進んでいるのかまでは確認しました。ただ、ターンの表示までは確認できていません。

0

1Answer

上記の結果のように、標準出力に出すようにし、どこまで確実に操作が進んでいるのかまでは確認しました。

どこまで進んでいますか?
どのような根拠でそこまで進んでいると判断しましたか?

ただ、ターンの表示までは確認できていません。

確認してみてください。
確認できない理由があれば提示してください。

0

Comments

  1. @Willpower

    Questioner

    midoribi様
    ご回答頂きまして有難うございます。

    どこまで進んでいますか?
    どのような根拠でそこまで進んでいると判断しましたか?

    → サーバが先手クライアントの入力を受け付けている&先手クライアントのgameTurnは変更されている&後手クライアントのgameTurnが変化していない部分まで進んでいると認識しております。その根拠といたしましては、
    発生している問題・エラー
    の部分で添付させて頂きました標準出力結果からになります。

    確認してみてください。
    確認できない理由があれば提示してください。

    → 記載不足大変失礼いたしました。厳密にはgameTurnplayerTurnに関しましては確認を行いました。条件として、それらが一致していれば「あなたの番です」が表示され、一致していなければ「相手の番です」となるようにしておりました。その為、条件を満たしていることは上記標準出力を基に確認できていたのですが、その後、なぜ表示されないのかまでは原因・進んでいる部分を確認できないという意図で記載させて頂いておりました。

    何卒宜しくお願いいたします。

  2. @Willpower

    Questioner

    こちらの問題に関し、自分なりに解決の糸口が見えました為、クローズさせて頂きます。お手数をおかけいたしました。

  3. で?どうやって、解決したの?

Your answer might help someone💌