JavaFx/Java 大作业 五子棋 实验报告

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编程学习的重要性,学到了以下知识

  1. CanavaJavaFx界面开发中绘制图形的应用

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

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

创新

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

存在的不足

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

需要改进的地方

  1. 整体的系统架构不尽合理,有需要改善的空间,在写代码时,函数与变量的调用多有不便,参数回传较为凌乱
  • 13
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值