java socket tictactoe

package com.board.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

import com.tictactoe.client.TicTacToeConstant;

/**
 * 

多功能分布式三子棋游戏 - 客户端

* * @see TicTacToeConstant * @see TicTacToeServer * @author chuckie * @version v1.01 */ public class TicTacToeClient extends JFrame implements TicTacToeConstant, ActionListener { /** * 缺省串行版本标识 */ private static final long serialVersionUID = 1L; /** * 窗口顶部的玩家基本信息栏文本 */ private JLabel infoLabel = new JLabel(); /** * 窗口顶部的玩家基本信息栏面板 */ private JPanel infoPanel = new JPanel(); /** * 窗口中间的棋盘面板 */ private JPanel gridPanel = new JPanel(); /** * 窗口中间的功能面板 */ private JPanel optionPanel = new JPanel(); /** * 窗口底部的玩家状态信息栏 */ private JLabel stateLabel = new JLabel(); /** * 功能按钮:重开本局 */ private JButton newGameButton = new JButton("重开本局"); /** * 功能按钮:悔棋 */ private JButton retractButton = new JButton("悔棋"); /** * 功能按钮:认输 */ private JButton giveUpButton = new JButton("认输"); /** * 功能按钮:结束 */ private JButton endGameButton = new JButton("结束"); /** * 功能按钮:关于 */ private JButton aboutButton = new JButton("关于"); /** * 保存所按下的按钮 */ private int currentOption; /** * 保存棋盘状态用于实现悔棋功能 */ private char[][][] lastBoard = new char[10][3][3]; /** * 初始化棋盘的所有格子 */ private TicTacToeCell[][] ticTacToeCell = new TicTacToeCell[3][3]; /** * 标识自己(X为玩家1,O为玩家2) */ private char myToken = ' '; /** * 标识对方 */ private char otherToken = ' '; /** * 当棋盘格子状态改变时,变量表选中行号;当功能按键被触发时,变量表状态选中序号(处理请求或处理返回) */ private int rowOrStateSelected; /** * 当棋盘格子状态改变时,变量表列号;当功能按键被触发时,变量表选项序号 */ private int columnOrOptionSelected; /** * 是否发送走棋信息 */ private boolean isSendStep; /** * 是否轮到自己走棋 */ private boolean myTurn = false; /** * 等待玩家选择下一步走棋 */ private boolean waiting = true; /** * 到服务器端的输入输出流 */ private DataInputStream serverInput; private DataOutputStream serverOutput; /** * 是否已经开始 */ private boolean isBeginRun = false; /** * 是否继续游戏 */ private boolean continueToPlay = true; /** * 用于与服务器端连接的IP地址(默认为本地主机,具体由玩家确定) */ private String host = "localhost"; /** * 用于与服务器端连接的端口号信息 */ private int port = 55555; /** * 构造函数 - 创建客户端程序 * * @see TicTacToeClient * @see #joinGame() * @param iniString * 自定义客户端名称 */ public TicTacToeClient(String iniString) { // TODO Auto-generated constructor stub // 初始化窗口 Container container = getContentPane(); // 基本信息栏 infoPanel.add(infoLabel, BorderLayout.CENTER); infoPanel.setBorder(new LineBorder(Color.black, 1)); // 棋盘 gridPanel.setLayout(new GridLayout(3, 3, 0, 0)); int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { gridPanel.add(ticTacToeCell[i][j] = new TicTacToeCell(i, j)); } } // 功能选项栏 optionPanel.setLayout(new GridLayout(5, 1, 0, 0)); optionPanel.add(newGameButton); optionPanel.add(retractButton); optionPanel.add(giveUpButton); optionPanel.add(endGameButton); optionPanel.add(aboutButton); // 窗口布局 container.add(optionPanel, BorderLayout.WEST); container.add(infoPanel, BorderLayout.NORTH); container.add(gridPanel, BorderLayout.CENTER); container.add(stateLabel, BorderLayout.SOUTH); // 显示窗口 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setTitle(iniString); setSize(360, 350); setVisible(true); // 为所有的按钮都设置监听器 newGameButton.addActionListener(this); retractButton.addActionListener(this); giveUpButton.addActionListener(this); endGameButton.addActionListener(this); aboutButton.addActionListener(this); try { // 与服务器端连接,加入棋盘开始游戏 joinGame(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 加入新游戏 * * @see #TicTacToeClient(String) * @see #playGame() * @throws IOException * @throws UnknownHostException * @throws InterruptedException */ private void joinGame() throws UnknownHostException, IOException, InterruptedException { // 与玩家指定的服务器建立连接,默认与本机连接 host = JOptionPane.showInputDialog(null, "请输入服务器IP地址:", "localhost"); Socket connectSocket = new Socket(host, port); // 创建到服务器的输入输出流 serverInput = new DataInputStream(connectSocket.getInputStream()); serverOutput = new DataOutputStream(connectSocket.getOutputStream()); // 开始游戏 playGame(); } /** * 开始游戏 * * @see #joinGame() * @throws IOException * @throws InterruptedException */ private void playGame() throws IOException, InterruptedException { // 从服务器端自己是玩家1还是玩家2 boolean player = serverInput.readBoolean(); // 标识谁先手 boolean goFirst; // 标识玩家1先后手的选择结果 int inputFlag; // 玩家1 if (player == P1) { myToken = 'X'; otherToken = 'O'; infoLabel.setText("玩家1 -> X"); stateLabel.setText("等待玩家2..."); // 接收通知,开始游戏 serverInput.readBoolean(); // 玩家1选择先后手 inputFlag = JOptionPane.showConfirmDialog(null, "是否选择先手:\n是(Y) -> 先手\n否(N) -> 后手", "玩家1选择先后手", JOptionPane.YES_NO_OPTION); // 把玩家1的选择通知服务器 if (inputFlag == JOptionPane.YES_OPTION) { serverOutput.writeBoolean(GO_FIRST_P1); } else { serverOutput.writeBoolean(GO_FIRST_P2); } // 服务器把玩家1的选择通知给两个游戏者 goFirst = serverInput.readBoolean(); // 状态栏显示先后手 if (goFirst == GO_FIRST_P1) { stateLabel.setText("玩家2已进入,玩家1先手"); // 轮到自己 myTurn = true; } else { stateLabel.setText("玩家2已进入,玩家2先手"); } // 玩家2 } else { myToken = 'O'; otherToken = 'X'; infoLabel.setText("玩家2 -> O"); stateLabel.setText("等待玩家1选择先后手..."); // 服务器把玩家1的选择通知给两个游戏者 goFirst = serverInput.readBoolean(); // 状态栏显示先后手 if (goFirst == GO_FIRST_P1) { stateLabel.setText("玩家1先手"); } else { stateLabel.setText("玩家2先手"); // 轮到自己 myTurn = true; } } // 初始化按钮状态 setButtonState(); // 正式开始游戏 isBeginRun = true; while (continueToPlay) { // 玩家1先手情况 if (goFirst == GO_FIRST_P1) { // 玩家1 if (player == P1) { // 等待玩家(自己)的下一步行动(走棋或点击功能按钮),下同 waitForPlayerAction(); // 将走棋信息或点击控制信息发送给服务器 端,下同 sendMoveOrOption(); // 从服务接收反馈信息(另一玩家的走棋信息或对选项请求的回应),下同 receiveInfoFromServer(); // 玩家2 } else { receiveInfoFromServer(); waitForPlayerAction(); sendMoveOrOption(); } // 玩家2先手情况 } else { // 玩家1 if (player == P1) { receiveInfoFromServer(); waitForPlayerAction(); sendMoveOrOption(); // 玩家2 } else { waitForPlayerAction(); sendMoveOrOption(); receiveInfoFromServer(); } } } } /** * 设置按钮状态 - 只有轮到玩家走棋才可以点击功能按钮 * * @see #sendMoveOrOption() * @see #receiveInfoFromServer() */ private void setButtonState() { if (myTurn) { newGameButton.setEnabled(true); retractButton.setEnabled(true); giveUpButton.setEnabled(true); endGameButton.setEnabled(true); } else { newGameButton.setEnabled(false); retractButton.setEnabled(false); giveUpButton.setEnabled(false); endGameButton.setEnabled(false); } } /** * 等待玩家的下一步行动 - 走棋或点击功能按钮 * * @see #playGame() * @throws InterruptedException */ private void waitForPlayerAction() throws InterruptedException { while (waiting) { Thread.sleep(50); } waiting = true; } /** * 将走棋信息或点击控制信息发送给服务器端 * * @see #playGame() * @see #setButtonState() * @see #storeBoard() * @throws IOException */ private void sendMoveOrOption() throws IOException { // 发送行号或状态类型 serverOutput.writeInt(rowOrStateSelected); // 发送列号或选项类型 serverOutput.writeInt(columnOrOptionSelected); // 设置按钮状态 setButtonState(); // 如果发送的是走棋信息则保存棋盘状态(用于实现悔棋功能) if (isSendStep) { storeBoard(); } if (rowOrStateSelected == RETURN_SELECTED) { if (columnOrOptionSelected == RETURN_RETRACT_YES) { // 将整个棋盘发送给服务器端 sendBoardToServer(); } } } /** * 从服务接收反馈信息 * * @see #playGame() * @see #receiveMove() * @see #setButtonState() * @throws IOException */ private void receiveInfoFromServer() throws IOException { // 获取游戏状态或选项状态 int status = serverInput.readInt(); // 玩家1胜利 if (status == WON_P1) { continueToPlay = false; if (myToken == 'X') { stateLabel.setText("我胜了!游戏结束"); } else if (myToken == 'O') { stateLabel.setText("我败了!游戏结束"); receiveMove(); } // 玩家2胜利 } else if (status == WON_P2) { continueToPlay = false; if (myToken == 'O') { stateLabel.setText("我胜了!游戏结束"); } else if (myToken == 'X') { stateLabel.setText("我败了!游戏结束"); receiveMove(); } // 平局 } else if (status == DRAW) { continueToPlay = false; stateLabel.setText("平分秋色!游戏结束"); if (myToken == 'O') { receiveMove(); } // 游戏继续 } else if (status == CONTINUE) { receiveMove(); // 轮到自己下棋 stateLabel.setText("轮到我下棋..."); myTurn = true; // 选项状态:处理对方的请求 } else if (status == OPTION_SELECTED) { // 从服务器读入具体的选择内容 int option = serverInput.readInt(); // 处理选项 handleOption(option); // 选项状态:处理从对方返回的对本方请求的回应 } else if (status == RETURN_SELECTED) { // 从服务器读入具体的返回内容 int isAccept = serverInput.readInt(); // 处理返回 handleReturn(isAccept); } // 设置按钮状态 setButtonState(); } /** * 从服务器端获取对方的下一步走棋 * * @see #receiveInfoFromServer() * @see #storeBoard() * @throws IOException */ private void receiveMove() throws IOException { int row = serverInput.readInt(); int column = serverInput.readInt(); // 更新棋盘状态并显示 ticTacToeCell[row][column].setToken(otherToken); // 保存棋盘状态 storeBoard(); } /** * 处理选项信息 * * @see #receiveInfoFromServer() * @see #resetBoard() * @see #retractBoard() * @param option * 选项信息 * @throws IOException */ private void handleOption(int option) throws IOException { switch (option) { // 重开本局 case OPTION_NEWGAME : int isRestart = JOptionPane.showConfirmDialog(null, "对方请求重新开始,是否同意?", "请求重新开始", JOptionPane.YES_NO_OPTION); if (isRestart == JOptionPane.YES_OPTION) { int reGoFirst = (myToken == 'X') ? 2 : 1; stateLabel.setText("同意对方请求,游戏重新开始,玩家" + reGoFirst + "先手"); // 清空棋盘 resetBoard(); // 生成返回信息 isRestart = RETURN_NEWGAME_YES; } else { isRestart = RETURN_NEWGAME_NO; } rowOrStateSelected = RETURN_SELECTED; columnOrOptionSelected = isRestart; // 发送返回信息 isSendStep = false; waiting = false; break; // 悔棋 case OPTION_RETRACT : int isRetract = JOptionPane.showConfirmDialog(null, "对方请求悔棋,是否同意?", "请求悔棋", JOptionPane.YES_NO_OPTION); if (isRetract == JOptionPane.YES_OPTION) { stateLabel.setText("同意对方请求,返回上一回合状态并继续游戏"); // 恢复上一回合状态 retractBoard(); // 生成返回信息 isRetract = RETURN_RETRACT_YES; } else { isRetract = RETURN_RETRACT_NO; } rowOrStateSelected = RETURN_SELECTED; columnOrOptionSelected = isRetract; // 发送返回信息 isSendStep = false; waiting = false; break; // 认输(无须对方得到对方同意) case OPTION_GIVEUP : continueToPlay = false; myTurn = false; stateLabel.setText("对方认输,我胜了!游戏结束"); break; // 结束 case OPTION_ENDGAME : int isEnd = JOptionPane.showConfirmDialog(null, "对方请求结束,是否同意?", "请求结束", JOptionPane.YES_NO_OPTION); if (isEnd == JOptionPane.YES_OPTION) { stateLabel.setText("同意对方请求,游戏结束"); // 生成返回信息 isEnd = RETURN_ENDGAME_YES; } else { isEnd = RETURN_ENDGAME_NO; } rowOrStateSelected = RETURN_SELECTED; columnOrOptionSelected = isEnd; // 发送返回信息 isSendStep = false; waiting = false; break; default : break; } } /** * 处理返回信息 * * @see #receiveInfoFromServer() * @see #resetBoard() * @see #retractBoard() * @param isAccept * 返回信息 * @throws IOException */ private void handleReturn(int isAccept) throws IOException { // 读取所请求的选项内容 switch (currentOption) { // 重开本局 case OPTION_NEWGAME : // 同意请求 if (isAccept == RETURN_NEWGAME_YES) { continueToPlay = true; myTurn = true; // 清空棋盘 resetBoard(); int reGoFirst = (myToken == 'X') ? 1 : 2; stateLabel.setText("对方同意请求,游戏重新开始,玩家" + reGoFirst + "先手"); // 不同意请求 } else { // 恢复下棋状态 myTurn = true; stateLabel.setText("对方不同意请求,游戏继续"); } break; // 悔棋 case OPTION_RETRACT : if (isAccept == RETURN_RETRACT_YES) { myTurn = true; // 恢复上一回合状态 retractBoard(); stateLabel.setText("对方同意请求,返回上一回合状态并继续游戏"); } else { // 恢复下棋状态 myTurn = true; stateLabel.setText("对方不同意请求,游戏继续"); } break; // 结束 case OPTION_ENDGAME : if (isAccept == RETURN_ENDGAME_YES) { continueToPlay = false; myTurn = false; stateLabel.setText("对方同意请求,游戏结束"); } else { // 恢复下棋状态 myTurn = true; stateLabel.setText("对方不同意请求,游戏继续"); } break; default : break; } } /** * 计算棋盘上有棋子的总格子数 * * @see TicTacToeCell * @see #storeBoard() * @see #retractBoard() * @return 棋盘上有棋子的总格子数 */ private int getNonBlankNumber() { int i, j, sum = 0; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { if (ticTacToeCell[i][j].getToken() != ' ') { ++sum; } } } return sum; } /** * 清空棋盘 * * @see TicTacToeCell * @see #handleOption(int) * @see #handleReturn(int) */ private void resetBoard() { int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { ticTacToeCell[i][j].setToken(' '); } } } /** * 保存棋盘状态 * * @see TicTacToeCell * @see #sendMoveOrOption() * @see #receiveMove() * @see #getNonBlankNumber() */ private void storeBoard() { int sum = getNonBlankNumber(); int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { lastBoard[sum][i][j] = ticTacToeCell[i][j].getToken(); } } } /** * 恢复到上一回合的棋盘状态 * * @see TicTacToeCell * @see #handleOption(int) * @see #handleReturn(int) * @see #getNonBlankNumber() * @see #storeBoard() */ private void retractBoard() { int sum = getNonBlankNumber(); sum -= 2; if (sum < 0) { sum = 0; } int i, j; if (sum != 0) { for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { ticTacToeCell[i][j].setToken(lastBoard[sum][i][j]); } } } else { for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { ticTacToeCell[i][j].setToken(' '); } } } // 保存棋盘状态 storeBoard(); } /** * 将整个棋盘发送给服务器端 * * @see TicTacToeCell * @see #handleOption(int) */ private void sendBoardToServer() throws IOException { int i, j; for (i = 0; i < 3; ++i) { for (j = 0; j < 3; ++j) { serverOutput.writeChar(ticTacToeCell[i][j].getToken()); } } } /** * 响应按钮点击事件 * * @see #newGameResponser() * @see #retractResponser() * @see #giveUpResponser() * @see #endGameResponser() */ public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub // 设置游戏开始前按钮无效 if (!isBeginRun) { return; } // 各种选项的处理 if (e.getSource() == newGameButton) { newGameResponser(); } else if (e.getSource() == retractButton) { retractResponser(); } else if (e.getSource() == giveUpButton) { giveUpResponser(); } else if (e.getSource() == endGameButton) { endGameResponser(); } else if (e.getSource() == aboutButton) { JOptionPane.showMessageDialog(null, "多功能分布式三子棋游戏 TicTacToe v1.01\nBy Johnsysu", "关于", JOptionPane.INFORMATION_MESSAGE); } } /** * 处理重开本局 * * @see #actionPerformed(ActionEvent) */ private void newGameResponser() { // 暂时不能下棋 myTurn = false; stateLabel.setText("等待对方选择..."); // 生成选项信息,下同 rowOrStateSelected = OPTION_SELECTED; columnOrOptionSelected = OPTION_NEWGAME; // 保存选项信息,下同 currentOption = OPTION_NEWGAME; // 发送选项信息,下同 isSendStep = false; waiting = false; } /** * 处理悔棋 * * @see #actionPerformed(ActionEvent) */ private void retractResponser() { // 暂时不能下棋 myTurn = false; stateLabel.setText("等待对方选择..."); rowOrStateSelected = OPTION_SELECTED; columnOrOptionSelected = OPTION_RETRACT; currentOption = OPTION_RETRACT; isSendStep = false; waiting = false; } /** * 处理认输 * * @see #actionPerformed(ActionEvent) */ private void giveUpResponser() { // 结束游戏 continueToPlay = false; myTurn = false; stateLabel.setText("我认输了!游戏结束"); rowOrStateSelected = OPTION_SELECTED; columnOrOptionSelected = OPTION_GIVEUP; currentOption = OPTION_GIVEUP; isSendStep = false; waiting = false; } /** * 处理结束 * * @see #actionPerformed(ActionEvent) */ private void endGameResponser() { // 暂时不能下棋 myTurn = false; stateLabel.setText("等待对方选择..."); rowOrStateSelected = OPTION_SELECTED; columnOrOptionSelected = OPTION_ENDGAME; currentOption = OPTION_ENDGAME; isSendStep = false; waiting = false; } /** *

棋盘格子类 - 客户端内部类

* * @see TicTacToeClient * @author 陈泽维 07302548 网络工程 * @version v1.01 */ private class TicTacToeCell extends JPanel implements MouseListener { /** * 缺省串行版本标识 */ private static final long serialVersionUID = 1L; /** * 表示在棋盘上的位置:行 */ private int row; /** * 表示在棋盘上的位置:列 */ private int column; /** * 表示格子状态 */ private char token = ' '; /** * 构造函数 - 创建棋盘的一个格子 * * @see TicTacToeCell * @param row * 格子所在行 * @param column * 格子所在列 */ public TicTacToeCell(int row, int column) { this.row = row; this.column = column; setBorder(new LineBorder(Color.black, 1)); // 使用鼠标监听器 addMouseListener(this); } /** * 得到状态信息 * * @see TicTacToeClient#getNonBlankNumber() * @see TicTacToeClient#storeBoard() * @see TicTacToeClient#sendBoardToServer() * @return 状态信息 */ public char getToken() { return token; } /** * 设置状态信息 * * @see TicTacToeClient#receiveMove() * @see TicTacToeClient#resetBoard() * @see TicTacToeClient#retractBoard() * @see #mouseClicked(MouseEvent) */ public void setToken(char c) { token = c; // 更新棋盘状态 repaint(); } /** * 显示走棋信息 * * @see #setToken(char) */ protected void paintComponent(Graphics g) { super.paintComponent(g); if (token == 'X') { g.drawLine(10, 10, getWidth() - 10, getHeight() - 10); g.drawLine(getWidth() - 10, 10, 10, getHeight() - 10); } else if (token == 'O') { g.drawOval(10, 10, getWidth() - 20, getHeight() - 20); } } /** * 响应鼠标点击事件 * * @see #setToken(char) */ public void mouseClicked(MouseEvent e) { // 只有轮到自己且格子为空则才可以在该格子上下棋 if ((token == ' ') && myTurn) { // 更新棋盘状态 setToken(myToken); // 生成相应的走棋信息 rowOrStateSelected = row; columnOrOptionSelected = column; // 轮到对方下棋 myTurn = false; stateLabel.setText("等待对方下棋..."); // 发送走棋信息给服务器端 isSendStep = true; waiting = false; } } public void mousePressed(MouseEvent e) { // TODO: implement this java.awt.event.MouseListener method; } public void mouseReleased(MouseEvent e) { // TODO: implement this java.awt.event.MouseListener method; } public void mouseEntered(MouseEvent e) { // TODO: implement this java.awt.event.MouseListener method; } public void mouseExited(MouseEvent e) { // TODO: implement this java.awt.event.MouseListener method; } } /** * 主函数 - 运行客户端程序 * * @see #TicTacToeClient(String) * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub new TicTacToeClient("多功能分布式三子棋游戏 TicTacToe - 客户端"); } }

你可能感兴趣的:(日常练习)