JavaFxJava 大作业 五子棋 实验报告

    技术2022-07-11  112

    Java大作业五子棋实验报告

    实验目的

    通过此次实验,对这一学期学习的内容尤其是界面开发部分做了一个很好的回顾,看似简单的五子棋程序,设计好也确实费了我一点功夫

    功能模块简介和系统结构图

    ChessGame类

    作为布局的基类,设置一些基本的按钮,用于启动程序,接收一些基本参数的传入(如谁先行)

    ChessPane类

    本类主要保存一些关于界面的基本信息,起到绘制界面,绘制棋子的功能

    UserPlay类

    本类存储一些关于用户动作的基本信息,接受并处理PlayAction传回的动作信息

    PlayAction类

    本类用于接收用户传入的关于棋局的动作信息

    系统结构图

    系统主界面设计及运行说明

    主界面设计

    如下图所示

    运行说明

    启动程序后,先选择先行方,如果不选择,默认黑棋先行,此后,双方轮流下子,直到一方先连成五子,游戏结束。

    主要的源程序代码

    使用到的包

    import javafx.application.*; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.*; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.Dialog; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.stage.*; import javafx.scene.layout.*; import javafx.scene.text.*; import javafx.scene.canvas.*; import javafx.event.*;

    ChessGame类

    public class ChessGame extends Application { public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) throws Exception { //新建棋盘实例 ChessPane chessPane = new ChessPane(); BorderPane borderPane = new BorderPane(); UserPlay userPlay = new UserPlay(); PlayAction playAction = new PlayAction(chessPane, userPlay);//将chessPane实例与playAction相关联 chessPane.setOnMouseClicked(playAction);//将chessPane实例添加鼠标事件 //把棋盘定位在边界面板中央 borderPane.setCenter(chessPane); //todo 构造一个Vbox放按钮 VBox vBoxButton = new VBox(); vBoxButton.getChildren().addAll();//添加结点 borderPane.setRight(vBoxButton);//将vbox放在面板右侧 vBoxButton.setSpacing(100);//设置按钮间的距离为100 vBoxButton.setAlignment(Pos.CENTER);//设置vbox居中 vBoxButton.setPadding(new Insets(0, 30, 0, 0));//设置右边距 //todo 重玩按钮 Button buttonReplay = new Button("Try again"); //设置按钮格式 buttonReplay.setPrefHeight(50); buttonReplay.setPrefWidth(100); buttonReplay.setStyle("-fx-background-color:linear-gradient(to right,#00fffc,#fff600);-fx-background-radius:25 ;-fx-border-radius:25;-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.7), 10, 0, 0, 1);"); // vBoxButton.getChildren().add(buttonReplay); buttonReplay.setOnMouseClicked(event -> { primaryStage.close(); }); //todo 退出按钮 Button buttonExit = new Button("Exit"); vBoxButton.getChildren().add(buttonExit); //设置按钮格式 buttonExit.setPrefHeight(50); buttonExit.setPrefWidth(100); buttonExit.setStyle("-fx-background-color:linear-gradient(to right,#d580ff,#80d5ff);-fx-background-radius:25 ;-fx-border-radius:25;-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.7), 10, 0, 0, 1);"); //设置退出事件 buttonExit.setOnMouseClicked(event -> { primaryStage.close(); }); //todo 切换AI 人人对战按钮 Scene scene = new Scene(borderPane, 800, 800); primaryStage.setScene(scene); primaryStage.setTitle("五子棋"); primaryStage.show(); //todo 初始是谁先下 BorderPane borderPaneFirst = new BorderPane(); Scene sceneFirst = new Scene(borderPaneFirst, 300, 300); HBox hBoxFirst = new HBox(); borderPaneFirst.setCenter(hBoxFirst); hBoxFirst.setAlignment(Pos.CENTER); hBoxFirst.setSpacing(50); Button buttonFirstBlack = new Button("black first"); buttonFirstBlack.setPrefWidth(100); buttonFirstBlack.setPrefHeight(50); buttonFirstBlack.setStyle("-fx-background-color:linear-gradient(to right,#00fffc,#fff600);-fx-background-radius:25 ;-fx-border-radius:25;-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.7), 10, 0, 0, 1);"); hBoxFirst.getChildren().add(buttonFirstBlack); Button buttonFirstWhite = new Button("white first"); buttonFirstWhite.setPrefHeight(50); buttonFirstWhite.setPrefWidth(100); buttonFirstWhite.setStyle("-fx-background-color:linear-gradient(to right,#FFFFCC,#FFCCFF);-fx-background-radius:25 ;-fx-border-radius:25;-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.7), 10, 0, 0, 1);"); hBoxFirst.getChildren().add(buttonFirstWhite); Stage stageWhoFirst = new Stage(); stageWhoFirst.setScene(sceneFirst); stageWhoFirst.setTitle("Who first?"); stageWhoFirst.show(); buttonFirstBlack.setOnMouseClicked(event -> { userPlay.setCurrentSide(1); stageWhoFirst.close(); }); buttonFirstWhite.setOnMouseClicked(event -> { userPlay.setCurrentSide(2); stageWhoFirst.close(); }); } }

    UserPlay类

    //用户操作类 class UserPlay extends ChessPane { private int sideLength = getSideLength();//设定边长 private double width = getWidth();//棋盘宽 private double height = getPaneHeight();//棋盘高 private double cellLen = getCellLen();//每个方格的长度 /** * currentside 表示当前的玩家 * 如果是黑为1 * 白为2 */ private int currentSide = 1; public int getCurrentSide() { return currentSide; } public void setCurrentSide(int currentSide) { this.currentSide = currentSide; } private int[][] chess = new int[sideLength + 10][sideLength + 10];//定义数组存棋盘 void initChess() { for (int i = 0; i <= sideLength; i++) { for (int j = 0; j <= sideLength; j++) { chess[i][j] = 0; } } } /** * 得到x,y坐标后 * 判断应该填在数组的哪个方格 */ public int realX, realY;//应该填的位置 public int getRealX(int x) { boolean isRealX = false;//是不是找到了应该填的位置 int RangeX = (int) getAlign(); //在x的范围以10为左右边界找对应列 System.out.println(RangeX + getCellLen() * getSideLength()); for (int i = RangeX, j = 0; i < RangeX + getCellLen() * getSideLength(); i += getCellLen(), j++) { if ((x >= i - 10) && (x <= i + 10)) { realX = j; isRealX = true; break; } } //debug in function System.out.println("xinf: " + x); System.out.println("realX: " + realX); if (isRealX) return realX; else return -1; } public int getRealY(int y) { boolean isRealY = false;//是不是找到了应该填的位置 int RangeY = (int) getAlign(); //在y的范围以10为左右边界找对应行 for (int i = RangeY, j = 0; i < RangeY + getCellLen() * getSideLength(); i += getCellLen(), j++) { if ((y >= i - 10) && (y <= i + 10)) { realY = j; isRealY = true; break; } } //debug in function System.out.println("yinf: " + y); System.out.println("realY: " + realY); if (isRealY) return realY; else return -1; } /** * 判断是否越界 越界为true 不越界为false */ public boolean isOverArea(int findX, int findY) { return findX == -1 || findY == -1; } /** * 落子,如果不符合范围,则显示警告信息 */ public boolean dropDownTheChess(int x, int y) { if (!isOverArea(x, y) && chess[x][y] == 0) { chess[x][y] = currentSide; //debug chess System.out.println("chessxy " + chess[x][y]); return true; } else { //显示提示错误框 Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("错误"); alert.setContentText("棋子不可以下在这里"); alert.showAndWait(); return false; } } /** * 换边 */ public void changeSide() { if (currentSide == 1) currentSide = 2; else currentSide = 1; } /** * 判断游戏是否结束 * true 结束游戏 * false 继续游戏 */ public boolean judgeGame(int row, int col, int chessColor) { int plane = plane(row, col, chessColor); int vertical = vertical(row, col, chessColor); int left = leftOblique(row, col, chessColor); int right = rightOblique(row, col, chessColor); changeSide();//换边 //debug System.out.println("currentsideinjudge" + currentSide); System.out.println("plane" + plane); System.out.println("vertical" + vertical); System.out.println("left" + left); System.out.println("right" + right); if (plane >= 5 || vertical >= 5 || left >= 5 || right >= 5) { return true; } else return false; } /** * 判断有没有出界 */ public boolean judgementOverArea(int x, int y) { if (x >= 0 && x <= getSideLength() && y >= 0 && y <= getSideLength()) { return false; } else return true; } /** * 水平线上是否连成五子 * * @param row * @param col * @param chessColor * @return */ public int plane(int row, int col, int chessColor) { int line = 1; int i = row - 1; for (; !judgementOverArea(i, col) && chess[i][col] == chessColor; i--) line++; for (i = row + 1; !judgementOverArea(i, col) && chess[i][col] == chessColor; i++) line++; return line; } /** * 检查棋子在垂直竖线上是否连成五子 * * @param row * @param col * @param chessColor * @return */ public int vertical(int row, int col, int chessColor) { int line = 1; int j = col - 1; for (; !judgementOverArea(row, j) && chess[row][j] == chessColor; j--) line++; for (j = col + 1; !judgementOverArea(row, j) && chess[row][j] == chessColor; j++) line++; return line; } /** * 检查左倾斜线上的棋子是否连成五子 * * @param row * @param col * @param chessColor * @return */ public int leftOblique(int row, int col, int chessColor) { int line = 1; int i = row - 1, j = col - 1; for (; !judgementOverArea(i, j) && chess[i][j] == chessColor; i--, j--) line++; for (i = row + 1, j = col + 1; !judgementOverArea(i, j) && chess[i][j] == chessColor; i++, j++) line++; return line; } /** * 检查右倾斜线上的棋子是否连成五子 * * @param row * @param col * @param chessColor * @return */ public int rightOblique(int row, int col, int chessColor) { int line = 1; int i = row - 1, j = col + 1; for (; !judgementOverArea(i, j) && chess[i][j] == chessColor; i--, j++) line++; for (i = row + 1, j = col - 1; !judgementOverArea(i, j) && chess[i][j] == chessColor; i++, j--) line++; return line; } }

    ChessPane类

    /** * 面板类——做基类——定义基本信息 */ class ChessPane extends Pane { public Canvas canvas;//创建一个画板 public GraphicsContext graphicsContext; private double cellLen = 40;//这里定义cell的长度 private double align = 70; //定义一个边界值,表示棋盘离程序边框的边界,从而使棋盘居中 private double paneWidth = 560;//棋盘宽 private double paneHeight = 560;//棋盘高 private int sideLength = 15;//设定边长 // todo 可以将这个ChessPane再放到一个边界面板的中心,top部位放操作按钮 public void setAlign(double align) { this.align = align; } public void setCanvas(Canvas canvas) { this.canvas = canvas; } public void setCellLen(double cellLen) { this.cellLen = cellLen; } public void setGraphicsContext(GraphicsContext graphicsContext) { this.graphicsContext = graphicsContext; } public void setPaneHeight(double paneHeight) { this.paneHeight = paneHeight; } public void setPaneWidth(double paneWidth) { this.paneWidth = paneWidth; } public void setSideLength(int sideLength) { this.sideLength = sideLength; } public Canvas getCanvas() { return canvas; } public double getCellLen() { return cellLen; } public double getPaneHeight() { return paneHeight; } public double getPaneWidth() { return paneWidth; } public int getSideLength() { return sideLength; } public double getAlign() { return align; } public ChessPane() { draw(); getChildren().add(canvas);//将画板添加到自定义面板的结点上 } public void draw() { canvas = new Canvas(700, 700); //制图环境 this.graphicsContext = canvas.getGraphicsContext2D(); graphicsContext.setFill(Color.BURLYWOOD);//将画布底色改为木色 graphicsContext.fillRect(align - 15, align - 15, 560 + 30, 560 + 30);//绘制填充矩形 //画横线 for (int i = 0; i < 15; i++) { graphicsContext.strokeLine(align, i * cellLen + align, 560 + align, i * cellLen + align); } //画竖线 for (int i = 0; i < 15; i++) { graphicsContext.strokeLine(i * cellLen + align, align, i * cellLen + align, 560 + align); } for (int i = 3; i <= 14; i += 4) for (int j = 3; j <= 14; ) { graphicsContext.setFill(Color.BLACK); //画天元 if (i == 7) { j = 7; graphicsContext.strokeOval(i * cellLen + align - 4, j * cellLen + align - 4, 8, 8); graphicsContext.fillOval(i * cellLen + align - 4, j * cellLen + align - 4, 8, 8); break; } //画星位 else { graphicsContext.strokeOval(i * cellLen + align - 4, j * cellLen + align - 4, 8, 8); graphicsContext.fillOval(i * cellLen + align - 4, j * cellLen + align - 4, 8, 8); j += 8; } } //边框加粗设计 graphicsContext.setLineWidth(3.0f);//改变绘制的边线粗细 graphicsContext.strokeRect(align, align, 560, 560);//绘制一个正方形覆盖原先的矩形 // graphicsContext.setFill(Color.BLACK); // graphicsContext.strokeOval(getAlign() + 1 * cellLen - cellLen / 2, getAlign() + 0 * cellLen - cellLen / 2, cellLen, cellLen); } /** * 画棋子 */ public void paintChess(int x, int y, int currentSide) { System.out.println("xinPaintChess" + x); System.out.println("yinPaintChess" + y); if (currentSide == 1) { graphicsContext.setFill(Color.BLACK); } else { graphicsContext.setFill(Color.WHITE); } System.out.println(getAlign() + x * cellLen); System.out.println(getAlign() + y * cellLen); System.out.println("currentsideinPaintChess" + currentSide); //大棋子 // graphicsContext.strokeOval(getAlign() + x * cellLen - cellLen / 2, getAlign() + y * cellLen - cellLen / 2, cellLen, cellLen); // graphicsContext.fillOval(getAlign() + x * cellLen - cellLen / 2, getAlign() + y * cellLen - cellLen / 2, cellLen, cellLen); //小棋子 graphicsContext.strokeOval(getAlign() + x * cellLen - cellLen / 3, getAlign() + y * cellLen - cellLen / 3, cellLen - 10, cellLen - 10); graphicsContext.fillOval(getAlign() + x * cellLen - cellLen / 3, getAlign() + y * cellLen - cellLen / 3, cellLen - 10, cellLen - 10); } }

    PlayAction类

    class PlayAction extends UserPlay implements EventHandler<MouseEvent> { private ChessPane chessPane; private UserPlay userPlay; public PlayAction(ChessPane chessPane, UserPlay userPlay) { this.chessPane = chessPane; this.userPlay = userPlay; } @Override public void handle(MouseEvent event) { double cellLen = getCellLen(); //定位 int x = (int) (event.getX()); int y = (int) (event.getY()); //debug 鼠标定位 System.out.println("x: " + x); System.out.println("y: " + y); //如果位置合法就画出棋子 int realX, realY; realX = getRealX(x); //将x,y坐标回传 realY = getRealY(y); if (userPlay.dropDownTheChess(realX, realY)) { chessPane.paintChess(realX, realY, userPlay.getCurrentSide()); if (userPlay.judgeGame(realX, realY, userPlay.getCurrentSide())) { Alert alert1 = new Alert(Alert.AlertType.INFORMATION); alert1.setTitle("Author's smile"); String winner; if (userPlay.getCurrentSide() == 1) { winner = "white side"; } else winner = "black side"; alert1.setContentText("Game over " + winner + " is winner!"); alert1.showAndWait(); } } }

    实验总结

    收获

    通过此次实验,我在写代码的过程中体会到了动手实践对于Java编程学习的重要性,学到了以下知识

    Canava在JavaFx界面开发中绘制图形的应用

    https://blog.csdn.net/u010164507/article/details/89106632

    使用this显式激活构造方法Alert的基本使用(show!)Button的简单美化

    创新

    精确的将鼠标位置和数组值相联系

    存在的不足

    没有实现重玩的按钮,在前期设计中对于初始化不便没有实现AI和人的对战

    需要改进的地方

    整体的系统架构不尽合理,有需要改善的空间,在写代码时,函数与变量的调用多有不便,参数回传较为凌乱
    Processed: 0.019, SQL: 9