Java五子棋(局域网)

Java五子棋(局域网)_第1张图片

 

先画出棋盘界面:

class Gb  extends JFrame implements MouseListener ,Runnable {
	BufferedWriter bw = null;
	BufferedReader br = null;//文档的流
    Chess[] chesses = new Chess[17 * 17];//棋子数组,每个网格一个数组
	TreeMap tm = new TreeMap<>();//key为序号 value为坐标对应的数组index
	TreeMap tmTemp;//读取进度时用,点击加载覆盖tm
	static int width = Toolkit.getDefaultToolkit().getScreenSize().width;
	static int height = Toolkit.getDefaultToolkit().getScreenSize().height;//获取屏幕的分辨率
    boolean isReading = false;
	boolean upCanPress = true;
	boolean downCanPress = false;//用于读取数据的三个变量

    public Gb() {
		this.setTitle("五子棋游戏完成版");
		this.setSize(900, 800);
		this.setLocation((width - 800) / 2, (height - 800) / 2 );
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(false);
		this.setVisible(true);
		this.repaint();
		this.addMouseListener(this);
		
	}

public void paint(Graphics g) {
		BufferedImage buf = new BufferedImage(900, 800, BufferedImage.TYPE_INT_RGB);//确定界面的大小,以及涂色方式为RGB
		Graphics g1 = buf.createGraphics();

            //选项
			
			//背景和棋盘底色
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//背景颜色
			g1.fill3DRect(0, 0, 900, 800, true);//绘色:(起始坐标X,起始坐标Y,结束坐标X,结束坐标Y)
			g1.setColor(new Color(235,177,13));//棋盘色
			g1.fill3DRect(60, 60, 680, 680, true);
			
			//棋盘线
			for (int i = 80; i <= 720; i += 40) {
				g1.setColor(Color.BLACK);
				g1.drawLine(80, i, 720, i);
				g1.drawLine(i, 80, i, 720);
			}
			
			//右上角黑白棋
            //用于提醒下一棋子是什么颜色
			if(isBlack) {
				g1.setColor(Color.BLACK);
				g1.fillOval(770, 50, 30, 30);
			}else {
				g1.setColor(Color.WHITE);
				g1.fillOval(770, 50, 30, 30);
			}
			
			//音乐键
            //图片在资源文件夹中,打开放在项目文件下
			try {
				imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			g1.drawImage(imageMusic,820,47,null);
			
			//功能按键
			g1.setFont(new Font("微软雅黑",Font.BOLD,15));
			g1.setColor(new Color(158,204,171));
			g1.fill3DRect(770, 100, 100, 40,true);
			g1.fill3DRect(770, 170, 100, 40,true);
			g1.fill3DRect(770, 240, 100, 40,true);
			g1.fill3DRect(770, 310, 100, 40,true);
			g1.fill3DRect(770, 380, 100, 40,true);
            //读取功能,点击之后可以读档,相关按键在功能开启后出现
			if(isReading) {
				g1.fill3DRect(770, 450, 100, 40,upCanPress);
				g1.fill3DRect(770, 520, 100, 40,true);
				g1.fill3DRect(770, 590, 100, 40,downCanPress);
			}
			g1.fill3DRect(770, 660, 100, 40,true);
			
			g1.setColor(Color.BLACK);
			g1.drawString("重新开始", 780, 125);
			g1.drawString("悔棋", 780, 195);
			g1.drawString("认输", 780, 265);
			g1.drawString("保存进度", 780, 335);
			g1.drawString("读取进度", 780, 405);
			if(isReading) {
				g1.drawString("上一步", 780, 475);
				g1.drawString("加载游戏", 780, 545);
				g1.drawString("下一步", 780, 615);
			}
			g1.drawString("返回大厅", 780, 685);
			
			//棋子填充颜色
			for(int i = 0 ; i < 17 ; i++ ) {
				for(int j = 0 ; j < 17 ; j++) {
					if(chesses[ i + j * 17] != null) {
						int realX = 70 + i * 40;
						int realY = 70 + j * 40;
						//判断棋子颜色 true为黑色  false为白色
						if (chesses[i + j * 17].isBlack() == true) {
							g1.setColor(Color.BLACK);
							g1.fillOval(realX, realY, 20, 20);
						}else if (chesses[i + j * 17].isBlack() == false) {
							g1.setColor(Color.WHITE);
							g1.fillOval(realX, realY, 20, 20);
						}
					}
				}
			}

}

棋子类的构建:

public class Chess {
	private int x;
	private int y;
	private int orderNum;
	private boolean black;
	
	public Chess() {
		
	}
	public Chess(int x, int y, int orderNum, boolean color) {
		super();
		this.x = x;
		this.y = y;
		this.orderNum = orderNum;
		this.black = color;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	public int getOrderNum() {
		return orderNum;
	}
	public void setOrderNum(int orderNum) {
		this.orderNum = orderNum;
	}
	public boolean isBlack() {
		return black;
	}
	public void setBlack(boolean color) {
		this.black = color;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Chess other = (Chess) obj;
		return black == other.black;
	}
	

	
	
}

棋子和棋盘的存储思路:

棋盘是17*17(非标准,当时一拍脑袋决定的),一般来说,用二维数组 chesses[17][17]存储棋盘信息是很符合大脑习惯的,不过二维数组有不方便的地方,直接用chesses[17 * 17]来存储了,其实也很好理解,数组索引对应每个棋子 X + Y * 17(可能需要理解一下),或者由索引反推坐标就是:X = i (数组索引) % 17;     Y = i / 17;。

chesses[17 * 17]是棋盘的存储数据,下一个棋子就会在对应索引新建一个棋子对象,并在对应的坐标绘制棋子图像。

落子的存储是用TreeMap,key为落子顺序(1,2,3,4…),value为棋子的一维数组索引。

这样存储的好处是方便读取数据,读取存档的时候很方便。

当然还可以另一个思路:

因为Chess类中申明了落子顺序OrderNum,直接用ArrayList存储落子信息以及棋盘信息,用ArrayList的方法也很方便。

下棋子:

           //如果游戏结束,canPlay = false
            if(canPlay) {
                //下棋子
				int x = (e.getX() - 60) / 40;
				int y = (e.getY() - 60) / 40;
				if (e.getX() >= 80 && e.getY() >= 80 && e.getX() <= 720 && e.getY() <= 720) {
					//填充
					if(chesses[x + y * 17] == null) {
						chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
						isBlack = !isBlack;
						isSaved = false;
						//测试:棋子序号 + 横坐标 + 纵坐标
						System.out.println(chesses[x + y * 17].getOrderNum() + "..." +chesses[x + y * 17].getX() + "..." + chesses[x + y * 17].getY());
						//test
						System.out.println(maxLine(chesses[x + y * 17]));
					}
					tm.put(chesses[x + y * 17].getOrderNum(), x + y * 17);
					this.repaint();
				}else {
					return;
				}
				//判断胜负
				isWin(maxLine(chesses[x + y * 17]));
			}

 maxLine方法:

米字形判断,每次落子都判断上下左右斜方向的最长连子数。

基本思路:以横向为例,落子后判断索引±4的最长连子,需要注意一个问题,就是棋子在棋盘的边界,需要加一个判断,如果纵坐标变动了(变成另一排),则重新计算maxLine。如果是竖向判断,则需要监控X坐标有无变动;斜向的话,需要监控Y坐标,如果变化为2,重新计算。

//判断最长连子数
	public int maxLine(Chess chess) {
		int xLine = 1;
		int yLine = 1;
		int rightLine = 1;
		int leftLine = 1;
		int max = 1;
		
		int index = chess.getX() + chess.getY() * 17;
		
			//横向判断 
			for (int i = index - 4 ; i < index + 4 ; i ++) {
				if (i > 0 && i < 17*17 - 1 && (i + 1) / 17 - i / 17 == 0 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 1] != null && chesses[i + 1].equals(chess)) {
						xLine++;
					}else {
						xLine = 1;
						continue;
					}
					max = xLine > max ? xLine : max;
				}else {
					xLine = 1;
				}
			}
		//纵向判断
		if (max == 5) {
			return 5;
		}else {
			for (int i = index - 4 * 17 ; i < index + 4 * 17 ; i += 17) {
				if (i > 0 && i < 17*17 - 17 && (i + 17) % 17 - i % 17 == 0 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 17] != null && chesses[i + 17].equals(chess)) {
						yLine++;
					}else {
						yLine = 1;
						continue;
					}
					max = yLine > max ? yLine : max;
				}else {
					yLine = 1;
				}
			}
		}
		
		//斜向\
		if(max == 5) {
			return 5;
		}else {
			for (int i = index - 4 * 18; i < index + 4 * 18 ; i += 18) {
				if (i > 0 && i < 17*17 -  18 && (i+ 18) / 17 - i / 17 == 1 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 18] != null && chesses[i + 18].equals(chess)) {
						leftLine++;
					}else {
						leftLine = 1;
						continue;
					}
					max = leftLine > max ? leftLine : max;
				}else {
					leftLine = 1;
				}
			}
		}
		
		// 斜向/
		if(max == 5) {
			return 5;
		}else {
			for (int i = index - 4 * 16; i < index + 4 * 16 ; i += 16) {
				if (i > 0 && i < 17*17 -  16 && (i+ 16) / 17 - i / 17 == 1 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 16] != null && chesses[i + 16].equals(chess)) {
						rightLine++;
					}else {
						rightLine = 1;
						continue;
					}
					max = rightLine > max ? rightLine : max;
				}else {
					rightLine = 1;
				}
			}
		}
		return max;	
	}

isWin方法:

//判断是否胜利,输入最长连子数
	public void isWin(int i) {
		if (i >= 5) {
			canPlay = false;
			if(isBlack == true) {
				JOptionPane.showMessageDialog(this, "白方胜利");
			}else {
				JOptionPane.showMessageDialog(this, "黑方胜利");
			}
		}
	}

canPlay用来控制能否下棋,游戏判出胜负后就不能继续落子了,需要点悔棋或者重新开始才能继续。

这样就实现了下棋的基本功能了,至于重新开始,悔棋之类的功能,在单机版很容易实现,之后会po出代码,先将一下稍微有点复杂的读取功能:

先看效果图:

Java五子棋(局域网)_第2张图片

选好文档后,(没有保存文档就乱点然后保存一个)就会弹出“上一步”“加载游戏”“下一步”三个功能按键,“上一步” 和 “下一步”是用来读档的,方便知道落子的顺序,同时也可以选中到想要的进度开始游戏。点击加载就可以从当前画面下的棋盘开始一局游戏了。然后,这三个功能键就会消失,等到再次读取进度的时候再出现。

功能键的画图实现代码在绘图中,功能实现代码如下:

if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 380 && e.getY() <= 420) {//这里是坐标
				int isYes = JOptionPane.showConfirmDialog(this, "是否读取进度","readload",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					isReading = true;
					readLoad();//定义的一个方法,具体在一下代码块里,作用是弹出一个选择文件的框
					tmTemp = new TreeMap<>();
					for(Map.Entry entry : tm.entrySet()) {
						tmTemp.put(entry.getKey(), entry.getValue());
					}
					
				}
			}
			/*
			 * 读取中的扩展功能
			 */
			if(isReading) {
				canPlay = false;
				canPress = false;
				int SIZE = tm.size();
				int count = tmTemp.size();
				//上一步
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 450 && e.getY() <= 490 && upCanPress) {
					if(count > 0 ) {
						chesses[tmTemp.get(count)] = null;
						tmTemp.remove(count-- );
						isBlack = !isBlack;
					}
					//test
					System.out.println(tm+" " + SIZE + "\n" +tmTemp + count );
				}
				//加载
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 520 && e.getY() <= 560) {
					isReading = false;
					restart();
					for(Map.Entry entry : tmTemp.entrySet()) {
						tm.put(entry.getKey(), entry.getValue());
						chesses[entry.getValue()] = new Chess(entry.getValue() % 17 , entry.getValue() / 17 ,countNum ++ ,isBlack);
						isBlack = !isBlack;
						isWin(maxLine(chesses[entry.getValue()]));
						canPress = true;
					}
					
					this.repaint();
				}
				//下一步
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 590 && e.getY() <= 630 && downCanPress) {
					if(count < SIZE ) {
						int index = tm.get(++count); 
						chesses[index] = new Chess(index % 17 , index / 17 ,count,isBlack);
						isBlack = !isBlack;
						tmTemp.put(count , index);
						
					}
					//test
					System.out.println(tm+" " + SIZE + "\n" +tmTemp + count  );
				}
				if(count == SIZE) {
					downCanPress = false;
				}else if(count == 0) {
					upCanPress = false;
				}else {
					downCanPress = true;
					upCanPress = true;
				}
				this.repaint();
			}

readLoad方法:

//读取功能,弹出窗口选择保存文档
	public void readLoad() {
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
		chooser.setFileFilter(filter);
		int option = chooser.showOpenDialog(null);
		if(option==JFileChooser.APPROVE_OPTION){
			File file = chooser.getSelectedFile();
			System.out.println(file.getAbsolutePath());
			restart();//定义的重开的方法:棋盘清空,存档清空…
			try {
				 br = new BufferedReader(new FileReader(file));
				String line = null;
				while((line = br.readLine()) != null) {
					String[] arr = line.split(" ");
					int num = Integer.parseInt(arr[1]);
					if(chesses[num] == null) {
						chesses[num] = new Chess(num % 17,num / 17,countNum++,isBlack);
						isBlack = !isBlack;
						tm.put(countNum - 1, num);
						this.repaint();
						isWin(maxLine(chesses[num]));
					}else {
						JOptionPane.showMessageDialog(this, "文件读取错误\r\n棋子重复");
						break;
					}
				}
			} catch (Exception e1) {
				e1.printStackTrace();
			}finally {
				if(br != null) {
					try {
						br.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}

 涉及到IO流和弹窗的类的使用,具体怎么存档可以根据自己开发需要进行修改。

读档的时候,因为可能会多次“上一步”和“下一步”,这里选择再新建一个TreeMap用来复制读档,这个TreeMap的作用就是保存完整的读档文件,具体上一步下一步的操作结果是落实在我们之前就新建好的TreeMap来存档文件,这样可以保证重复“上一步”和“下一步”的操作。

等点击“加载游戏”的时候,再用临时的TreeMap复制进棋盘存储数据的TreeMap中。

等无法进行“上一步”或者“下一步”的操作时,会进行一个判断,此时会将无法执行的按键(比如,才开始读档时,是不能点“下一步”的,或者棋盘已经没有棋子的时候,是不能“上一步”的),按键的样式会变灰一点,具体的实现在绘画代码块中,只需要讲按键方法中最后一个值改为false就行,canPress布尔变量就是定义来干这个事情的。

以下是其他功能的实现代码,同样,绘图的实现在最上面的代码块中。

/*
			 * 重新开始功能
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
				if(canPlay == true) {
					if(countNum == 1) {
						JOptionPane.showMessageDialog(this, "棋盘并无棋子");
					}else {
						int isYes = JOptionPane.showConfirmDialog(this, "胜负未分,是否重新开始","restart",JOptionPane.YES_NO_OPTION);
						if(isYes== 0) {
							restart();
						}
					}
				}else {
					restart();
				}
			}
			/*
			 *悔棋功能
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否悔棋","take back a move",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					if(tm.get(1) != null) {
						canPlay = true;
						isBlack = !isBlack;
						isSaved = false;
						chesses[tm.remove(--countNum)] = null;
						this.repaint();
					}else {
						JOptionPane.showMessageDialog(this, "棋盘没有棋子");
					}
				}else {
					//JOptionPane.showMessageDialog(this, "悔棋失败");
				}
			}
			/*
			 * 保存功能:落子顺序和坐标数组序号
			 */	
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否保存棋局","saveload",JOptionPane.YES_NO_OPTION);
				if(isYes == 0) {
					saveLoad();
				}
			}


            /*
			 * 返回大厅
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
				if(isSaved == true) {
					int isYes = JOptionPane.showConfirmDialog(this, "是否退出","exit",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						page = 1;
						
						restart();
						this.repaint();
					}
				}else {
					int isYes = JOptionPane.showConfirmDialog(this, "棋谱未保存,是否退出前保存?","exit",JOptionPane.YES_NO_CANCEL_OPTION);
					if(isYes== 0) {
						saveLoad();
						page = 1;
						
						restart();
						this.repaint();
					}else if(isYes == 1){
						page = 1;
						
						restart();
						this.repaint();
					}else {
						
					}
				}
			}
                /*
				 * 认输功能
				 */
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
					int isYes = JOptionPane.showConfirmDialog(this, "是否认输","give up",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						canPlay = false;
						if(isBlack == true) {
							JOptionPane.showMessageDialog(this, "白方胜利");
						}else {
							JOptionPane.showMessageDialog(this, "黑方胜利");
						}	
					}
				}

方法代码:

//重新开始
	public void restart() {
		canPlay = true;
		isBlack = true;
		isSaved = false;
		chesses = new Chess[17 * 17];
		countNum = 1;
		tm = new TreeMap();
		this.repaint();
	}
//保存功能,弹出窗口保存
	public void saveLoad() {
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
		chooser.setFileFilter(filter);
		int option = chooser.showSaveDialog(null);
		if(option==JFileChooser.APPROVE_OPTION){	
			File file = chooser.getSelectedFile();
			String fileName = chooser.getName(file);
			if(fileName.indexOf(".txt")==-1){
				file = new File(chooser.getCurrentDirectory(),fileName+".txt");
				System.out.println("renamed"); 
				System.out.println(file.getName());
			}
			
			try {
				bw = new BufferedWriter(new FileWriter(file));
				for(Map.Entry entry : tm.entrySet()) {
					bw.write(entry.getKey() + " " + entry.getValue());
					bw.newLine();
					bw.flush();
				}
			}catch(IOException ee){
				System.out.println("IO异常");
				ee.printStackTrace();
			}finally{
				if(bw != null) {
					try {
						bw.close();
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}

这样基本的单机版就算完成了。因为代码是从完整版里面截下来的,比较乱,不过没关系,最后会有全部的代码汇总。

再来一个难实现的功能就是联网了,(为了实现这个功能熬了半个月的夜,当然现在回想起来也不难,自己基础没打好)。

联网的思路有很多,可以建立一个服务器类,棋盘类的代码只需要完成连接服务器的socket就行,当然服务器的构建就会需要花费很多功夫了,毕竟还有匹配(开房间),禁止其他人加入这些功能需要考虑。

我选择了另一个简单的思路:棋盘类继承Runnable类,重写run方法,等点击联网功能键的时候,开出一个支线程,在线程中开启一个 ServerSocket用来接受另一个客户端发来的信息。

Java五子棋(局域网)_第3张图片

 等需要发送信息的时候,调用发送方法,新建socket连接对方服务器,即可完成信息的交互。

这里还需要多一嘴,落子的信息很好理解:落子时,只需要将我的落子的棋子在chesses[]数组中的索引给到对方即可,那功能按键呢?

我选择用常量来代替,棋盘落子只需要用到0到17*17 - 1这个区间,那么从17 * 17开始,就可以定义为功能按键的信息。具体代码:

@Override
	public void run() {
		// TODO Auto-generated method stub
		
		System.out.println("执行run()");
		String line = null;
		try {
			server = new ServerSocket(Integer.parseInt(yourPort));
			while(true) {
				matchsocket = server.accept();
				System.out.println("连接成功");
				obr = new BufferedReader(new InputStreamReader(matchsocket.getInputStream()));
				line = obr.readLine();
				int inputNum =Integer.parseInt(line.trim());
					
					
				System.out.println("读取数据" + inputNum);
				translator(inputNum);
				
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	public void send(int i) {
		try {
			yoursocket = new Socket(matchIP,Integer.parseInt(matchPort.trim()));
			obw = new BufferedWriter(new OutputStreamWriter(yoursocket.getOutputStream()));
			obw.write(String.valueOf(i));
			obw.newLine();
			System.out.println("发送数据" + i);
			obw.flush();
			obw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void translator(int i) {
		if(ONLINE_DISCONNECT == i) {
			isFilled = false;
			yourIP = null;
			yourPort = null;
			matchIP = null;
			matchPort = null;
			obw = null;
			obr = null;
			yoursocket = null;
			matchsocket = null;
			server = null;
			
			onlineRestart();
			page = 1;
			
			this.repaint();
			
		}else if(ONLINE_RESTART_REQUEST == i) {
			//只能判断胜负后才能重开
			restartWaitting = true;
			JOptionPane.showMessageDialog(this, "对手希望再来一局");
		}else if(ONLINE_RESTART_YES == i) {
			onlineRestart();
			int j = random.nextInt(100);
			myNum = ONLINE_DISCONNECT + j + 1;
			send(myNum);
		}else if(ONLINE_REGRATE_REQUEST == i) {
			regrateCanPress = false;
			int isYes = JOptionPane.showConfirmDialog(this, "对方请求悔棋,是否同意","take back a move",JOptionPane.YES_NO_OPTION);
			if(isYes == 0) {
				send(ONLINE_REGRATE_YES);
				
				
				canPlay = true;
				isBlack = !isBlack;
				yourTurn = !yourTurn;
				isSaved = false;
				chesses[tm.remove(--countNum)] = null;
				regrateCanPress = true;
				this.repaint();
				
			}else {
				send(ONLINE_REGRATE_NO);
				
				regrateCanPress = true;
			}
		}else if(i > ONLINE_DISCONNECT && i <= ONLINE_DISCONNECT + 100) {
			yourTurn = myNum > i ? true : false ;
			isBlack = true;
			if(yourTurn) {
				send(ONLINE_MYTURN);
				JOptionPane.showMessageDialog(this, "执先手");
			}else {
				send(ONLINE_YOURTURN);
				JOptionPane.showMessageDialog(this, "执后手");
			}
		}else if(ONLINE_REGRATE_YES == i) {
			canPlay = true;
			isBlack = !isBlack;
			yourTurn = !yourTurn;
			isSaved = false;
			chesses[tm.remove(--countNum)] = null;
			regrateCanPress = true;
			this.repaint();
		}else if(ONLINE_REGRATE_NO == i) {
			JOptionPane.showMessageDialog(this, "对方不同意悔棋");
			regrateCanPress = true;
		}else if(ONLINE_SURRENDER == i) {
			canPlay = false;
			JOptionPane.showMessageDialog(this, "胜利:对方认输");
		}else if(i >= 0 && i < 17 * 17) {
			chesses[i]  = new Chess(i % 17 , i / 17, countNum++ , isBlack);
			onlineIsWin(maxLine(chesses[i]));
			isBlack = !isBlack;
			yourTurn = !yourTurn;
			isSaved = false;
			tm.put(chesses[i].getOrderNum(), i);
			this.repaint();
		}else if(ONLINE_MYTURN == i) {
			yourTurn = false;
			isBlack = true;
		}else if(ONLINE_YOURTURN == i) {
			yourTurn = true;
			isBlack = yourTurn;
		}else if(ONLINE_WIN == i) {
			JOptionPane.showMessageDialog(this, "游戏结束");
		}
			
	}

 三个方法,分别用来实现[开启服务器][发送信息]和[翻译信息]。

之后,我们只需要在单机版上做出一点调整即可,每次对棋盘做出修改的时候,将相应的信息传递给对方,对方通过translator()方法翻译出来,在棋盘上做出相应的动作即可。

全部的代码在这:

这是主类棋盘类,几乎所有的功能都在这里实现(按理说不应该这样……):

package mygobang;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

import javax.imageio.ImageIO;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;


class Gb  extends JFrame implements MouseListener ,Runnable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	JLabel label;
	BufferedImage imageMusic = null ;
	BufferedWriter bw = null;
	BufferedReader br = null;//文档的流

	Thread onlineThread = new Thread(this);
	BufferedReader obr = null;
	BufferedWriter obw = null;//联网的流
	Chess[] chesses = new Chess[17 * 17];
	TreeMap tm = new TreeMap<>();//key为序号 value为坐标对应的数组index
	TreeMap tmTemp;//读取进度时用,点击加载覆盖tm
	static int width = Toolkit.getDefaultToolkit().getScreenSize().width;
	static int height = Toolkit.getDefaultToolkit().getScreenSize().height;
	boolean isReading = false;
	boolean upCanPress = true;
	boolean downCanPress = false;
	static boolean hasWindow = false;
	static boolean threadIsStart = false;
	static String yourIP = null;
	static String yourPort = null;
	static String matchIP = null;
	static String matchPort = null;
	private int page = 1;//1 为开始界面。2为单机界面。3为联机界面。
	ServerSocket server = null;
	Socket yoursocket = null , matchsocket = null;
	Random random = new Random();
	boolean yourTurn = false;
	boolean restartWaitting = false;
	boolean restartCanPress = true;
	boolean regrateCanPress = true;
	public static final int ONLINE_RESTART_REQUEST = 17 * 17;
	public static final int ONLINE_RESTART_YES = 17 * 17 + 1;
	public static final int ONLINE_REGRATE_REQUEST = 17 * 17 + 2;
	public static final int ONLINE_REGRATE_YES = 17 * 17 + 3;
	public static final int ONLINE_REGRATE_NO = 17 * 17 + 4;
	public static final int ONLINE_SURRENDER = 17 * 17 + 5;
	public static final int ONLINE_MYTURN = 17 * 17 + 6;
	public static final int ONLINE_YOURTURN = 17 * 17 + 7;
	public static final int ONLINE_WIN = 17 * 17 + 8;
	public static final int ONLINE_DISCONNECT = 404;
	int myNum ;
	
	public Gb() {
		this.setTitle("五子棋游戏未完成版");
		this.setSize(900, 800);
		this.setLocation((width - 800) / 2, (height - 800) / 2 );
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(false);
		this.setVisible(true);
		this.repaint();
		this.addMouseListener(this);
		while(true) {
			this.music();
		}
		
	}
	
	
	//画棋盘
	public void paint(Graphics g) {
		BufferedImage buf = new BufferedImage(900, 800, BufferedImage.TYPE_INT_RGB);
		Graphics g1 = buf.createGraphics();
		
		
		
		
		/**
		 * 开始界面
		 */
		if(page == 1 ) {
			//背景和棋盘底色
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//背景
			g1.fill3DRect(0, 0, 900, 800, true);

			
			
			g1.setColor(new Color(158,204,171));
			g1.fill3DRect(200, 300, 200, 200,true);
			g1.fill3DRect(550, 300, 200, 200,true);
			BufferedImage game = null;
			BufferedImage people = null;
			try {
				game = ImageIO.read(new File("game1.png"));
				people =ImageIO.read(new File("people1.png"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			g1.drawImage(game,260,360,null);
			g1.drawImage(people,610,360,null);
			
			
			try {
				imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			g1.drawImage(imageMusic,820,47,null);
			
			
		}else if (page == 2 ) {
			
			//选项
			
			//背景和棋盘底色
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//背景
			g1.fill3DRect(0, 0, 900, 800, true);
			g1.setColor(new Color(235,177,13));//棋盘色
			g1.fill3DRect(60, 60, 680, 680, true);
			
			//棋盘线
			for (int i = 80; i <= 720; i += 40) {
				g1.setColor(Color.BLACK);
				g1.drawLine(80, i, 720, i);
				g1.drawLine(i, 80, i, 720);
			}
			
			//右上角黑白棋
			if(isBlack) {
				g1.setColor(Color.BLACK);
				g1.fillOval(770, 50, 30, 30);
			}else {
				g1.setColor(Color.WHITE);
				g1.fillOval(770, 50, 30, 30);
			}
			
			//音乐键
			try {
				imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			g1.drawImage(imageMusic,820,47,null);
			
			//功能按键
			g1.setFont(new Font("微软雅黑",Font.BOLD,15));
			g1.setColor(new Color(158,204,171));
			g1.fill3DRect(770, 100, 100, 40,true);
			g1.fill3DRect(770, 170, 100, 40,true);
			g1.fill3DRect(770, 240, 100, 40,true);
			g1.fill3DRect(770, 310, 100, 40,true);
			g1.fill3DRect(770, 380, 100, 40,true);
			if(isReading) {
				g1.fill3DRect(770, 450, 100, 40,upCanPress);
				g1.fill3DRect(770, 520, 100, 40,true);
				g1.fill3DRect(770, 590, 100, 40,downCanPress);
			}
			g1.fill3DRect(770, 660, 100, 40,true);
			
			g1.setColor(Color.BLACK);
			g1.drawString("重新开始", 780, 125);
			g1.drawString("悔棋", 780, 195);
			g1.drawString("认输", 780, 265);
			g1.drawString("保存进度", 780, 335);
			g1.drawString("读取进度", 780, 405);
			if(isReading) {
				g1.drawString("上一步", 780, 475);
				g1.drawString("加载游戏", 780, 545);
				g1.drawString("下一步", 780, 615);
			}
			g1.drawString("返回大厅", 780, 685);
			
			//棋子填充颜色
			for(int i = 0 ; i < 17 ; i++ ) {
				for(int j = 0 ; j < 17 ; j++) {
					if(chesses[ i + j * 17] != null) {
						int realX = 70 + i * 40;
						int realY = 70 + j * 40;
						//判断棋子颜色 true为黑色  false为白色
						if (chesses[i + j * 17].isBlack() == true) {
							g1.setColor(Color.BLACK);
							g1.fillOval(realX, realY, 20, 20);
						}else if (chesses[i + j * 17].isBlack() == false) {
							g1.setColor(Color.WHITE);
							g1.fillOval(realX, realY, 20, 20);
						}
					}
				}
			}
		}else/* page == 3  */ {
			//选项
			
			//背景和棋盘底色
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//背景
			g1.fill3DRect(0, 0, 900, 800, true);
			g1.setColor(new Color(235,177,13));//棋盘色
			g1.fill3DRect(60, 60, 680, 680, true);
			
			//棋盘线
			for (int i = 80; i <= 720; i += 40) {
				g1.setColor(Color.BLACK);
				g1.drawLine(80, i, 720, i);
				g1.drawLine(i, 80, i, 720);
			}
			
			//右上角黑白棋
			if(isBlack) {
				g1.setColor(Color.BLACK);
				g1.fillOval(770, 50, 30, 30);
			}else {
				g1.setColor(Color.WHITE);
				g1.fillOval(770, 50, 30, 30);
			}
			
			//音乐键
			
			try {
				imageMusic= playMusic ? ImageIO.read(new File("performance.png")) : ImageIO.read(new File("volume-mute.png"));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			g1.drawImage(imageMusic,820,47,null);
			
			//功能按键
			g1.setFont(new Font("微软雅黑",Font.BOLD,15));
			g1.setColor(new Color(158,204,171));
			g1.fill3DRect(770, 100, 100, 40,true);
			g1.fill3DRect(770, 170, 100, 40,true);
			g1.fill3DRect(770, 240, 100, 40,true);
			g1.fill3DRect(770, 310, 100, 40,true);
			
			g1.fill3DRect(770, 660, 100, 40,true);
			
			g1.setColor(Color.BLACK);//188,132,168
			g1.drawString("重新开始", 780, 125);
			g1.drawString("悔棋", 780, 195);
			g1.drawString("认输", 780, 265);
			g1.drawString("保存进度", 780, 335);
			
			g1.drawString("返回大厅", 780, 685);
			
			//棋子填充颜色
			for(int i = 0 ; i < 17 ; i++ ) {
				for(int j = 0 ; j < 17 ; j++) {
					if(chesses[ i + j * 17] != null) {
						int realX = 70 + i * 40;
						int realY = 70 + j * 40;
						//判断棋子颜色 true为黑色  false为白色
						if (chesses[i + j * 17].isBlack() == true) {
							g1.setColor(Color.BLACK);
							g1.fillOval(realX, realY, 20, 20);
						}else if (chesses[i + j * 17].isBlack() == false) {
							g1.setColor(Color.WHITE);
							g1.fillOval(realX, realY, 20, 20);
						}
					}
				}
			}
		}
		g.drawImage(buf, 0 , 0 ,this);
	}
	
	//鼠标点击下棋子
	boolean canPlay = true;
	boolean isBlack = true;
	boolean isSaved = false;
	boolean canPress = true;
	static boolean isFilled = false;
	int countNum = 1;
	public void mousePressed(MouseEvent e) {
		/*
		 * 打开音乐,关闭音乐
		 */
		if(e.getX() >= 820 && e.getX() <= 850 && e.getY() >= 20 && e.getY() <= 80) {
			playMusic =!playMusic;
			this.repaint();
		}
		
		
		
		
		/**
		 * 三个页面  page 1,2,3  
		 */
	
		if (page == 1 && !hasWindow) {
			//人机按钮
			if(e.getX() >= 200 && e.getX() <= 400 && e.getY() >= 300 && e.getY() <= 500) {
				page = 2;
				this.repaint();
			}
			//联网对战按钮
			if(e.getX() >= 550 && e.getX() <= 750 && e.getY() >= 300 && e.getY() <= 500) {
				page = 3;
				hasWindow = true;
				this.repaint();
				new ComponentInWindow();
				int i = random.nextInt(100);
				myNum = ONLINE_DISCONNECT + i + 1;
				System.out.println("随机数是" + myNum);
			}
		
			
			
			
			
			
		}else if(page == 2 && !hasWindow) {
			/*
			 * 重新开始功能
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
				if(canPlay == true) {
					if(countNum == 1) {
						JOptionPane.showMessageDialog(this, "棋盘并无棋子");
					}else {
						int isYes = JOptionPane.showConfirmDialog(this, "胜负未分,是否重新开始","restart",JOptionPane.YES_NO_OPTION);
						if(isYes== 0) {
							restart();
						}
					}
				}else {
					restart();
				}
			}
			/*
			 *悔棋功能
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否悔棋","take back a move",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					if(tm.get(1) != null) {
						canPlay = true;
						isBlack = !isBlack;
						isSaved = false;
						chesses[tm.remove(--countNum)] = null;
						this.repaint();
					}else {
						JOptionPane.showMessageDialog(this, "棋盘没有棋子");
					}
				}else {
					//JOptionPane.showMessageDialog(this, "悔棋失败");
				}
			}
			/*
			 * 保存功能:落子顺序和坐标数组序号
			 */	
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否保存棋局","saveload",JOptionPane.YES_NO_OPTION);
				if(isYes == 0) {
					saveLoad();
				}
			}
			/*
			 * 读取进度功能
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 380 && e.getY() <= 420) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否读取进度","readload",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					isReading = true;
					readLoad();
					tmTemp = new TreeMap<>();
					for(Map.Entry entry : tm.entrySet()) {
						tmTemp.put(entry.getKey(), entry.getValue());
					}
					
				}
			}
			/*
			 * 读取中的扩展功能
			 */
			if(isReading) {
				canPlay = false;
				canPress = false;
				int SIZE = tm.size();
				int count = tmTemp.size();
				//上一步
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 450 && e.getY() <= 490 && upCanPress) {
					if(count > 0 ) {
						chesses[tmTemp.get(count)] = null;
						tmTemp.remove(count-- );
						isBlack = !isBlack;
					}
					//test
					System.out.println(tm+" " + SIZE + "\n" +tmTemp + count );
				}
				//加载
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 520 && e.getY() <= 560) {
					isReading = false;
					restart();
					for(Map.Entry entry : tmTemp.entrySet()) {
						tm.put(entry.getKey(), entry.getValue());
						chesses[entry.getValue()] = new Chess(entry.getValue() % 17 , entry.getValue() / 17 ,countNum ++ ,isBlack);
						isBlack = !isBlack;
						isWin(maxLine(chesses[entry.getValue()]));
						canPress = true;
					}
					
					this.repaint();
				}
				//下一步
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 590 && e.getY() <= 630 && downCanPress) {
					if(count < SIZE ) {
						int index = tm.get(++count); 
						chesses[index] = new Chess(index % 17 , index / 17 ,count,isBlack);
						isBlack = !isBlack;
						tmTemp.put(count , index);
						
					}
					//test
					System.out.println(tm+" " + SIZE + "\n" +tmTemp + count  );
				}
				if(count == SIZE) {
					downCanPress = false;
				}else if(count == 0) {
					upCanPress = false;
				}else {
					downCanPress = true;
					upCanPress = true;
				}
				this.repaint();
			}
			/*
			 * 返回大厅
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
				if(isSaved == true) {
					int isYes = JOptionPane.showConfirmDialog(this, "是否退出","exit",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						page = 1;
						
						restart();
						this.repaint();
					}
				}else {
					int isYes = JOptionPane.showConfirmDialog(this, "棋谱未保存,是否退出前保存?","exit",JOptionPane.YES_NO_CANCEL_OPTION);
					if(isYes== 0) {
						saveLoad();
						page = 1;
						
						restart();
						this.repaint();
					}else if(isYes == 1){
						page = 1;
						
						restart();
						this.repaint();
					}else {
						
					}
				}
			}
			
			if(canPlay) {
				/*
				 * 认输功能
				 */
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
					int isYes = JOptionPane.showConfirmDialog(this, "是否认输","give up",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						canPlay = false;
						if(isBlack == true) {
							JOptionPane.showMessageDialog(this, "白方胜利");
						}else {
							JOptionPane.showMessageDialog(this, "黑方胜利");
						}	
					}
				}
				//下棋子
				int x = (e.getX() - 60) / 40;
				int y = (e.getY() - 60) / 40;
				if (e.getX() >= 80 && e.getY() >= 80 && e.getX() <= 720 && e.getY() <= 720) {
					//填充
					if(chesses[x + y * 17] == null) {
						chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
						isBlack = !isBlack;
						isSaved = false;
						//测试:棋子序号 + 横坐标 + 纵坐标
						System.out.println(chesses[x + y * 17].getOrderNum() + "..." +chesses[x + y * 17].getX() + "..." + chesses[x + y * 17].getY());
						//test
						System.out.println(maxLine(chesses[x + y * 17]));
					}
					tm.put(chesses[x + y * 17].getOrderNum(), x + y * 17);
					this.repaint();
				}else {
					return;
				}
				//判断胜负
				isWin(maxLine(chesses[x + y * 17]));
			}
		
		
		
		
		
			
			
			
		}else if(page == 3 && !hasWindow) {
			
			System.out.println("点击");
			if(!hasWindow && !threadIsStart) {
				Thread t = new Thread(this);
				t.start();

				System.out.println("线程开启");
				send(myNum);
				threadIsStart = true;
			}
			
//			if(threadIsStart) {
//				
//			}
			
			
			/*
			 * 重新开始功能
			 */
			if(canPress && restartCanPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
				if(canPlay) {
					JOptionPane.showMessageDialog(this, "胜负未分,请勿放弃!");
				}else {
					if(!restartWaitting) {
						send(ONLINE_RESTART_REQUEST);
						restartWaitting = true;
						restartCanPress = false;
					}else {
						int i = random.nextInt(100);
						myNum = ONLINE_DISCONNECT + i + 1;
						send(ONLINE_RESTART_YES);
						send(myNum);
						onlineRestart();
					}
				}
			}
			/*
			 *悔棋功能
			 */
			if(canPress && regrateCanPress && !yourTurn && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否悔棋","take back a move",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					if(tm.get(1) != null) {
						regrateCanPress = false;
						send(ONLINE_REGRATE_REQUEST);	
					}else {
						JOptionPane.showMessageDialog(this, "棋盘没有棋子");
					}
				}
			}
			/*
			 * 保存功能:落子顺序和坐标数组序号
			 */	
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
				int isYes = JOptionPane.showConfirmDialog(this, "是否保存棋局","saveload",JOptionPane.YES_NO_OPTION);
				if(isYes == 0) {
					saveLoad();
				}
			}
			
			/*
			 * 返回大厅
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
				if(isSaved == true) {
					int isYes = JOptionPane.showConfirmDialog(this, "是否退出","exit",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						isFilled = false;
						yourIP = null;
						yourPort = null;
						matchIP = null;
						matchPort = null;
						obw = null;
						obr = null;
						yoursocket = null;
						matchsocket = null;
						server = null;
						threadIsStart = false;
						onlineRestart();
						page = 1;
						
						this.repaint();
						
						send(ONLINE_DISCONNECT);
					}
				}else {
					int isYes = JOptionPane.showConfirmDialog(this, "棋谱未保存,是否退出前保存?","exit",JOptionPane.YES_NO_CANCEL_OPTION);
					if(isYes== 0) {
						saveLoad();
						isFilled = false;
						yourIP = null;
						yourPort = null;
						matchIP = null;
						matchPort = null;
						obw = null;
						obr = null;
						yoursocket = null;
						matchsocket = null;
						server = null;
						threadIsStart = false;
						onlineRestart();
						page = 1;
						
						this.repaint();
						
						send(ONLINE_DISCONNECT);
					}else if(isYes == 1){
						isFilled = false;
						yourIP = null;
						yourPort = null;
						matchIP = null;
						matchPort = null;
						obw = null;
						obr = null;
						yoursocket = null;
						matchsocket = null;
						server = null;
						threadIsStart = false;
						onlineRestart();
						page = 1;
						
						this.repaint();
						
						send(ONLINE_DISCONNECT);
					}else {
						
					}
				}
			}
			
			
			if(canPlay) {
				/*
				 * 认输功能
				 */
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
					int isYes = JOptionPane.showConfirmDialog(this, "是否认输","give up",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						canPlay = false;
						send(ONLINE_SURRENDER);
						
						JOptionPane.showMessageDialog(this, "认输");
						
					}
				}
				//下棋子
				int x = (e.getX() - 60) / 40;
				int y = (e.getY() - 60) / 40;
				if (yourTurn && e.getX() >= 80 && e.getY() >= 80 && e.getX() <= 720 && e.getY() <= 720) {
					//填充
					if(chesses[x + y * 17] == null) {
						chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
						send(x + y * 17);
						
						isBlack = !isBlack;
						yourTurn = !yourTurn;
						isSaved = false;
						//测试:棋子序号 + 横坐标 + 纵坐标
						System.out.println(chesses[x + y * 17].getOrderNum() + "..." +chesses[x + y * 17].getX() + "..." + chesses[x + y * 17].getY());
						//test
						System.out.println(maxLine(chesses[x + y * 17]));
					}
					tm.put(chesses[x + y * 17].getOrderNum(), x + y * 17);
					this.repaint();
				}else {
					return;
				}
				//判断胜负
				onlineIsWin(maxLine(chesses[x + y * 17]));
			}
		
		}
		
	}

	boolean playMusic = true;
	public void music() {
		try {
			AudioInputStream ais = AudioSystem.getAudioInputStream(new File("UGmusic.wav"));
			AudioFormat aif = ais.getFormat();
			final SourceDataLine sdl;
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, aif);
			sdl = (SourceDataLine) AudioSystem.getLine(info);
			sdl.open(aif);
			sdl.start();
			FloatControl fc = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN);
			// value可以用来设置音量,从0-2.0
			double value = 2;
			float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
			fc.setValue(dB);
			int nByte = 0;
			final int SIZE = 1024 * 64;
			byte[] buffer = new byte[SIZE];
			while (nByte != -1) {// 判断 播放/暂停 状态
				if(playMusic) {
					nByte = ais.read(buffer, 0, SIZE);
					sdl.write(buffer, 0, nByte);
				}else {
					nByte = ais.read(buffer, 0, 0);
				}
			}
			sdl.stop();
 
		} catch (Exception e) {
			e.printStackTrace();
		}


	}
	
	//判断最长连子数
	public int maxLine(Chess chess) {
		int xLine = 1;
		int yLine = 1;
		int rightLine = 1;
		int leftLine = 1;
		int max = 1;
		
		int index = chess.getX() + chess.getY() * 17;
		
			//横向判断 
			for (int i = index - 4 ; i < index + 4 ; i ++) {
				if (i > 0 && i < 17*17 - 1 && (i + 1) / 17 - i / 17 == 0 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 1] != null && chesses[i + 1].equals(chess)) {
						xLine++;
					}else {
						xLine = 1;
						continue;
					}
					max = xLine > max ? xLine : max;
				}else {
					xLine = 1;
				}
			}
		//纵向判断
		if (max == 5) {
			return 5;
		}else {
			for (int i = index - 4 * 17 ; i < index + 4 * 17 ; i += 17) {
				if (i > 0 && i < 17*17 - 17 && (i + 17) % 17 - i % 17 == 0 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 17] != null && chesses[i + 17].equals(chess)) {
						yLine++;
					}else {
						yLine = 1;
						continue;
					}
					max = yLine > max ? yLine : max;
				}else {
					yLine = 1;
				}
			}
		}
		
		//斜向\
		if(max == 5) {
			return 5;
		}else {
			for (int i = index - 4 * 18; i < index + 4 * 18 ; i += 18) {
				if (i > 0 && i < 17*17 -  18 && (i+ 18) / 17 - i / 17 == 1 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 18] != null && chesses[i + 18].equals(chess)) {
						leftLine++;
					}else {
						leftLine = 1;
						continue;
					}
					max = leftLine > max ? leftLine : max;
				}else {
					leftLine = 1;
				}
			}
		}
		
		// 斜向/
		if(max == 5) {
			return 5;
		}else {
			for (int i = index - 4 * 16; i < index + 4 * 16 ; i += 16) {
				if (i > 0 && i < 17*17 -  16 && (i+ 16) / 17 - i / 17 == 1 ) {
					if(chesses[i] != null && chesses[i].equals(chess) && chesses[i + 16] != null && chesses[i + 16].equals(chess)) {
						rightLine++;
					}else {
						rightLine = 1;
						continue;
					}
					max = rightLine > max ? rightLine : max;
				}else {
					rightLine = 1;
				}
			}
		}
		return max;	
	}
	//重新开始
	public void restart() {
		canPlay = true;
		isBlack = true;
		isSaved = false;
		chesses = new Chess[17 * 17];
		countNum = 1;
		tm = new TreeMap();
		this.repaint();
	}
	//联网重新开始
	public void onlineRestart() {
		canPlay = true;
		isBlack = true;
		isSaved = false;
		chesses = new Chess[17 * 17];
		countNum = 1;
		tm = new TreeMap();
		restartWaitting = false;
		restartCanPress = true;
		this.repaint();
	}
	
	//判断是否胜利,输入最长连子数
	public void isWin(int i) {
		if (i >= 5) {
			canPlay = false;
			if(isBlack == true) {
				JOptionPane.showMessageDialog(this, "白方胜利");
			}else {
				JOptionPane.showMessageDialog(this, "黑方胜利");
			}
		}
	}
	
	public void onlineIsWin(int i) {
		if (i >= 5) {
			canPlay = false;
			if(yourTurn) {
				JOptionPane.showMessageDialog(this, "游戏结束!");
			}else {
				send(ONLINE_WIN);
			}
		}
	}
	//保存功能,弹出窗口保存
	public void saveLoad() {
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
		chooser.setFileFilter(filter);
		int option = chooser.showSaveDialog(null);
		if(option==JFileChooser.APPROVE_OPTION){	
			File file = chooser.getSelectedFile();
			String fileName = chooser.getName(file);
			if(fileName.indexOf(".txt")==-1){
				file = new File(chooser.getCurrentDirectory(),fileName+".txt");
				System.out.println("renamed"); 
				System.out.println(file.getName());
			}
			
			try {
				bw = new BufferedWriter(new FileWriter(file));
				for(Map.Entry entry : tm.entrySet()) {
					bw.write(entry.getKey() + " " + entry.getValue());
					bw.newLine();
					bw.flush();
				}
			}catch(IOException ee){
				System.out.println("IO异常");
				ee.printStackTrace();
			}finally{
				if(bw != null) {
					try {
						bw.close();
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}
	//读取功能,弹出窗口选择保存文档
	public void readLoad() {
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter("(*.txt)", "txt");
		chooser.setFileFilter(filter);
		int option = chooser.showOpenDialog(null);
		if(option==JFileChooser.APPROVE_OPTION){
			File file = chooser.getSelectedFile();
			System.out.println(file.getAbsolutePath());
			restart();
			try {
				 br = new BufferedReader(new FileReader(file));
				String line = null;
				while((line = br.readLine()) != null) {
					String[] arr = line.split(" ");
					int num = Integer.parseInt(arr[1]);
					if(chesses[num] == null) {
						chesses[num] = new Chess(num % 17,num / 17,countNum++,isBlack);
						isBlack = !isBlack;
						tm.put(countNum - 1, num);
						this.repaint();
						isWin(maxLine(chesses[num]));
					}else {
						JOptionPane.showMessageDialog(this, "文件读取错误\r\n棋子重复");
						break;
					}
				}
			} catch (Exception e1) {
				e1.printStackTrace();
			}finally {
				if(br != null) {
					try {
						br.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}
	
	public void translator(int i) {
		if(ONLINE_DISCONNECT == i) {
			isFilled = false;
			yourIP = null;
			yourPort = null;
			matchIP = null;
			matchPort = null;
			obw = null;
			obr = null;
			yoursocket = null;
			matchsocket = null;
			server = null;
			
			onlineRestart();
			page = 1;
			
			this.repaint();
			
		}else if(ONLINE_RESTART_REQUEST == i) {
			//只能判断胜负后才能重开
			restartWaitting = true;
			JOptionPane.showMessageDialog(this, "对手希望再来一局");
		}else if(ONLINE_RESTART_YES == i) {
			onlineRestart();
			int j = random.nextInt(100);
			myNum = ONLINE_DISCONNECT + j + 1;
			send(myNum);
		}else if(ONLINE_REGRATE_REQUEST == i) {
			regrateCanPress = false;
			int isYes = JOptionPane.showConfirmDialog(this, "对方请求悔棋,是否同意","take back a move",JOptionPane.YES_NO_OPTION);
			if(isYes == 0) {
				send(ONLINE_REGRATE_YES);
				
				
				canPlay = true;
				isBlack = !isBlack;
				yourTurn = !yourTurn;
				isSaved = false;
				chesses[tm.remove(--countNum)] = null;
				regrateCanPress = true;
				this.repaint();
				
			}else {
				send(ONLINE_REGRATE_NO);
				
				regrateCanPress = true;
			}
		}else if(i > ONLINE_DISCONNECT && i <= ONLINE_DISCONNECT + 100) {
			yourTurn = myNum > i ? true : false ;
			isBlack = true;
			if(yourTurn) {
				send(ONLINE_MYTURN);
				JOptionPane.showMessageDialog(this, "执先手");
			}else {
				send(ONLINE_YOURTURN);
				JOptionPane.showMessageDialog(this, "执后手");
			}
		}else if(ONLINE_REGRATE_YES == i) {
			canPlay = true;
			isBlack = !isBlack;
			yourTurn = !yourTurn;
			isSaved = false;
			chesses[tm.remove(--countNum)] = null;
			regrateCanPress = true;
			this.repaint();
		}else if(ONLINE_REGRATE_NO == i) {
			JOptionPane.showMessageDialog(this, "对方不同意悔棋");
			regrateCanPress = true;
		}else if(ONLINE_SURRENDER == i) {
			canPlay = false;
			JOptionPane.showMessageDialog(this, "胜利:对方认输");
		}else if(i >= 0 && i < 17 * 17) {
			chesses[i]  = new Chess(i % 17 , i / 17, countNum++ , isBlack);
			onlineIsWin(maxLine(chesses[i]));
			isBlack = !isBlack;
			yourTurn = !yourTurn;
			isSaved = false;
			tm.put(chesses[i].getOrderNum(), i);
			this.repaint();
		}else if(ONLINE_MYTURN == i) {
			yourTurn = false;
			isBlack = true;
		}else if(ONLINE_YOURTURN == i) {
			yourTurn = true;
			isBlack = yourTurn;
		}else if(ONLINE_WIN == i) {
			JOptionPane.showMessageDialog(this, "游戏结束");
		}
			
	}
	
	public void send(int i) {
		try {
			yoursocket = new Socket(matchIP,Integer.parseInt(matchPort.trim()));
			obw = new BufferedWriter(new OutputStreamWriter(yoursocket.getOutputStream()));
			obw.write(String.valueOf(i));
			obw.newLine();
			System.out.println("发送数据" + i);
			obw.flush();
			obw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		System.out.println("执行run()");
		String line = null;
		try {
			server = new ServerSocket(Integer.parseInt(yourPort));
			while(true) {
				matchsocket = server.accept();
				System.out.println("连接成功");
				obr = new BufferedReader(new InputStreamReader(matchsocket.getInputStream()));
				line = obr.readLine();
				int inputNum =Integer.parseInt(line.trim());
					
					
				System.out.println("读取数据" + inputNum);
				translator(inputNum);
				
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	
	

	@Override
	public void mouseClicked(MouseEvent e) {
		
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}


	
	
	
}

新建一个棋子类:

package mygobang;

public class Chess {
	private int x;
	private int y;
	private int orderNum;
	private boolean black;
	
	public Chess() {
		
	}
	public Chess(int x, int y, int orderNum, boolean color) {
		super();
		this.x = x;
		this.y = y;
		this.orderNum = orderNum;
		this.black = color;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	public int getOrderNum() {
		return orderNum;
	}
	public void setOrderNum(int orderNum) {
		this.orderNum = orderNum;
	}
	public boolean isBlack() {
		return black;
	}
	public void setBlack(boolean color) {
		this.black = color;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Chess other = (Chess) obj;
		return black == other.black;
	}
	

	
	
}

联网时用到的一个弹窗类:

package mygobang;

import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

public class ComponentInWindow extends JFrame implements ActionListener {
	JTextField text1,text2,text3,text4;
	JButton button;
	public ComponentInWindow() {
		 init();
	     setVisible(true);
	     this.setResizable(false);
	     this.setBounds(0, 0, 320, 150);
	     this.setTitle("网络链接");
	     this.setLocation((Gb.width - 320) / 2, (Gb.height - 150) / 2 );
	     setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	     button.addActionListener(this);
	     add(button);
	}
	
	public void init() {
		setLayout(new FlowLayout()); // 设置布局

        add(new JLabel("你的IP:"));
        text1 = new JTextField(10);
        add(text1);
        
        add(new JLabel("你的端口:"));
        text2 = new JTextField(10);
        add(text2);
        add(new JLabel("对手IP:"));
        text3 = new JTextField(10);
        add(text3);
        add(new JLabel("对手端口:"));
        text4 = new JTextField(10);
        add(text4);
        
        add(new JLabel("按钮:"));
        button = new JButton("开始联机");
        add(button);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		Gb.yourIP = text1.getText();
		Gb.yourPort = text2.getText();
		Gb.matchIP = text3.getText();
		Gb.matchPort = text4.getText();
		if(!Gb.yourIP.equals("") && !Gb.yourPort.equals("") && !Gb.matchIP.equals("") && !Gb.matchPort.equals("")) {
			Gb.hasWindow = false;
			Gb.isFilled = true;

			this.dispose();
		}else {
			JOptionPane.showMessageDialog(this, "请输入IP和端口");
		}
		
		
	}
}

执行main类:

package mygobang;

public class Run  {
	public static void main(String[] args) {
		new Gb();
	}
}

最后给个效果图:

Java五子棋(局域网)_第4张图片

这是第一页,左边单机,右边联网。右上角是背景音乐,点一下可以暂停,同时图片会换。

单机的就是封面了,再来个联网的:

Java五子棋(局域网)_第5张图片

 输入完毕后就可以开始游戏了。(局域网)

 没有点击开始联机,小窗口是关不掉的,同时棋盘也无法进行操作。

代码源文件和用到的图片音乐都上传到云了,需要自取:

链接: https://pan.baidu.com/s/1gg17ibt9eInbJO8GEgZ5ow

提取码: wfnu

 

当然,肯定也有做五子棋完成作业的小伙伴,除了特定的IO流弹窗类绘图类这种,尽量看了之后自己手打哈。

你可能感兴趣的:(项目,java,游戏)