通过此次实验,对这一学期学习的内容尤其是界面开发部分做了一个很好的回顾,看似简单的五子棋程序,设计好也确实费了我一点功夫
作为布局的基类,设置一些基本的按钮,用于启动程序,接收一些基本参数的传入(如谁先行)
本类主要保存一些关于界面的基本信息,起到绘制界面,绘制棋子的功能
本类存储一些关于用户动作的基本信息,接受并处理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.*;
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();
});
}
}
//用户操作类
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;
}
}
/**
* 面板类——做基类——定义基本信息
*/
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);
}
}
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
的简单美化