功能需求
1、15*15 交叉线的五子棋面板
2、五个特殊的交叉点是实心小圆点,中间的称为天元,四周的称为星(有点强迫症,不在乎外表的可以忽略)
2、黑白棋的交替落子
3、胜负的判定
4、棋子落在交叉线上
5、棋子不可以下在棋盘外和已经有棋子的位置
游戏分为四个部分:
- 实体层,游戏的主体,胜负的判定
- 视图层,游戏面板的实现
- 控制层,实现鼠标点击事件
- 测试层,测试功能
项目的结构如下:
实体层
知识点:this 显式激活构造方法
其实涉及到的算法不难,主要是连子的计数,分为横向,纵向和两个对角线方向,从刚落下的棋子开始往前数,再往后数,计算连在一起的同色棋子个数(注意开始的棋子不要重复计算),只要有一个方向上的数目大于等于 5 就判断输赢。
package entity;
import javafx.scene.control.*;
public class FiveChess {
/**
* 维度
* 初始化由黑方开始下
*/
private int n;
private double width;
private double height;
private double cellLen;
private char[][] chess;
private char currentSide;
public FiveChess() {
this(15,560,560,40,'B');
}
public FiveChess(int n, double width, double height, double cellLen,char currentSide) {
this.n = n;
this.width = width;
this.height = height;
this.cellLen = cellLen;
this.currentSide = currentSide;
this.chess = new char[n+1][n+1];
initChess();
}
/**
* 对棋盘进行初始化
*/
public void initChess() {
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
chess[i][j] = '0';
}
public int getN() {
return n;
}
public void setN(int n) {
this.n = n;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getCellLen() {
return cellLen;
}
public void setCellLen(double cellLen) {
this.cellLen = cellLen;
}
public char getCurrentSide() {
return currentSide;
}
public void setCurrentSide(char side) {
this.currentSide = side;
}
public char[][] getChess() {
return chess;
}
/**
* 检查是否越界
* @param x
* @param y
* @return
*/
public boolean overArea(int x,int y) {
if(x>=0 && x<=n && y>=0 && y<=n)
return false;
else
return true;
}
/**
* 落子
* @param x
* @param y
*/
public boolean play(int x,int y) {
if(!overArea(x,y) && chess[x][y]=='0') {
chess[x][y] = currentSide;
return true;
} else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("警告");
alert.setContentText("棋子不可以下在此处!");
alert.showAndWait();
return false;
}
}
/**
* 换边
*/
public void changeSide() {
setCurrentSide(currentSide=='B'?'W':'B');
}
/**
* 判断游戏是否结束
* true代表结束游戏 ,false代表继续游戏
* @param row
* @param col
* @param chessColor
* @return
*/
public boolean judgeGame(int row,int col,char chessColor) {
int level = level(row,col,chessColor);
int vertical = vertical(row,col,chessColor);
int left = leftOblique(row,col,chessColor);
int right = rightOblique(row,col,chessColor);
changeSide();
if(level>=5 || vertical>=5 || left>=5 || right>=5)
return true;
else
return false;
}
/**
* 检查棋子在水平横线上是否连成五子
* @return
*/
public int level(int row,int col,char chessColor) {
int line = 1;
int i = row-1;
for(;chess[i][col]==chessColor && !overArea(i,col);i--)
line++;
for(i=row+1;chess[i][col]==chessColor && !overArea(i,col);i++)
line++;
return line;
}
/**
* 检查棋子在垂直竖线上是否连成五子
* @param row
* @param col
* @param chessColor
* @return
*/
public int vertical(int row,int col,char chessColor) {
int line = 1;
int j=col-1;
for(;chess[row][j]==chessColor && !overArea(row,j);j--)
line++;
for(j=col+1;chess[row][j]==chessColor && !overArea(row,j);j++)
line++;
return line;
}
/**
* 检查左倾斜线上的棋子是否连成五子
* @param row
* @param col
* @param chessColor
* @return
*/
public int leftOblique(int row,int col,char chessColor) {
int line = 1;
int i = row-1,j = col-1;
for(;chess[i][j]==chessColor && !overArea(i,j);i--,j--)
line++;
for(i=row+1,j=col+1;chess[i][j]==chessColor && !overArea(i,j);i++,j++)
line++;
return line;
}
/**
* 检查右倾斜线上的棋子是否连成五子
* @param row
* @param col
* @param chessColor
* @return
*/
public int rightOblique(int row,int col,char chessColor) {
int line = 1;
int i = row-1,j = col+1;
for(;chess[i][j]==chessColor && !overArea(i,j);i--,j++)
line++;
for(i=row+1,j=col-1;chess[i][j]==chessColor && !overArea(i,j);i++,j--)
line++;
return line;
}
}
视图层
package view;
import entity.FiveChess;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
public class ChessPane extends Pane {
private FiveChess fiveChess;
private Canvas canvas;
private GraphicsContext gc;
//棋盘距窗口边框左、上的距离
private double align = 70;
public ChessPane(FiveChess fiveChess) {
this.fiveChess = fiveChess;
draw();
getChildren().add(canvas);
}
public double getAlign() {
return align;
}
public void setAlign(double align) {
this.align = align;
}
/**
* 绘制棋盘
*/
public void draw() {
canvas = new Canvas(700,700);
this.gc = canvas.getGraphicsContext2D();
double cell = fiveChess.getCellLen();
//填充棋盘颜色
gc.setFill(Color.BURLYWOOD);
gc.fillRect(align-15, align-15, fiveChess.getWidth()+30, fiveChess.getHeight()+30);
for(int y=1;y<fiveChess.getN();y++) {
//画横线
gc.strokeLine(align, y*cell+align, fiveChess.getWidth()+align, y*cell+align);
}
for(int x=1;x<fiveChess.getN();x++) {
//画竖线
gc.strokeLine(x*cell+align, align, x*cell+align, fiveChess.getHeight()+align);
}
/**
* 天元(7,7),四个星位(3,3),(3,11),(11,3),(11,11)
* 不想要这五个点的可以不要这一部分,没有什么影响
**/
for(int i=3;i<=14;i+=4)
for(int j=3;j<=14;) {
gc.setFill(Color.BLACK);
//画天元
if(i == 7) {
j = 7;
gc.strokeOval(i*cell+align-4, j*cell+align-4, 8, 8);
gc.fillOval(i*cell+align-4, j*cell+align-4, 8, 8);
break;
}
//画星位
else {
gc.strokeOval(i*cell+align-4, j*cell+align-4, 8, 8);
gc.fillOval(i*cell+align-4, j*cell+align-4, 8, 8);
j += 8;
}
}
//边框加粗
gc.setLineWidth(3.0f);
gc.strokeRect(align, align, fiveChess.getWidth(), fiveChess.getHeight());
}
/**
* 绘制棋子
*/
public void paintChess(int x,int y) {
double cell = fiveChess.getCellLen();
if(fiveChess.getCurrentSide() == 'B')
gc.setFill(Color.BLACK);
else
gc.setFill(Color.WHITE);
gc.strokeOval(align+x*cell-cell/2, align+y*cell-cell/2, cell, cell);
gc.fillOval(align+x*cell-cell/2, align+y*cell-cell/2, cell, cell);
}
}
控制层
package controler;
import entity.FiveChess;
import javafx.event.EventHandler;
import javafx.scene.control.Alert;
import javafx.scene.input.MouseEvent;
import view.ChessPane;
public class PlayAction implements EventHandler<MouseEvent> {
private FiveChess fiveChess;
private ChessPane chessPane;
public PlayAction(FiveChess fiveChess, ChessPane chessPane) {
this.fiveChess = fiveChess;
this.chessPane = chessPane;
}
@Override
public void handle(MouseEvent event) {
// TODO Auto-generated method stub
double cellLen = fiveChess.getCellLen();
//定位
int x = (int)((event.getX()-chessPane.getAlign()+cellLen/2)/cellLen);
int y = (int)((event.getY()-chessPane.getAlign()+cellLen/2)/cellLen);
//如果棋子的位置合法就画出棋子
if(fiveChess.play(x, y))
chessPane.paintChess(x, y);
if(fiveChess.judgeGame(x, y, fiveChess.getCurrentSide())) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("五子棋游戏");
alert.setContentText("游戏结束,"+fiveChess.getCurrentSide()+"获胜!");
alert.showAndWait();
}
}
}
测试层
package test;
import controler.PlayAction;
import entity.FiveChess;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import view.ChessPane;
public class Test extends Application {
public static void main(String[] args) {
// TODO Auto-generated method stub
Application.launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
FiveChess fiveChess = new FiveChess();
ChessPane chessPane = new ChessPane(fiveChess);
PlayAction playAction = new PlayAction(fiveChess,chessPane);
chessPane.setOnMouseClicked(playAction);
Scene scene = new Scene(chessPane,700,700);
primaryStage.setScene(scene);
primaryStage.setTitle("五子棋");
primaryStage.show();
}
}
效果图