Java Gobang (LAN)

 

Draw the board interface first:

class Gb  extends JFrame implements MouseListener ,Runnable {
	BufferedWriter bw = null;
	BufferedReader br = null;//Document Stream
    Chess[] chesses = new Chess[17 * 17];//Array of chess pieces, one array per grid
	TreeMap<Integer,Integer> tm = new TreeMap<>();//key is the array index whose ordinal value is the coordinate
	TreeMap<Integer,Integer> tmTemp;//Read progress with, click Load to overwrite tm
	static int width = Toolkit.getDefaultToolkit().getScreenSize().width;
	static int height = Toolkit.getDefaultToolkit().getScreenSize().height;//Get the resolution of the screen
    boolean isReading = false;
	boolean upCanPress = true;
	boolean downCanPress = false;//Three variables for reading data

    public Gb() {
		this.setTitle("Gobang Game Completion");
		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);//Determine the size of the interface and the way it is colored as RGB
		Graphics g1 = buf.createGraphics();

            //option
			
			//Background and board background color
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//background color
			g1.fill3DRect(0, 0, 900, 800, true);//Painting: (Start coordinate X, Start coordinate Y, End coordinate X, End coordinate Y)
			g1.setColor(new Color(235,177,13));//Chess board color
			g1.fill3DRect(60, 60, 680, 680, true);
			
			//Checkerboard Line
			for (int i = 80; i <= 720; i += 40) {
				g1.setColor(Color.BLACK);
				g1.drawLine(80, i, 720, i);
				g1.drawLine(i, 80, i, 720);
			}
			
			//Black and White Chess in Upper Right Corner
            //Used to remind the next chess piece what color it is
			if(isBlack) {
				g1.setColor(Color.BLACK);
				g1.fillOval(770, 50, 30, 30);
			}else {
				g1.setColor(Color.WHITE);
				g1.fillOval(770, 50, 30, 30);
			}
			
			//Music key
            //Pictures in the resource folder, open under the project file
			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);
			
			//Function keys
			g1.setFont(new Font("Microsoft YaHei",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);
            //Read function, Click to read the file, related keys appear after the function is turned on
			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("Restart", 780, 125);
			g1.drawString("Regret Chess", 780, 195);
			g1.drawString("Confession", 780, 265);
			g1.drawString("Save Progress", 780, 335);
			g1.drawString("Read Progress", 780, 405);
			if(isReading) {
				g1.drawString("Previous step", 780, 475);
				g1.drawString("Load Game", 780, 545);
				g1.drawString("Next step", 780, 615);
			}
			g1.drawString("Back to the lobby", 780, 685);
			
			//Chess Fill Color
			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;
						//Judges the chess piece color true to black false to white
						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);
						}
					}
				}
			}

}

Construction of chess subclasses:

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;
	}
	

	
	
}

Storage ideas for chess pieces and board:

The chess board is 17*17 (non-standard, decided by a stroke of the head at that time). Generally speaking, it is very brain-friendly to store the information of the chess board with a two-dimensional array chesses[17][17]. However, the two-dimensional array is inconvenient to store directly with chesses[17 * 17]. It is also well understood that the array index corresponds to each chess X + Y * 17 (You may need to understand this), or the coordinates inverted by the index are: X = i (array index)%17; Y = i / 17;.

chesses[17 * 17] is the data stored on the board. The next chess piece will create a new chess object at the corresponding index and draw an image of the chess piece at the corresponding coordinates.

Falls are stored in TreeMap, key s in the order of falls (1,2,3,4...), and value s in the one-dimensional array index of the pieces.

The advantage of this storage is that it is easy to read the data and read the archive.

Of course, there is another way of thinking:

Because the order OrderNum is declared in the Chess class, it is also convenient to use ArrayList to store the drop information and the board information directly.

Chess pieces:

           //If the game is over, canPlay = false
            if(canPlay) {
                //Chess pieces
				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) {
					//Fill
					if(chesses[x + y * 17] == null) {
						chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
						isBlack = !isBlack;
						isSaved = false;
						//Test: Number of chess pieces + abscissa + ordinate
						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;
				}
				//Judging the winner or loser
				isWin(maxLine(chesses[x + y * 17]));
			}

The maxLine method:

In meter shape judgment, the longest number of concatenates in the oblique direction from top to bottom is determined each time a faller falls.

The basic idea: Take the horizontal as an example, to judge the longest conjoints with index (+) 4 after falling a piece, we need to pay attention to one problem, that is, the pieces are on the boundary of the board, we need to add a judgment. If the vertical coordinates change (to another row), the maxLine is recalculated. If it is a vertical judgment, the X coordinates need to be monitored for change; if it is oblique, the Y coordinates need to be monitored, and if it changes to 2, the maxLine is recalculated.

//Judging the Longest Connector Number
	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;
		
			//Horizontal Judgment 
			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;
				}
			}
		//Vertical Judgment
		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;
				}
			}
		}
		
		//Oblique\
		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;
				}
			}
		}
		
		// Oblique/
		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 method:

//Decide whether or not to win, enter the longest number of conjoins
	public void isWin(int i) {
		if (i >= 5) {
			canPlay = false;
			if(isBlack == true) {
				JOptionPane.showMessageDialog(this, "White Win");
			}else {
				JOptionPane.showMessageDialog(this, "Black Win");
			}
		}
	}

canPlay is used to control whether or not you can play chess. After a game has won or lost, you can't continue falling. You need to repent or start again before you can continue.

This achieves the basic function of chess. As for restart, repentance and other functions, it is easy to achieve them in the single-machine version, then the code will be po oled out, first a slightly more complex reading function:

First look at the effect map:

When you have selected the document, (Save one without saving the document) The three function keys "Previous Step", "Load Game", "Next Step", "Previous Step" and "Next Step" pop up It is used to read the files, to know the order of the falls, and to pick the desired progress to start the game. Click Load to start a game from the board in the current picture. Then, the three function keys disappear until the progress is read again.

The drawing implementation code of the function key is in the drawing, and the function implementation code is as follows:

if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 380 && e.getY() <= 420) {//Here are the coordinates
				int isYes = JOptionPane.showConfirmDialog(this, "Whether to read progress","readload",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					isReading = true;
					readLoad();//A defined method, specified in the following code block, that pops up a box to select a file
					tmTemp = new TreeMap<>();
					for(Map.Entry<Integer, Integer> entry : tm.entrySet()) {
						tmTemp.put(entry.getKey(), entry.getValue());
					}
					
				}
			}
			/*
			 * Extensions in Reading
			 */
			if(isReading) {
				canPlay = false;
				canPress = false;
				int SIZE = tm.size();
				int count = tmTemp.size();
				//Previous step
				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 );
				}
				//Load
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 520 && e.getY() <= 560) {
					isReading = false;
					restart();
					for(Map.Entry<Integer, Integer> 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();
				}
				//Next step
				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 method:

//Read function, pop-up window selection to save documents
	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();//Defined method of reopening: board empty, archive empty...
			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, "File read error\r\n Chess repeat");
						break;
					}
				}
			} catch (Exception e1) {
				e1.printStackTrace();
			}finally {
				if(br != null) {
					try {
						br.close();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}

Involves the use of classes for IO streams and pop-ups, and how to archive can be modified to suit your own development needs.

When reading an archive, because there may be more than one Previous Step and Next Step, here you choose to create a new TreeMap to copy the archive. The purpose of this TreeMap is to save the complete archive file. The next step is to implement the new TreeMap created before us to archive the file. This ensures repeating the previous and next steps.

When you click Load Game, copy the temporary TreeMap into the TreeMap where the data is stored on the board.

A judgment will be made when the previous or next step is not possible. The keys that will not be executed (for example, when you start reading the file, you cannot click Next Step, or when there are no pieces on the board, you cannot click Back Step) In the drawing code block, just change the last value of the key method to false. The canPress Boolean variable is defined to do this.

Here is the implementation code for the other functions, and again, the implementation of the drawing is in the top block of code.

/*
			 * Restart functionality
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
				if(canPlay == true) {
					if(countNum == 1) {
						JOptionPane.showMessageDialog(this, "The board has no pieces");
					}else {
						int isYes = JOptionPane.showConfirmDialog(this, "Win or lose, start over or not","restart",JOptionPane.YES_NO_OPTION);
						if(isYes== 0) {
							restart();
						}
					}
				}else {
					restart();
				}
			}
			/*
			 *Regret Chess Function
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
				int isYes = JOptionPane.showConfirmDialog(this, "Regret Chess","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, "Chess board has no pieces");
					}
				}else {
					//JOptionPane.showMessageDialog(this, "Regret for failure");
				}
			}
			/*
			 * Save function: drop order and coordinate array number
			 */	
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
				int isYes = JOptionPane.showConfirmDialog(this, "Whether to save the game","saveload",JOptionPane.YES_NO_OPTION);
				if(isYes == 0) {
					saveLoad();
				}
			}


            /*
			 * Back to the lobby
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
				if(isSaved == true) {
					int isYes = JOptionPane.showConfirmDialog(this, "Exit or not","exit",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						page = 1;
						
						restart();
						this.repaint();
					}
				}else {
					int isYes = JOptionPane.showConfirmDialog(this, "Chess scores are not saved. Do you want to save them before exiting?","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 {
						
					}
				}
			}
                /*
				 * Subscription function
				 */
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
					int isYes = JOptionPane.showConfirmDialog(this, "Whether to admit defeat or not","give up",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						canPlay = false;
						if(isBlack == true) {
							JOptionPane.showMessageDialog(this, "White Win");
						}else {
							JOptionPane.showMessageDialog(this, "Black Win");
						}	
					}
				}

Method code:

//Restart
	public void restart() {
		canPlay = true;
		isBlack = true;
		isSaved = false;
		chesses = new Chess[17 * 17];
		countNum = 1;
		tm = new TreeMap<Integer,Integer>();
		this.repaint();
	}
//Save function, pop-up window save
	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<Integer,Integer> entry : tm.entrySet()) {
					bw.write(entry.getKey() + " " + entry.getValue());
					bw.newLine();
					bw.flush();
				}
			}catch(IOException ee){
				System.out.println("IO abnormal");
				ee.printStackTrace();
			}finally{
				if(bw != null) {
					try {
						bw.close();
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}

This basic single-machine version is complete. Because the code is cut from the full version, it's messy, but that's okay, there's a summary of all the code at the end.

Another difficult feature to achieve is networking. (After half a month of overnight to achieve this function, of course, it is not difficult to recall now, I have not done well in my own foundation.)

There are many ideas for networking. You can set up a server class. The code of the board class only needs to complete the socket to connect to the server. Of course, the server construction will take a lot of effort. After all, there is matching (opening a room) and prohibiting others from joining these functions needs to be considered.

I chose another simple idea: Checkerboard inherits the Runnable class, overrides the run method, and when I click on the networking function key, opens a thread in which a ServerSocket receives information from another client.

When you need to send information, call the send method and create a new socket to connect to the other server to complete the information interaction.

There is also a mouthful to add here. The information of a falling piece is very understandable: When a falling piece is dropped, just give the index of my falling piece in the chesses[] array to the other side, then what about the function keys?

I choose to use constants instead. Checkerboard pieces only need a range of 0 to 17*17 - 1. Starting with 17*17, they can be defined as information for function keys. Specific code:

@Override
	public void run() {
		// TODO Auto-generated method stub
		
		System.out.println("implement run()");
		String line = null;
		try {
			server = new ServerSocket(Integer.parseInt(yourPort));
			while(true) {
				matchsocket = server.accept();
				System.out.println("Connection Successful");
				obr = new BufferedReader(new InputStreamReader(matchsocket.getInputStream()));
				line = obr.readLine();
				int inputNum =Integer.parseInt(line.trim());
					
					
				System.out.println("Read data" + 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("send data" + 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) {
			//You can only restart after you have judged the winner or loser
			restartWaitting = true;
			JOptionPane.showMessageDialog(this, "The opponent wants another round");
		}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, "The other side requests repentance and agrees","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, "Take the lead");
			}else {
				send(ONLINE_YOURTURN);
				JOptionPane.showMessageDialog(this, "Postmaster");
			}
		}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, "The other side does not agree to repent");
			regrateCanPress = true;
		}else if(ONLINE_SURRENDER == i) {
			canPlay = false;
			JOptionPane.showMessageDialog(this, "Win: The other side admits defeat");
		}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, "Game over");
		}
			
	}

Three methods are used to implement [Open Server] [Send Information] and [Translate Information].

After that, we only need to make some adjustments on the single-machine version. Each time we make changes to the chess board, we pass the corresponding information to the other side, which is translated by the translator() method and then make corresponding actions on the board.

All the code is here:

This is the main chessboard class, where almost all functions are implemented (arguably not):

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;//Document Stream

	Thread onlineThread = new Thread(this);
	BufferedReader obr = null;
	BufferedWriter obw = null;//Networked Streams
	Chess[] chesses = new Chess[17 * 17];
	TreeMap<Integer,Integer> tm = new TreeMap<>();//key is the array index whose ordinal value is the coordinate
	TreeMap<Integer,Integer> tmTemp;//Read progress with, click Load to overwrite 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 is the start interface. 2 For single machine interface. 3 is the online interface.
	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("Gobang game incomplete version");
		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();
		}
		
	}
	
	
	//Draw a board
	public void paint(Graphics g) {
		BufferedImage buf = new BufferedImage(900, 800, BufferedImage.TYPE_INT_RGB);
		Graphics g1 = buf.createGraphics();
		
		
		
		
		/**
		 * Start Interface
		 */
		if(page == 1 ) {
			//Background and board background color
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//background
			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 ) {
			
			//option
			
			//Background and board background color
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//background
			g1.fill3DRect(0, 0, 900, 800, true);
			g1.setColor(new Color(235,177,13));//Chess board color
			g1.fill3DRect(60, 60, 680, 680, true);
			
			//Checkerboard Line
			for (int i = 80; i <= 720; i += 40) {
				g1.setColor(Color.BLACK);
				g1.drawLine(80, i, 720, i);
				g1.drawLine(i, 80, i, 720);
			}
			
			//Black and White Chess in Upper Right Corner
			if(isBlack) {
				g1.setColor(Color.BLACK);
				g1.fillOval(770, 50, 30, 30);
			}else {
				g1.setColor(Color.WHITE);
				g1.fillOval(770, 50, 30, 30);
			}
			
			//Music key
			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);
			
			//Function keys
			g1.setFont(new Font("Microsoft YaHei",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("Restart", 780, 125);
			g1.drawString("Regret Chess", 780, 195);
			g1.drawString("Confession", 780, 265);
			g1.drawString("Save Progress", 780, 335);
			g1.drawString("Read Progress", 780, 405);
			if(isReading) {
				g1.drawString("Previous step", 780, 475);
				g1.drawString("Load Game", 780, 545);
				g1.drawString("Next step", 780, 615);
			}
			g1.drawString("Back to the lobby", 780, 685);
			
			//Chess Fill Color
			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;
						//Judges the chess piece color true to black false to white
						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  */ {
			//option
			
			//Background and board background color
			//http://zhongguose.com/#zhizihuang
			g1.setColor(new Color(248,232,193));//background
			g1.fill3DRect(0, 0, 900, 800, true);
			g1.setColor(new Color(235,177,13));//Chess board color
			g1.fill3DRect(60, 60, 680, 680, true);
			
			//Checkerboard Line
			for (int i = 80; i <= 720; i += 40) {
				g1.setColor(Color.BLACK);
				g1.drawLine(80, i, 720, i);
				g1.drawLine(i, 80, i, 720);
			}
			
			//Black and White Chess in Upper Right Corner
			if(isBlack) {
				g1.setColor(Color.BLACK);
				g1.fillOval(770, 50, 30, 30);
			}else {
				g1.setColor(Color.WHITE);
				g1.fillOval(770, 50, 30, 30);
			}
			
			//Music key
			
			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);
			
			//Function keys
			g1.setFont(new Font("Microsoft YaHei",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("Restart", 780, 125);
			g1.drawString("Regret Chess", 780, 195);
			g1.drawString("Confession", 780, 265);
			g1.drawString("Save Progress", 780, 335);
			
			g1.drawString("Back to the lobby", 780, 685);
			
			//Chess Fill Color
			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;
						//Judges the chess piece color true to black false to white
						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);
	}
	
	//Mouse Click to Play Chess
	boolean canPlay = true;
	boolean isBlack = true;
	boolean isSaved = false;
	boolean canPress = true;
	static boolean isFilled = false;
	int countNum = 1;
	public void mousePressed(MouseEvent e) {
		/*
		 * Turn on music, turn off music
		 */
		if(e.getX() >= 820 && e.getX() <= 850 && e.getY() >= 20 && e.getY() <= 80) {
			playMusic =!playMusic;
			this.repaint();
		}
		
		
		
		
		/**
		 * Three page 1,2,3  
		 */
	
		if (page == 1 && !hasWindow) {
			//Man-machine button
			if(e.getX() >= 200 && e.getX() <= 400 && e.getY() >= 300 && e.getY() <= 500) {
				page = 2;
				this.repaint();
			}
			//Networked Battle Button
			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("Random number is" + myNum);
			}
		
			
			
			
			
			
		}else if(page == 2 && !hasWindow) {
			/*
			 * Restart functionality
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
				if(canPlay == true) {
					if(countNum == 1) {
						JOptionPane.showMessageDialog(this, "The board has no pieces");
					}else {
						int isYes = JOptionPane.showConfirmDialog(this, "Win or lose, start over or not","restart",JOptionPane.YES_NO_OPTION);
						if(isYes== 0) {
							restart();
						}
					}
				}else {
					restart();
				}
			}
			/*
			 *Regret Chess Function
			 */
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
				int isYes = JOptionPane.showConfirmDialog(this, "Regret Chess","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, "Chess board has no pieces");
					}
				}else {
					//JOptionPane.showMessageDialog(this, "Regret for failure");
				}
			}
			/*
			 * Save function: drop order and coordinate array number
			 */	
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
				int isYes = JOptionPane.showConfirmDialog(this, "Whether to save the game","saveload",JOptionPane.YES_NO_OPTION);
				if(isYes == 0) {
					saveLoad();
				}
			}
			/*
			 * Read Progress Function
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 380 && e.getY() <= 420) {
				int isYes = JOptionPane.showConfirmDialog(this, "Whether to read progress","readload",JOptionPane.YES_NO_OPTION);
				if(isYes== 0) {
					isReading = true;
					readLoad();
					tmTemp = new TreeMap<>();
					for(Map.Entry<Integer, Integer> entry : tm.entrySet()) {
						tmTemp.put(entry.getKey(), entry.getValue());
					}
					
				}
			}
			/*
			 * Extensions in Reading
			 */
			if(isReading) {
				canPlay = false;
				canPress = false;
				int SIZE = tm.size();
				int count = tmTemp.size();
				//Previous step
				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 );
				}
				//Load
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 520 && e.getY() <= 560) {
					isReading = false;
					restart();
					for(Map.Entry<Integer, Integer> 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();
				}
				//Next step
				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();
			}
			/*
			 * Back to the lobby
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
				if(isSaved == true) {
					int isYes = JOptionPane.showConfirmDialog(this, "Exit or not","exit",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						page = 1;
						
						restart();
						this.repaint();
					}
				}else {
					int isYes = JOptionPane.showConfirmDialog(this, "Chess scores are not saved. Do you want to save them before exiting?","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) {
				/*
				 * Subscription function
				 */
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
					int isYes = JOptionPane.showConfirmDialog(this, "Whether to admit defeat or not","give up",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						canPlay = false;
						if(isBlack == true) {
							JOptionPane.showMessageDialog(this, "White Win");
						}else {
							JOptionPane.showMessageDialog(this, "Black Win");
						}	
					}
				}
				//Chess pieces
				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) {
					//Fill
					if(chesses[x + y * 17] == null) {
						chesses[x + y * 17] = new Chess(x,y,countNum++,isBlack);
						isBlack = !isBlack;
						isSaved = false;
						//Test: Number of chess pieces + abscissa + ordinate
						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;
				}
				//Judging the winner or loser
				isWin(maxLine(chesses[x + y * 17]));
			}
		
		
		
		
		
			
			
			
		}else if(page == 3 && !hasWindow) {
			
			System.out.println("click");
			if(!hasWindow && !threadIsStart) {
				Thread t = new Thread(this);
				t.start();

				System.out.println("Thread Open");
				send(myNum);
				threadIsStart = true;
			}
			
//			if(threadIsStart) {
//				
//			}
			
			
			/*
			 * Restart functionality
			 */
			if(canPress && restartCanPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 100 && e.getY() <= 140 ) {
				if(canPlay) {
					JOptionPane.showMessageDialog(this, "Don't give up!");
				}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();
					}
				}
			}
			/*
			 *Regret Chess Function
			 */
			if(canPress && regrateCanPress && !yourTurn && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 170 && e.getY() <= 210) {
				int isYes = JOptionPane.showConfirmDialog(this, "Regret Chess","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, "Chess board has no pieces");
					}
				}
			}
			/*
			 * Save function: drop order and coordinate array number
			 */	
			if(canPress && e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 310 && e.getY() <= 350) {
				int isYes = JOptionPane.showConfirmDialog(this, "Whether to save the game","saveload",JOptionPane.YES_NO_OPTION);
				if(isYes == 0) {
					saveLoad();
				}
			}
			
			/*
			 * Back to the lobby
			 */
			if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 660 && e.getY() <= 700) {
				if(isSaved == true) {
					int isYes = JOptionPane.showConfirmDialog(this, "Exit or not","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, "Chess scores are not saved. Do you want to save them before exiting?","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) {
				/*
				 * Subscription function
				 */
				if(e.getX() >= 770 && e.getX() <= 870 && e.getY() >= 240 && e.getY() <= 280) {
					int isYes = JOptionPane.showConfirmDialog(this, "Whether to admit defeat or not","give up",JOptionPane.YES_NO_OPTION);
					if(isYes== 0) {
						canPlay = false;
						send(ONLINE_SURRENDER);
						
						JOptionPane.showMessageDialog(this, "Confession");
						
					}
				}
				//Chess pieces
				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) {
					//Fill
					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;
						//Test: Number of chess pieces + abscissa + ordinate
						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;
				}
				//Judging the winner or loser
				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 can be used to set the volume from 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) {// Determine Play/Pause Status
				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();
		}


	}
	
	//Judging the Longest Connector Number
	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;
		
			//Horizontal Judgment 
			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;
				}
			}
		//Vertical Judgment
		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;
				}
			}
		}
		
		//Oblique\
		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;
				}
			}
		}
		
		// Oblique/
		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;	
	}
	//Restart
	public void restart() {
		canPlay = true;
		isBlack = true;
		isSaved = false;
		chesses = new Chess[17 * 17];
		countNum = 1;
		tm = new TreeMap<Integer,Integer>();
		this.repaint();
	}
	//Network Restart
	public void onlineRestart() {
		canPlay = true;
		isBlack = true;
		isSaved = false;
		chesses = new Chess[17 * 17];
		countNum = 1;
		tm = new TreeMap<Integer,Integer>();
		restartWaitting = false;
		restartCanPress = true;
		this.repaint();
	}
	
	//Decide whether or not to win, enter the longest number of conjoins
	public void isWin(int i) {
		if (i >= 5) {
			canPlay = false;
			if(isBlack == true) {
				JOptionPane.showMessageDialog(this, "White Win");
			}else {
				JOptionPane.showMessageDialog(this, "Black Win");
			}
		}
	}
	
	public void onlineIsWin(int i) {
		if (i >= 5) {
			canPlay = false;
			if(yourTurn) {
				JOptionPane.showMessageDialog(this, "Game over!");
			}else {
				send(ONLINE_WIN);
			}
		}
	}
	//Save function, pop-up window save
	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<Integer,Integer> entry : tm.entrySet()) {
					bw.write(entry.getKey() + " " + entry.getValue());
					bw.newLine();
					bw.flush();
				}
			}catch(IOException ee){
				System.out.println("IO abnormal");
				ee.printStackTrace();
			}finally{
				if(bw != null) {
					try {
						bw.close();
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
				}
			}
			isSaved = true;
		}
	}
	//Read function, pop-up window selection to save documents
	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, "File read error\r\n Chess repeat");
						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) {
			//You can only restart after you have judged the winner or loser
			restartWaitting = true;
			JOptionPane.showMessageDialog(this, "The opponent wants another round");
		}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, "The other side requests repentance and agrees","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, "Take the lead");
			}else {
				send(ONLINE_YOURTURN);
				JOptionPane.showMessageDialog(this, "Postmaster");
			}
		}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, "The other side does not agree to repent");
			regrateCanPress = true;
		}else if(ONLINE_SURRENDER == i) {
			canPlay = false;
			JOptionPane.showMessageDialog(this, "Win: The other side admits defeat");
		}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, "Game over");
		}
			
	}
	
	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("send data" + 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("implement run()");
		String line = null;
		try {
			server = new ServerSocket(Integer.parseInt(yourPort));
			while(true) {
				matchsocket = server.accept();
				System.out.println("Connection Successful");
				obr = new BufferedReader(new InputStreamReader(matchsocket.getInputStream()));
				line = obr.readLine();
				int inputNum =Integer.parseInt(line.trim());
					
					
				System.out.println("Read data" + 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
		
	}


	
	
	
}

Create a new chess subclass:

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;
	}
	

	
	
}

A pop-up window class for networking:

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("Network Link");
	     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()); // Set Layout

        add(new JLabel("Your IP:"));
        text1 = new JTextField(10);
        add(text1);
        
        add(new JLabel("Your Port:"));
        text2 = new JTextField(10);
        add(text2);
        add(new JLabel("Opponent IP:"));
        text3 = new JTextField(10);
        add(text3);
        add(new JLabel("Opponent Port:"));
        text4 = new JTextField(10);
        add(text4);
        
        add(new JLabel("Button:"));
        button = new JButton("Start Online");
        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, "Please enter IP And ports");
		}
		
		
	}
}

Execute main class:

package mygobang;

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

Finally, give an effect picture:

This is the first page, with one computer on the left and networking on the right. In the upper right corner is the background music. Click to pause while the picture changes.

Single machine is the cover page, another networked:

Once you have finished typing, you can start the game. (LAN)

No Click to start online, the small window is not closed, and the board is not operational.

Code source files and used picture music have been uploaded to the cloud, you need to take your own:

Links: https://pan.baidu.com/s/1gg17ibt9eInbJO8GEgZ5ow

Extraction Code: wfnu

 

Of course, there must also be some little buddies who do the work of Gobang, in addition to the specific IO balloon window drawing class, try to see after their own hands.

Keywords: Java Game Development Project

Added by Cagecrawler on Thu, 23 Dec 2021 10:13:03 +0200