学习java的时候一直想要做出一个像样的小游戏,所以就动手做了一个远程联网对战的java五子棋小游戏。这个程序我前前后后也是改动了几次,这次发出来的是最终版本了,虽然还是有很多不足,但本人已经没有精力去修改了
下面开始进入正题:
--------------------------------------------------------------------分割线----------------------------------------------------------------------------
该程序基于java语言,由于是远程双人对战,所以本程序由服务器和客户端组成,运行时需要先运行服务器。界面使用的是javafx而非javaSwing。联机对战用的是javaSocket通信,但使用javaSocket进行远程联机对战需要主机有公网ip(对计算机网络知识有点了解都知道,我们上网所用的ip地址基本都是路由器或者运营商提供的局域网ip地址,这种ip地址是不能在外网直接访问到的。要实现直接被访问的效果需要使用公网ip),没有的话可以自行百度怎么使用javaSocket实现远程通信,由于一些原因笔者就不说了。。。如果距离很近,可以连接同一wifi使用局域网直接对战。由于是早期作品,所以代码可读性比较差,本人也懒得重构了~~~
由于要尽量把功能做到和市面上的五子棋程序差不多,比如各种对话框提示,实时聊天,玩家退出提示啥的,所以代码比较多,对初学者来说并不是那么容易看懂,不过还是有不错的参考价值,是一个不错的小游戏。
服务器:
相当于一个大厅,自动等待玩家加入,为每两个玩家创建一个房间,实现多组玩家同时在线对战
客户端:
玩家游戏界面
游戏界面如下:
服务器:
玩家一:
玩家二:
服务器代码:
package server;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import zhou.pane.FiveChessGameConstants;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
public class FiveChessGameServer extends Application implements FiveChessGameConstants
{
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
// UI界面
TextArea textArea = new TextArea();
textArea.setEditable(false);
Scene scene = new Scene(new Pane(textArea), 650, 250);
// 文本域背景透明
// scene.getStylesheets().add(getClass().getResource("BackgroundColor.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setTitle("五子棋服务器");
primaryStage.show();
// 关闭线程
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
@Override
public void handle(WindowEvent event)
{
System.exit(0);
}
});
new Thread(() ->
{
ServerSocket serverSocket = null;
try
{
// 一个服务器端口
serverSocket = new ServerSocket(9520);
Platform.runLater(() ->
{
textArea.appendText(new Date() + ":服务器端口为9520!\n");
});
// 等待用户连接
while (true)
{
Platform.runLater(() ->
{
textArea.appendText("\n" + new Date() + ":等待玩家连接。。。\n");
});
// 等待用户一连接
Socket user1 = serverSocket.accept();
Platform.runLater(() ->
{
textArea.appendText(new Date() + ":玩家一连接成功!\n");
textArea.appendText("玩家一的名称和IP地址为:" + user1.getInetAddress().getHostName() + " "
+ user1.getInetAddress().getHostAddress() + "\n");
});
// IO流
DataOutputStream toUser1 = new DataOutputStream(user1.getOutputStream());
// 提示玩家一其为黑子
toUser1.writeInt(player1);
// 等待用户二连接
Socket user2 = serverSocket.accept();
Platform.runLater(() ->
{
textArea.appendText(new Date() + ":玩家二连接成功!\n");
textArea.appendText("玩家二的名称和IP地址为:" + user2.getInetAddress().getHostName() + " "
+ user2.getInetAddress().getHostAddress() + "\n");
});
// IO流
DataOutputStream toUser2 = new DataOutputStream(user2.getOutputStream());
// 提示玩家二其为白子
toUser2.writeInt(player2);
// 开始一局游戏
new Thread(new HandleASession(user1, user2)).start();
}
} catch (IOException e)
{
e.printStackTrace();
}
}).start();
}
}
package server;
import zhou.pane.FiveChessGameConstants;
import zhou.pane.UtilsConnet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
//一个游戏进程
class HandleASession implements Runnable, FiveChessGameConstants
{
private int row, col;
private boolean waiting = true;
private boolean chessBack = false;
//游戏线程结束标志
private boolean exit = false;
//两个玩家
private Socket user1;
private Socket user2;
//容器
private CopyOnWriteArrayList<Socket> all = new CopyOnWriteArrayList<Socket>();
//棋盘
private int[][] cell = new int[15][15];
private DataOutputStream toUser1;
private DataOutputStream toUser2;
public HandleASession(Socket user1, Socket user2)
{
this.user1 = user1;
this.user2 = user2;
all.add(user1);
all.add(user2);
try
{
toUser1 = new DataOutputStream(this.user1.getOutputStream());
toUser2 = new DataOutputStream(this.user2.getOutputStream());
}
catch (IOException e){
System.out.println("-----2-----");
release();
}
//两个接收玩家信息的类定义
new Thread(new ChatWith(user1, user2)).start();
new Thread(new ChatWith(user2,user1)).start();
//初始化棋盘
for (int i = 0; i < cell.length; i++) {
for (int j = 0; j < cell[0].length; j++) {
cell[i][j] = 0;
}
}
}
@Override
public void run()
{
try
{
// 告诉玩家 1 开始
toUser1.writeInt(0);
} catch (Exception e)
{
release();
}
// 开始一场游戏
while (!exit)
{
try
{
// 判断是否需要等待
waitingForPleyer();
// 玩家一棋子落子
cell[row][col] = 1;
// 判断玩家一是否胜出
if (isWon() == 1)
{
toUser1.writeInt(1);
toUser1.writeInt(player1_won);
toUser2.writeInt(1);
toUser2.writeInt(player1_won);
sendMove(toUser2, row, col);
// 游戏结束
exit = true;
break;
} else if (isFull())
{
toUser1.writeInt(1);
toUser1.writeInt(draw);
toUser2.writeInt(1);
toUser2.writeInt(draw);
sendMove(toUser2, row, col);
// 游戏结束
exit = true;
break;
} else
{
// 没有玩家胜出,继续游戏
toUser2.writeInt(1);
toUser2.writeInt(keep);
sendMove(toUser2, row, col);
}
// 判断是否需要等待
waitingForPleyer();
cell[row][col] = 2;
if (isWon() == 2)
{
toUser1.writeInt(1);
toUser1.writeInt(player2_won);
toUser2.writeInt(1);
toUser2.writeInt(player2_won);
sendMove(toUser1, row, col);
// 游戏结束
exit = true;
break;
} else
{
// 没有玩家胜出,继续游戏
toUser1.writeInt(1);
toUser1.writeInt(keep);
sendMove(toUser1, row, col);
}
}
catch (Exception e)
{
release();
}
}
}
//判断是否需要等待
private void waitingForPleyer() throws InterruptedException{
while(waiting){
//判断是否悔棋
if(chessBack)
{
cell[row][col] = 0;
chessBack = false;
}
Thread.sleep(100);
}
waiting = true;
}
//判断是否胜出
private int isWon() {
int user;
for (int i = 0; i < cell.length; i++) {//判断一行中是否有四个棋子相同
for (int j = 0; j < cell[0].length && cell[0].length - j >= 5; j++) {//只需判断到倒数第四个棋子,往后则不满足四子相同
user = cell[i][j];
int t = 1;
for (int k = j; k < cell[0].length - 1; k++) {//采用col-1作为限定条件,避免比较时数组越界
if (user != 0 && user == cell[i][k + 1]) {
t++;
if (t == 5) {
return user;
}
} else {
break;
}
}
}
}
for (int j = 0; j < cell[0].length; j++) {//判断某一列中是否有五个棋子相同
for (int i = 0; i < cell.length && cell.length - i >= 5; i++) {
user = cell[i][j];
int t = 1;//判断条件,达到五个棋子就退出
for (int k = i; k < cell.length - 1; k++) {
if (user != 0 && user == cell[k + 1][j]) {
t++;
if (t == 5) {
return user;
}
} else {
break;
}
}
}
}
for (int i = 0; i < cell.length && cell.length - i >= 5; i++) {//判断某一对角线上是否有五个棋子相同(右上左下对角线)
for (int j = 0; j < cell[0].length && cell[0].length - j >= 5; j++) {
user = cell[i][j];
int t = 1;
for (int x = i, y = j; x < cell.length - 1 && y < cell[0].length - 1; x++, y++) {
if (user != 0 && user == cell[x + 1][y + 1]) {
t++;
if (t == 5) {
return user;
}
} else {
break;
}
}
}
}
for (int i = 14; i >= 4; i--) {//判断某一对角线上是否有五个棋子相同(右下左上对角线)
//只有当列行数都大于等于5时,才存在对角线上的五子相同情况
for (int j = 0; j < cell[0].length && cell[0].length - j >= 5; j++) {
user = cell[i][j];
int t = 1;
for (int x = i, y = j; x >= 1 && y < cell[0].length - 1; x--, y++) {
if (user != 0 && user == cell[x - 1][y + 1]) {
t++;
if (t == 5) {
return user;
}
}
else {
break;
}
}
}
}
return 3;//行、列、对角线都没有四个棋子相同,返回循环条件3
}//寻找相同的棋子
//移动函数
private void sendMove(DataOutputStream out, int row, int col) throws IOException
{
out.writeInt(row);
out.writeInt(col);
}
//判断棋盘是否已满
private boolean isFull() {
for (int i = 0; i < cell.length; i++) {
for (int j = 0; j < cell[0].length; j++) {
if (cell[i][j] == 0) {
return false;
}
}
}
//已满
return true;
}
/**
* 用于关闭流
*/
private void release()
{
exit = true;
UtilsConnet.close(user1, user2, toUser1, toUser2);
}
/**
* 用户信息交互
* @author 久拥-
*
*/
class ChatWith implements Runnable
{
// 判断数据的类型
private int x;
// 获取聊天内容
private String str1 = "";
private Socket user1;
private Socket user2;
private boolean exit;
private DataInputStream fromUser1;
private DataOutputStream toUser2;
public ChatWith(Socket user1, Socket user2)
{
this.user1 = user1;
this.user2 = user2;
try
{
fromUser1 = new DataInputStream(this.user1.getInputStream());
toUser2 = new DataOutputStream(this.user2.getOutputStream());
} catch (IOException e)
{
release();
}
}
@Override
public void run()
{
while (!exit)
{
try
{
x = fromUser1.readInt();
if (x == 2)
{
str1 = fromUser1.readUTF();
toUser2.writeInt(2);
toUser2.writeUTF(str1);
} else if (x == 1)
{
row = fromUser1.readInt();
col = fromUser1.readInt();
waiting = false;
}
// 悔棋
else if (x == 3)
{
chessBack = true;
toUser2.writeInt(3);
}
// 是否允许悔棋确认
else if (x == 4)
{
Boolean bl = fromUser1.readBoolean();
// 是否允许悔棋标志
toUser2.writeInt(4);
toUser2.writeBoolean(bl);
}
} catch (Exception e)
{
// 玩家异常退出或者逃跑
all.remove(user1);
waiting = false;
try
{
for (Socket socket : all)
{
if (socket == user2)
toUser2.writeInt(20);
}
} catch (IOException e1)
{
e1.printStackTrace();
}
// 释放资源
release();
}
}
}
/**
* 用于关闭流
*/
private void release()
{
exit = true;
UtilsConnet.close(user1, user2, fromUser1, toUser2);
}
public int getRow()
{
return row;
}
public int getCol()
{
return col;
}
public boolean isChessBack()
{
return chessBack;
}
}
}
棋盘界面代码:
package zhou.pane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
/**
* 棋盘类
*/
public class LinePane extends Pane
{
private final static LinePane linePane = new LinePane();
private int[][] chessXY = new int[225][2];
private LinePane()
{
this.display();
}
public static LinePane getInstance()
{
return linePane;
}
public void display()
{
//棋子横坐标
int chessX = 20;
for(int i =0; i < 15 ; i++){
//棋子纵坐标
int chessY = 20;
for(int j =0; j < 15; j++){
//存储各个棋子的坐标
chessXY[i * 15 + j][0] = chessX;
chessXY[i * 15 + j][1] = chessY;
chessY += 50;
}
chessX += 50;
}
//横
Line[] line = new Line[15];
//竖
Line[] line1 = new Line[15];
//getChildren().add(new ImageView("棋盘素材/棋盘背景.jpg"));
//棋盘
int startX = 20, startY = 20, endX = 720, endY = 20;
int startX1= 20, startY1 = 20, endX1 = 20, endY1 = 720;
for(int i = 0; i < 15; i++) {
line[i] = new Line(startX, startY,endX, endY);
line[i].setStroke(Color.BLACK);
getChildren().add(line[i]);
startY += 50;
endY +=50;
line1[i] = new Line(startX1, startY1, endX1, endY1);
line1[i].setStroke(Color.BLACK);
getChildren().add(line1[i]);
startX1 +=50;
endX1 +=50;
}
//棋盘上的五个定位点
Circle[] circle = new Circle[5];
for(int i = 0; i < 5; i++){
if(i == 0){
circle[i] = new Circle(5);
circle[i].setCenterX(370);
circle[i].setCenterY(370);
circle[i].setFill(Color.BLACK);
getChildren().addAll(circle[i]);
}
else if(i == 1){
circle[i] = new Circle(5);
circle[i].setCenterX(220);
circle[i].setCenterY(220);
circle[i].setFill(Color.BLACK);
getChildren().addAll(circle[i]);
}
else if(i == 2){
circle[i] = new Circle(5);
circle[i].setCenterX(520);
circle[i].setCenterY(220);
circle[i].setFill(Color.BLACK);
getChildren().addAll(circle[i]);
}
else if(i == 3){
circle[i] = new Circle(5);
circle[i].setCenterX(220);
circle[i].setCenterY(520);
circle[i].setFill(Color.BLACK);
getChildren().addAll(circle[i]);
}
else{
circle[i] = new Circle(5);
circle[i].setCenterX(520);
circle[i].setCenterY(520);
circle[i].setFill(Color.BLACK);
getChildren().addAll(circle[i]);
}
}
}
public int getChessX(int row, int col)
{
return chessXY[row * 15 + col][0];
}
public int getChessY(int row, int col)
{
return chessXY[row * 15 + col][1];
}
}
代码有点多,这里就不放了,我放在百度网盘,需要的自取:
链接: https://pan.baidu.com/s/1JFSClXIN4ReQoMH2Ms-q7g 提取码: 4ptx
写在最后:
希望对大家有所帮助!