JavaFx-----五子棋(单机版和对战版)
一.项目介绍
使用 JavaFx + MySql + MyBatis 实现单机和网络版五子棋对战.
二.功能介绍
1. 登录
-- 使用MyBatis和JDBC连接数据库, 实现登录功能
-- 使用I/O流,实现本地文件记住密码功能

2.注册
-- 使用MyBatis和JDBC连接数据库, 实现注册功能
-- 注册完密码后,返回登录界面,自动填充注册的用户名和密码

3.网络模式选择

4.单机版
-- 对战
-- 新局
-- 悔棋
-- 保存棋谱
-- 打开棋谱

5.网络版
-- 显示在线用户
-- 连接对战


三. 项目源码

package com.wn.main;
import java.net.InetAddress;
import java.net.UnknownHostException;
import com.wn.pojo.Global;
import com.wn.ui.LoginStage;
import javafx.application.Application;
import javafx.stage.Stage;
public class MainApplication extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
//获取当前用户的本机IP地址
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
//将本机ip存储到全局变量中
Global.myIp = inetAddress.getHostAddress();
new LoginStage().show();
}
}
package com.wn.ui;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.DataHanding;
import com.wn.dao.UserDAO;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.Global;
import com.wn.pojo.User;
import com.wn.pojo.UserStatus;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LoginStage extends Stage implements EventHandler<ActionEvent> {
private Button loginBtn;
private Button registerBtn;
private TextField account;
private PasswordField password;
private CheckBox checkBox;
public LoginStage() {
setTitle("五子棋-登录");
Pane pane = new Pane();
Scene scene = new Scene(pane, 400, 250);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
pane.setBackground(new Background(new BackgroundFill(Color.GAINSBORO, null, null)));
// 设置窗口不可变
setResizable(false);
// 标题:用户登录
Label title = new Label("用户登录");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(150);
title.setLayoutY(30);
// 账户标签
Label accountLabel = new Label("账户 : ");
accountLabel.setFont(new Font("KaiTi", 20));
accountLabel.setLayoutX(80);
accountLabel.setLayoutY(90);
// 账户输入框
account = new TextField();
account.setLayoutX(150);
account.setLayoutY(90);
// 密码标签
Label passwordLabel = new Label("密码 : ");
passwordLabel.setFont(new Font("KaiTi", 20));
passwordLabel.setLayoutX(80);
passwordLabel.setLayoutY(140);
// 密码输入框
password = new PasswordField();
password.setLayoutX(150);
password.setLayoutY(140);
// 记住密码
checkBox = new CheckBox();
checkBox.setLayoutX(80);
checkBox.setLayoutY(180);
Text text = new Text(100, 195,"记住密码");
text.setFont(new Font("KaiTi",15));
// 登录按钮
loginBtn = new Button("登\t录");
loginBtn.setLayoutX(80);
loginBtn.setLayoutY(210);
loginBtn.setDefaultButton(true);
loginBtn.setOnAction(this);
// 注册按钮
registerBtn = new Button("注\t册");
registerBtn.setLayoutX(250);
registerBtn.setLayoutY(210);
registerBtn.setOnAction(this);
// 把元素添加到面板
pane.getChildren().addAll(title, accountLabel, account, passwordLabel,
password, loginBtn, registerBtn,checkBox,text);
// 如果用户刚注册,则在登录界面读取用户注册的账户和密码
String content = null;
if (Global.account != null && Global.password != null) {
this.account.setText(Global.account);
this.password.setText(Global.password);
//读取用户上一次在本机登录的账户和密码
}else if ((content=DataHanding.readLastAccount())!=null) {
String[] strings = content.split(",");
if (strings[strings.length-1].equals(Global.myIp)) {
account.setText(strings[0]);
password.setText(strings[1]);
}
}
}
public void login() {
String accountText = account.getText();
String passwordText = password.getText();
Global.account = accountText;
//用户名或密码不能为空
if (accountText.equals("")||passwordText.equals("")) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用户名或密码不能为空");
alert.show();
return;
}
//获取用户当前登录状态,如果已登录,不能重复登录
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
UserStatus status = statusDAO.selectInfoByAccount(accountText);
if (status.getStatus() == 1) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("当前用户已在线,不能重复登录");
alert.show();
return;
}
// 获取UserDAO的实现类
UserDAO userDAO = DAOUtils.getMapper(UserDAO.class);
// 根据用户输入的account获得User对象
User user = userDAO.getByAccount(accountText);
if (user != null && user.getPassword().equals(passwordText)) {
//登录成功,判断用户是否选择记住密码
if (checkBox.isSelected()) {
//用户选择记住密码,调用记住密码方法
DataHanding.rememberLastAccount(accountText, passwordText,Global.myIp);
}
//更新用户状态为在线状态
statusDAO.updateStatusByAccount(accountText,Global.myIp, 1);
// 已设置自动提交事务
// 打开游戏模式选择界面
new ModeChooseStage().show();
this.close();
} else {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用户名或密码错误 ,登录失败");
alert.showAndWait();
}
}
@Override
public void handle(ActionEvent event) {
//获取用户点击的按钮
Button btn = (Button) event.getSource();
String text = btn.getText();
switch (text) {
case "登\t录":
login();
break;
case "注\t册":
new RegisterStage().show();
this.close();
break;
}
}
}
package com.wn.ui;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.DataHanding;
import com.wn.dao.UserDAO;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.Global;
import com.wn.pojo.User;
import com.wn.pojo.UserStatus;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LoginStage extends Stage implements EventHandler<ActionEvent> {
private Button loginBtn;
private Button registerBtn;
private TextField account;
private PasswordField password;
private CheckBox checkBox;
public LoginStage() {
setTitle("五子棋-登录");
Pane pane = new Pane();
Scene scene = new Scene(pane, 400, 250);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
pane.setBackground(new Background(new BackgroundFill(Color.GAINSBORO, null, null)));
// 设置窗口不可变
setResizable(false);
// 标题:用户登录
Label title = new Label("用户登录");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(150);
title.setLayoutY(30);
// 账户标签
Label accountLabel = new Label("账户 : ");
accountLabel.setFont(new Font("KaiTi", 20));
accountLabel.setLayoutX(80);
accountLabel.setLayoutY(90);
// 账户输入框
account = new TextField();
account.setLayoutX(150);
account.setLayoutY(90);
// 密码标签
Label passwordLabel = new Label("密码 : ");
passwordLabel.setFont(new Font("KaiTi", 20));
passwordLabel.setLayoutX(80);
passwordLabel.setLayoutY(140);
// 密码输入框
password = new PasswordField();
password.setLayoutX(150);
password.setLayoutY(140);
// 记住密码
checkBox = new CheckBox();
checkBox.setLayoutX(80);
checkBox.setLayoutY(180);
Text text = new Text(100, 195,"记住密码");
text.setFont(new Font("KaiTi",15));
// 登录按钮
loginBtn = new Button("登\t录");
loginBtn.setLayoutX(80);
loginBtn.setLayoutY(210);
loginBtn.setDefaultButton(true);
loginBtn.setOnAction(this);
// 注册按钮
registerBtn = new Button("注\t册");
registerBtn.setLayoutX(250);
registerBtn.setLayoutY(210);
registerBtn.setOnAction(this);
// 把元素添加到面板
pane.getChildren().addAll(title, accountLabel, account, passwordLabel,
password, loginBtn, registerBtn,checkBox,text);
// 如果用户刚注册,则在登录界面读取用户注册的账户和密码
String content = null;
if (Global.account != null && Global.password != null) {
this.account.setText(Global.account);
this.password.setText(Global.password);
//读取用户上一次在本机登录的账户和密码
}else if ((content=DataHanding.readLastAccount())!=null) {
String[] strings = content.split(",");
if (strings[strings.length-1].equals(Global.myIp)) {
account.setText(strings[0]);
password.setText(strings[1]);
}
}
}
public void login() {
String accountText = account.getText();
String passwordText = password.getText();
Global.account = accountText;
//用户名或密码不能为空
if (accountText.equals("")||passwordText.equals("")) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用户名或密码不能为空");
alert.show();
return;
}
//获取用户当前登录状态,如果已登录,不能重复登录
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
UserStatus status = statusDAO.selectInfoByAccount(accountText);
if (status.getStatus() == 1) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("当前用户已在线,不能重复登录");
alert.show();
return;
}
// 获取UserDAO的实现类
UserDAO userDAO = DAOUtils.getMapper(UserDAO.class);
// 根据用户输入的account获得User对象
User user = userDAO.getByAccount(accountText);
if (user != null && user.getPassword().equals(passwordText)) {
//登录成功,判断用户是否选择记住密码
if (checkBox.isSelected()) {
//用户选择记住密码,调用记住密码方法
DataHanding.rememberLastAccount(accountText, passwordText,Global.myIp);
}
//更新用户状态为在线状态
statusDAO.updateStatusByAccount(accountText,Global.myIp, 1);
// 已设置自动提交事务
// 打开游戏模式选择界面
new ModeChooseStage().show();
this.close();
} else {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用户名或密码错误 ,登录失败");
alert.showAndWait();
}
}
@Override
public void handle(ActionEvent event) {
//获取用户点击的按钮
Button btn = (Button) event.getSource();
String text = btn.getText();
switch (text) {
case "登\t录":
login();
break;
case "注\t册":
new RegisterStage().show();
this.close();
break;
}
}
}
package com.wn.ui;
import com.wn.CommonUtils.DAOUtils;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.ChessMode;
import com.wn.pojo.Global;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class ModeChooseStage extends Stage implements EventHandler<ActionEvent> {
Pane pane;
public ModeChooseStage() {
setTitle("五林大会");
pane = new Pane();
Scene scene = new Scene(pane,460,260);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
Image image = new Image("Imgs/Gobang.jpg",480,280,false,true,true);
ImageView imageView = new ImageView(image);
pane.getChildren().add(imageView);
pane.setBackground(new Background(
new BackgroundFill(Color.DARKOLIVEGREEN, null, null)));
// 设置窗口不可变
setResizable(false);
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.setStatusByAccount(Global.account, 0);
}
});
Label title = new Label("选择游戏模式");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(155);
title.setLayoutY(80);
//添加单机版和网络部按钮
Button buttonOnline = new Button("网络版");
buttonOnline.setFont(new Font("KaiTi",20));
buttonOnline.setPrefSize(100, 60);
buttonOnline.setLayoutX(280);
buttonOnline.setLayoutY(140);
buttonOnline.setOnAction(this);
Button buttonSingle = new Button("单机版");
buttonSingle.setFont(new Font("KaiTi",20));
buttonSingle.setPrefSize(100, 60);
buttonSingle.setLayoutX(80);
buttonSingle.setLayoutY(140);
buttonSingle.setOnAction(this);
//将按钮添加进面板
pane.getChildren().addAll(title,buttonOnline,buttonSingle);
}
/**
* 在线模式
* @author Dracarys
*/
public void playOnineVersion() {
new OnlineConfigStage().show();
this.close();
Global.mode = ChessMode.NETWORK;
}
/**
* 单机版按钮及单机版游戏事件
* @author Dracarys
* @param pane
*/
public void playSingleVersion() {
try {
new GameStage().show();
Global.mode = ChessMode.SINGLE;
this.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void handle(ActionEvent event) {
Button btn = (Button)event.getSource();
switch (btn.getText()) {
case "单机版":
playSingleVersion();
break;
case "网络版":
playOnineVersion();
break;
}
}
}
package com.wn.ui;
import java.util.List;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.NetUtils;
import com.wn.CommonUtils.ReceiveThread;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.ConnectionMessage;
import com.wn.pojo.Global;
import com.wn.pojo.UserStatus;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class OnlineConfigStage extends Stage implements EventHandler<ActionEvent> {
private Pane pane; //舞台面板
private TextField myPort; //本机端口
private TextField conIp; //连接ip
private TextField conPort; //连接端口
private List<UserStatus> list; //在线用户列表
public OnlineConfigStage(){
setTitle("网络连接设置");
pane = new Pane();
Scene scene = new Scene(pane,600,400);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
pane.setBackground(new Background(new BackgroundFill(Color.GAINSBORO, null, null)));
// 设置窗口不可变
setResizable(false);
addElement();
getOnlineUser();
//当用户退出程序时,将用户的状态设置为下线
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.setStatusByAccount(Global.account, 0);
}
});
}
private void addTipsInfo() {
//在线玩家标题提示
Label title = new Label("当前在线玩家");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(350);
title.setLayoutY(30);
pane.getChildren().add(title);
//在线用户信息行
String[] tipsInfo = {"用户名","连接IP","连接PORT"};
for (int i = 0; i < tipsInfo.length; i++) {
Label Lable = new Label(tipsInfo[i]);
Lable.setFont(new Font("KaiTi", 20));
Lable.setLayoutX(280+100*i);
Lable.setLayoutY(80);
pane.getChildren().add(Lable);
}
}
private void addElement() {
//网络连接设置标题提示
Label title = new Label("网络连接设置");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(50);
title.setLayoutY(30);
//连接地址文本提示
Label myPortLable = new Label("本机端口");
myPortLable.setFont(new Font("KaiTi", 20));
myPortLable.setLayoutX(60);
myPortLable.setLayoutY(90);
//添加本机端口输入框
myPort = new TextField();
myPort.setLayoutX(60);
myPort.setLayoutY(120);
myPort.setText("6666"); //测试port
//连接端口文本提示
Label conIPLable = new Label("连接IP");
conIPLable.setFont(new Font("KaiTi", 20));
conIPLable.setLayoutX(60);
conIPLable.setLayoutY(160);
//添加连接IP输入框
conIp = new TextField();
conIp.setLayoutX(60);
conIp.setLayoutY(190);
conIp.setText("192.172.4.8"); //测试ip
//连接端口文本提示
Label conPortLable = new Label("连接端口");
conPortLable.setFont(new Font("KaiTi", 20));
conPortLable.setLayoutX(60);
conPortLable.setLayoutY(230);
//添加连接PORT输入框
conPort = new TextField();
conPort.setLayoutX(60);
conPort.setLayoutY(260);
conPort.setText("6666"); //测试port
//添加确定和取消按钮
Button conBtn = new Button("连\t接");
conBtn.setPrefSize(150, 30);
conBtn.setLayoutX(60);
conBtn.setLayoutY(320);
conBtn.setOnAction(this);
Button canBtn = new Button("取\t消");
canBtn.setPrefSize(150, 25);
canBtn.setLayoutX(60);
canBtn.setLayoutY(360);
canBtn.setOnAction(this);
pane.getChildren().addAll(title,myPortLable,myPort,conIPLable,conIp,conPortLable,conPort,conBtn,canBtn);
}
private void getOnlineUser() {
//获取UserStatusDAO实现类
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
//获取在线用户列表
list = statusDAO.selectInfoByStatus(1);
if (list.size()<=1) {
return;
}
addTipsInfo();
int i = 0;
for (UserStatus userStatus : list) {
if (!userStatus.getAccount().equals(Global.account)) {
//添加用户名信息
Label accountLable = new Label(userStatus.getAccount());
accountLable.setFont(new Font("KaiTi", 20));
accountLable.setLayoutX(290);
accountLable.setLayoutY(120+60*i);
//添加ip信息
Label ipLable = new Label(userStatus.getIp());
ipLable.setFont(new Font("KaiTi", 20));
ipLable.setLayoutX(360);
ipLable.setLayoutY(120+60*i);
//添加port信息
Label portLable = new Label(userStatus.getPort()+"");
portLable.setFont(new Font("KaiTi", 20));
portLable.setLayoutX(500);
portLable.setLayoutY(120+60*i);
pane.getChildren().addAll(accountLable,ipLable,portLable);
i++;
}
}
}
public void connect(){
//当网络连接信息未填写时,弹窗提醒
if (myPort.getText().equals("") || conIp.getText().equals("") || conPort.getText().equals("")) {
Alert alert = new Alert(AlertType.INFORMATION,"网络连接信息不能为空");
alert.initOwner(this);
alert.showAndWait();
return;
}
//如果没有在线用户时,无法正常连接
if (list.size()<=1) {
Alert alert = new Alert(AlertType.INFORMATION,"当前无在线用户,无法进行连接");
alert.initOwner(this);
alert.showAndWait();
return;
}
//尝试将用户输入的端口信息转为int型
try {
Global.myPort = Integer.parseInt(myPort.getText());
Global.oppoIp = conIp.getText();
Global.oppoPort = Integer.parseInt(conPort.getText());
} catch (NumberFormatException e1) {
e1.printStackTrace();
return;
}
try {
//启动线程,接收客户端连接
ReceiveThread receiveThread = new ReceiveThread();
Thread thread = new Thread(receiveThread);
thread.start();
new GameStage().show();
//将用户输入的本机端口保存到数据库
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.updatePortByAccount(Global.account, Global.myPort);
//向服务端发送连接请求信息,连接信息中包含客户端的用户
NetUtils.sendMessage(new ConnectionMessage(Global.account));
} catch (InterruptedException e) {
e.printStackTrace();
}
this.close();
}
@Override
public void handle(ActionEvent event) {
Button btn = (Button)event.getSource();
switch (btn.getText()) {
case "连\t接":
connect();
break;
case "取\t消":
new ModeChooseStage().show();
this.close();
break;
}
}
}
package com.wn.ui;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.NetUtils;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.ButtonMessage;
import com.wn.pojo.ChessMessage;
import com.wn.pojo.ChessMode;
import com.wn.pojo.ConnectionMessage;
import com.wn.pojo.Global;
import com.wn.pojo.Message;
import com.wn.pojo.MessageTimes;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class GameStage extends Stage implements EventHandler<ActionEvent> {
private List<Circle> chessList = new ArrayList<Circle>();
private boolean isBlack = true; //判断棋子是否是黑色
private boolean gameOver = false; //判断游戏是否结束 true游戏结束,false代表可以下棋
private int topMargin = 50;//上间距
private int leftMargin = 350;//上间距
private int gap = 50; //内间距
private int chessWidth = 1400; //棋盘宽
private int chessHeight = 830; //棋盘高
private int size = 15; //棋盘线条数
private int buttonWidth = 80; //按钮宽
private int buttonLength = 40; //按钮高
private int cicreRadius = 18; //棋子半径
private int i = 0; //打开棋谱记录当前是第几手棋
private boolean canPlay = false; //定义网络模式下,当前是否能下棋
Pane pane;
public GameStage() throws InterruptedException {
//设置窗口不可改变
// setResizable(false);
//创建面板对象
pane = new Pane();
//创建场景对象
Scene scene = new Scene(pane,chessWidth,chessHeight);
//将场景放进舞台
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
Image image = new Image("Imgs/background.jpg",1400,830,false,true,true);
ImageView imageView = new ImageView(image);
pane.getChildren().add(imageView);
setTitle("五子棋");
drawLine();
addButton();
Global.gameStage = this;
playChess();
//当用户退出程序时,将用户的状态设置为下线
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
exitGame();
}
});
}
/**
* 根据接收到的Message对象,更新棋子信息
* @author Dracarys
*/
public void updateUI(Message message) {
if (message instanceof ConnectionMessage) {
ConnectionMessage conMessage = (ConnectionMessage)message;
if (conMessage.getClientUser()!=null) {
//有客户端连接时,弹窗提醒
Alert alert = new Alert(AlertType.CONFIRMATION,conMessage.getClientUser()+"已进入战斗!!");
alert.initOwner(this);
alert.show();
}
//接收到的消息为棋子信息消息
}else if (message instanceof ChessMessage) {
ChessMessage chessMessage = (ChessMessage)message;
double x = chessMessage.getX();
double y = chessMessage.getY();
double pieceX = x * gap + leftMargin;
double pieceY = y * gap + topMargin;
dropPiece(pieceX, pieceY);
//接收到消息之后将标签改完false
canPlay = false;
return;
//接收到的消息为按钮信息消息
}else if (message instanceof ButtonMessage) {
ButtonMessage btnMessage = (ButtonMessage)message;
String btnName = btnMessage.getBtnName();
switch (btnName) {
case "新\t局":
//接收到点击新局按钮的第一次消息
if (btnMessage.getTimes().equals(MessageTimes.FIRST)) {
//弹窗提醒,让接收方选择是否同意开启新局
Alert alert = new Alert(AlertType.CONFIRMATION,"对方想开启新局");
alert.initOwner(this);
alert.showAndWait();
//如果用户选择同意开启新局,则开启新局
if (alert.getResult() == ButtonType.OK) {
startGame();
//然后用户不同意开启新局,将按钮消息的同意情况更改为false
}else {
//不同意,更改isAgree为false
btnMessage.setAgree(false);
}
//向新局发起方发送消息
btnMessage.setTimes(MessageTimes.SECOND);
NetUtils.sendMessage(btnMessage);
}else if (btnMessage.getTimes().equals(MessageTimes.SECOND)) {
Alert alert = new Alert(AlertType.INFORMATION);
//对方不同意开启新局
if (!btnMessage.isAgree()) {
alert.setContentText("对方不同意开启新局");
alert.initOwner(this);
alert.showAndWait();
}else {
alert.setContentText("对方同意开启新局");
alert.initOwner(this);
alert.showAndWait();
startGame();
}
}
break;
case "悔\t棋":
//第一次接收到悔棋消息时,让接收方选择是否悔棋
if (btnMessage.getTimes().equals(MessageTimes.FIRST)) {
//弹窗提醒,通过同意,就开启新局
Alert alert = new Alert(AlertType.CONFIRMATION,"对方想悔棋");
alert.showAndWait();
//如果用户选择同意悔棋,则直接悔棋,并发送消息
if (alert.getResult() == ButtonType.OK) {
regretChess();
canPlay = false;
//然后用户不同意悔棋,将按钮消息的同意情况更改为false
}else {
//不同意,更改isAgree为false
btnMessage.setAgree(false);
}
//向悔棋发起方发送消息
btnMessage.setTimes(MessageTimes.SECOND);
NetUtils.sendMessage(btnMessage);
}else if (btnMessage.getTimes().equals(MessageTimes.SECOND)) {
Alert alert = new Alert(AlertType.INFORMATION);
//对方不同意悔棋
if (!btnMessage.isAgree()) {
alert.setContentText("对方不同意悔棋");
alert.show();
}else {
regretChess();
canPlay = false;
Global.regretTimes = 1;
}
}
break;
}
}
}
/**
* 保存棋谱
* @author Dracarys
* @param pane
*/
public void saveChessRecord() {
if (!gameOver) {
return;
}
//创建打开文件弹窗对象
FileChooser fileChooser = new FileChooser();
//设置弹窗标题
fileChooser.setTitle("保存棋谱");
//设置默认目录
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
//设置文件后缀
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("csv", "*.csv"));
//打开文件保存对话框
File file = fileChooser.showSaveDialog(this);
//点击取消,则文件为空,停止保存
if (file == null) {
return;
}
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
for (Circle chess : chessList) {
bw.write(chess.getCenterX()+","+chess.getCenterY()+","+chess.getFill());
bw.newLine();
}
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("保存棋谱成功");
alert.show();
} catch (Exception e) {
}
}
/**
* 打开棋谱
* @author Dracarys
* @param pane
*/
public void openChessScore() {
//打开棋谱仅在单机版有效,网络版点击弹框提醒
if (Global.mode.equals(ChessMode.NETWORK)) {
Alert alert = new Alert(AlertType.INFORMATION,"该功能仅在单机版有效");
alert.showAndWait();
return;
}
//创建打开文件弹窗对象
FileChooser fc = new FileChooser();
//设置对象标题
fc.setTitle("打开棋谱");
//设置打开的初始路径
fc.setInitialDirectory(new File(System.getProperty("user.home")));
//设置打开的文件默认后缀
fc.getExtensionFilters().add(new FileChooser.ExtensionFilter("csv", "*.csv"));
//将选择的文件赋值给file对象
File file = fc.showOpenDialog(this);
//如果文件为空则不继续打开
if (file == null) {
return;
}
//打开棋谱时情况棋子集合和棋盘上的棋子
chessList.clear();
pane.getChildren().removeIf(c-> c instanceof Circle);
i = 0;
//创建缓冲流,读取棋谱文件
try (BufferedReader br = new BufferedReader(new FileReader(file)) ) {
String content;
while ((content = br.readLine())!=null) {
String[] list = content.split(",");
//根据文件棋子信息创建棋子
Circle chess = new Circle(Double.parseDouble(list[0]), Double.parseDouble(list[1]), cicreRadius,Color.web(list[2]));
//将棋子添加进集合
chessList.add(chess);
}
} catch (Exception e) {
}
//产生三个按钮控制棋谱读取
String[] list = {"<",">","x"};
for (int i = 0; i < list.length; i++) {
Button btn = new Button(list[i]);
btn.setPrefSize(30, 30);
btn.setLayoutX(1060);
btn.setLayoutY(300+60*i);
btn.setOnAction(this);
pane.getChildren().add(btn);
}
}
/**
* 退出游戏
* @author Dracarys
* @param pane
*/
public void exitGame() {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("退出游戏确认");
alert.setHeaderText("点击确认按钮将退出游戏程序");
alert.setContentText("您确认退出吗?");
alert.showAndWait();
if (alert.getResult() == ButtonType.OK) {
if (Global.mode.equals(ChessMode.SINGLE)) {
System.exit(0);
}else {
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.setStatusByAccount(Global.account, 0);
System.exit(0);
}
}
}
/**
* 悔棋
* @author Dracarys
*/
public void regretChess() {
if (chessList.size()>=1&&gameOver == false) {
chessList.remove(chessList.size()-1);
pane.getChildren().remove(pane.getChildren().size()-1);
isBlack = !isBlack;
}
}
/**
* 开始游戏事件
* @author Dracarys
* @param pane
*/
public void startGame() {
// if (!gameOver) {
// Alert alert = new Alert(AlertType.INFORMATION,"游戏还未结束不能开始新局");
// alert.showAndWait();
// return;
// }
gameOver = false;
isBlack = true;
chessList.clear();
pane.getChildren().removeIf(c -> c instanceof Circle);
}
/**
* 添加棋盘线
* @author Dracarys
* @param pane
*/
public void drawLine() {
//棋盘700*700,横竖14条线,每条先间隔50
//第一条横线起点(350,50),y起点(1050,50);第一条竖线x起点(350,50),y起点(350,650)
//第二条横线x起点(350,100),y起点(1050,100);第二条竖线x起点(1050,50),y起点(1050,650)
for (int i = 0; i < size; i++) {
//横线
Line line1 = new Line(leftMargin, topMargin+i*gap, leftMargin+50*14, topMargin+i*gap);
//竖线
Line line2 = new Line(leftMargin+i*gap, topMargin, leftMargin+i*gap, topMargin+50*14);
pane.getChildren().add(line1);
pane.getChildren().add(line2);
}
}
//添加按钮
public void addButton() {
String[] btnList = {"新\t局","保存棋谱","打开棋谱","悔\t棋","退\t出"};
for (int i = 0; i < btnList.length; i++) {
Button button = new Button(btnList[i]);
button.setPrefSize(buttonWidth, buttonLength);
button.setLayoutX(leftMargin+75*i+buttonWidth*i);
button.setLayoutY(size*topMargin+20);
button.setOnAction(this);
pane.getChildren().add(button);
}
}
/**
* 在鼠标点击的位置落子
* @author Dracarys
* @param pane
*/
public void playChess() {
pane.setOnMouseClicked(e -> {
//网络版:判断用户是否已发送棋子消息,如果已发送
if (canPlay) {
return;
}
if (gameOver) {
return;
}
double x = e.getX(); //获取用户鼠标点击的坐标x值
double y = e.getY(); //获取用户鼠标点击的坐标y值
//左上顶点坐标(50,50),棋子半径为18,边界为坐标-棋子半径
//右下顶点坐标(750,750),棋子半径为18,边界为坐标+棋子半径
if (x < leftMargin - cicreRadius || x > chessWidth - leftMargin + cicreRadius) {
return;
}
if (y < topMargin - cicreRadius || y > 800 - topMargin + cicreRadius) {
//50-18 = 32
return;
}
//将鼠标的坐标x和y都换算成(1,1)->(14,14)范围
double xIndex = Math.round(((x - leftMargin) / gap));
double yIndex = Math.round(((y - topMargin) / gap));
//判断当前鼠标点的位置是否存在棋子
double pieceX = xIndex * gap + leftMargin;
double pieceY = yIndex * gap + topMargin;
if (hasPiece(pieceX, pieceY)) {
return;
}
//落棋子
dropPiece(pieceX,pieceY);
//创建棋子消息对象
if (Global.mode.equals(ChessMode.NETWORK)) {
ChessMessage message = new ChessMessage(xIndex,yIndex);
//发送棋子消息对象
NetUtils.sendMessage(message);
canPlay = true;
}
});
}
public void dropPiece(double x,double y) {
Circle circle = new Circle();
//设置棋子落子的坐标
circle.setCenterX(x);
circle.setCenterY(y);
circle.setRadius(cicreRadius);
if (isBlack) {
circle.setFill(Color.BLACK);
} else {
circle.setFill(Color.WHITE);
}
pane.getChildren().add(circle);
isBlack = !isBlack;
Circle chess = new Circle(x, y,cicreRadius, isBlack ? Color.BLACK : Color.WHITE);
chessList.add(chess);
//判断游戏输赢
if (isWin(chess)) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("游戏结束");
alert.setHeaderText("游戏结果");
alert.setContentText(isBlack ? "白棋胜" : "黑棋胜");
alert.show();
gameOver = true;
}
}
/** 判断当前坐标位置是否有棋子
* @author Dracarys
* @param x
* @param y
* @return
*/
private boolean hasPiece(double x,double y) {
//遍历数组
for (int i = 0; i < chessList.size(); i++) {
Circle c = chessList.get(i);
if (c.getCenterX() == x && c.getCenterY() == y) {
return true;
}
}
return false;
}
/**
* 判断是否胜利
* @author Dracarys
* @param chess
* @return
*/
private boolean isWin(Circle chess) {
int count = 1;
//判断x轴向左边判断另外4个位置棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessLeft = new Circle(chess.getCenterX()-i*gap,
chess.getCenterY(),cicreRadius,chess.getFill());
if (contain(chessLeft)) {
count++;
}else {
break;
}
}
//判断x轴向右边判断另外4个位子棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessRight = new Circle(chess.getCenterX()+i*gap,
chess.getCenterY(),cicreRadius,chess.getFill());
if (contain(chessRight)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
count = 1;
//判读y轴向上4个位置的棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessTop = new Circle(chess.getCenterX(),
chess.getCenterY()-i*gap,cicreRadius,chess.getFill());
if (contain(chessTop)) {
count++;
}else {
break;
}
}
//判读y轴向下4个位置的棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessDown = new Circle(chess.getCenterX(),
chess.getCenterY()+i*gap,cicreRadius,chess.getFill());
if (contain(chessDown)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
count = 1;
//判读左斜线边4个棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessTopLeft = new Circle(chess.getCenterX()-i*gap,
chess.getCenterY()-i*gap,cicreRadius,chess.getFill());
if (contain(chessTopLeft)) {
count++;
}else {
break;
}
}
//判读左斜线边4个棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessTopRight = new Circle(chess.getCenterX()+i*gap,
chess.getCenterY()+i*gap,cicreRadius,chess.getFill());
if (contain(chessTopRight)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
count = 1;
//判读右斜线边4个棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessDownLeft = new Circle(chess.getCenterX()+i*gap,
chess.getCenterY()-i*gap,cicreRadius,chess.getFill());
if (contain(chessDownLeft)) {
count++;
}else {
break;
}
}
//判读右斜线边4个棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessDownRight = new Circle(chess.getCenterX()-i*gap,
chess.getCenterY()+i*gap,cicreRadius,chess.getFill());
if (contain(chessDownRight)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
return false;
}
/**
* 判断棋子集合中是否包含当前棋子
* @author Dracarys
* @param chess
* @return
*/
public boolean contain(Circle chess) {
for (Circle circle : chessList) {
if (circle.getCenterX() == chess.getCenterX() && circle.getCenterY()==chess.getCenterY()&&circle.getFill().equals(chess.getFill())) {
return true;
}
}
return false;
}
/**
* 重写用户点击按钮的方法
*/
@Override
public void handle(ActionEvent event) {
Button source = (Button) event.getSource();
String text = source.getText();
switch (text) {
case "新\t局":
if (Global.mode.equals(ChessMode.SINGLE)) {
startGame();
}else {
ButtonMessage btnMessage = new ButtonMessage(text,true,MessageTimes.FIRST);
NetUtils.sendMessage(btnMessage);
}
break;
case "保存棋谱":
saveChessRecord();
break;
case "打开棋谱":
openChessScore();
break;
case "悔\t棋":
if (gameOver) {
return;
}
if (chessList.isEmpty()) {
return;
}
if (Global.regretTimes>=1) {
Alert alert = new Alert(AlertType.INFORMATION,"已经悔悔过棋, 不能再次悔棋");
alert.initOwner(this);
alert.show();
return;
}
if (!canPlay) {
Alert alert = new Alert(AlertType.INFORMATION,"不能悔棋");
alert.initOwner(this);
alert.show();
return;
}
if (Global.mode.equals(ChessMode.SINGLE)) {
regretChess();
}else if (Global.mode.equals(ChessMode.NETWORK)) {
System.out.println(Global.regretTimes);
//网络模式下,一方选择悔棋,发送悔棋消息给另一方,按钮为名称为悔棋,发送方同意悔棋,消息次数为第一次
NetUtils.sendMessage(new ButtonMessage("悔\t棋", true, MessageTimes.FIRST));
}
break;
case "退\t出":
exitGame();
break;
case "<":
openChessScoreLast();
break;
case ">":
openChessScoreNext();
break;
case "x":
openChessScoreAll();
break;
}
}
/**
* 打开棋谱,点击x,显示所有棋子
* @author Dracarys
*/
private void openChessScoreAll() {
for (int j = i; j < chessList.size(); j++) {
pane.getChildren().add(chessList.get(j));
}
pane.getChildren().remove(36);
pane.getChildren().remove(36);
pane.getChildren().remove(36);
}
/**
* 打开棋谱,点击上一步按钮
* @author Dracarys
*/
private void openChessScoreLast() {
if (i == 0) {
return;
}
pane.getChildren().remove(pane.getChildren().size()-1);
i--;
}
/**
* 打开棋谱,点击下一步按钮
* @author Dracarys
*/
private void openChessScoreNext() {
if (i == chessList.size()) {
pane.getChildren().remove(36);
pane.getChildren().remove(36);
pane.getChildren().remove(36);
return;
}
Circle chess = chessList.get(i);
pane.getChildren().add(chess);
i++;
}
}

浙公网安备 33010602011771号