##五子棋*
需求分析
完成一个简单的五子棋游戏
界面
- 窗体【带标题和背景】
- 棋盘【棋盘,棋子】
- 图标【白子,黑子,重来】
业务
- 页面展示
- 选择先走的棋子
- 落子开始下棋
- 黑白切换
- 判断输赢,如何判断赢了
- 重来
模型
- 棋子
- 棋盘
- 位置
- 状态数据
架构
- 视图(View):展示界面
- 控制器|处理器(Controller):处理请求,动作,处理数据
- 模型(Model):数据存储
- 常量(Constant):保存固定数据
- 测试(Test):每一步都应该测试,确保正确性
**
实现
- 新建包、准备资源、工具类、模型
资源
- 静态资源,图片,音频等
工具类
package com.example.constant
public class Constant {
//窗口宽高
public static final int WINDOW_W = 800;
public static final int WINDOW_H = 650;
//窗口位置
public static final int WINDOW_X = 300;
public static final int WINDOW_Y = 200;
//棋盘格子边长
public static final int NODE_W = 30;
//棋子路径
public static final String WHITE_PATH = "images/白子.png";
public static final String BLACK_PATH = "images/黑子.png";
//落子半径
public static final int R = 12;
//棋子边长
public static final int PIECE_W = 24;
//图标宽高
public static final int ICON_W = 120;
public static final int ICON_H = 60;
//图标的位置
public static final int WHITE_ICON_X = WINDOW_X - 150;
public static final int WHITE_ICON_Y = 150;
public static final int BLACK_ICON_X = WINDOW_X - 150;
public static final int BLACK_ICON_Y = 250;
public static final int START_ICONX = WINDOW_X - 150;
public static final int START_ICONy = 350;
//图标路径
public static final String WHITE_ICON = "images/白子.png";
public static final String BLCK_ICON = "images/黑子.png";
public static final String RESTART_ICON = "images/重来png";
public static final int BLACK = -1;
public static final int WHITE = 1;
public static final int NONE = 0;
}
模型
- Postion:每一个棋子的位置【在二维数组中的行列】
- Node:棋盘中的每一个格子【同时也映射棋子】
- CheckerBoard:棋盘
- Stamp:状态类,游戏是否开始,该谁下,检查当前落子是否赢得比赛
视图和控制器
基本界面
只提供最基本的窗体和静态内容的展示【棋盘,图标】,后期可以有选择的重绘整个棋盘
BaseFrame
package view;
import controller.BaseFrameController;
import util.Constant;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
/*
* 基本窗体
* 显示初始内容
* 设置:位置,大小,标题,背景*/
public class BaseFrame extends JFrame {
BaseFrameController baseFrameController = new BaseFrameController();
//窗体初始化
public BaseFrame(){
super.setLocation(Constant.WINDOW_X,Constant.WINDOW_Y);//位置
super.setSize(Constant.WINDOW_W,Constant.WINDOW_H);//大小
super.setTitle("五子棋");//标题
super.setBackground(new Color(255,255,255)); //BG COLOR
super.setResizable(false);
super.setVisible(true);
super.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0); //退出程序
}
});
}
/*绘制窗体内容
* 图标*/
@Override
public void paint(Graphics g) {
baseFrameController.paintCheckerBoarad(g);
try {
baseFrameController.paintIcon(g);
} catch (IOException e) {
e.printStackTrace();
}
}
}
基本控制器Basectroller
```java
package controller;
/*
* 基本界面的处理
* 绘制棋盘
* 绘制图表*/
import model.CheckerBoard;
import model.Node;
import util.Constant;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.File;
import java.io.IOException;
public class BaseFrameController {
//绘制棋盘
//遍历棋盘 利用棋子自身的绘制工能将自己展示
public void paintCheckerBoarad (Graphics g){
for (Node[] nodes : CheckerBoard.checkerboard) {
for (Node node:nodes){
node.drawNode(g);
}
}
}
/*画图标
* 白子
* 黑子
* 重来
* 数据在常量类中【路径】【大小】【位置】*/
public void paintIcon(Graphics g) throws IOException {
//白子 黑子 重来图标
Image whiteImage = ImageIO.read(new File(Constant.WHITE_ICON));
Image blackImage = ImageIO.read(new File(Constant.BLCK_ICON));
Image start = ImageIO.read((new File(Constant.RESTART_ICON)));
g.drawImage(whiteImage,Constant.WHITE_ICON_X,Constant.WHITE_ICON_Y,Constant.ICON_W,Constant.ICON_H,null);
g.drawImage(blackImage,Constant.BLACK_ICON_X,Constant.BLACK_ICON_Y,Constant.ICON_W,Constant.ICON_H,null);
g.drawImage(start,Constant.START_ICONX,Constant.START_ICONy,Constant.ICON_W,Constant.ICON_H,null);
}
}
``主界面和处理
- 在主界面中增加接收用户的点击操作【选先手开始游戏】
2.落子操作
遍历数组
在落子之后去确定前后的距离是否有连续5颗棋子
4.是否胜利
遍历check 4个方向
private boolean isWin(Node node){
if(horizon()){
System.out.println("水平方向胜利");
return true;
}
if(vertical()){
System.out.println("垂直方向胜利");
return true;
}
if(lurd()){
System.out.println("左上方向胜利");
return true;
}
if(ldru()){
System.out.println("左上方向胜利");
return true;
}
return false;
}
/*水平方向判断
* 找到起始位置
* 找到终止点*/
private boolean horizon(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
int fd = frontDistance(postion.getCol());
int bd = backDistance(postion.getCol());
//起始列
int start = postion.getCol() - fd;
//终止列
int end = postion.getCol() + bd;
//同一行
int row = postion.getRow();
int count = 0;
for(int i = start;i<=end;i++){
if(node.getValue() != CheckerBoard.checkerboard[row][i].getValue()){
count = 0;
}else {
//如果有5可连续棋子 ,赢了
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*垂直方向是否胜利*/
private boolean vertical(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
int fd = frontDistance(postion.getRow());
int bd = backDistance(postion.getRow());
//起始行
int start = postion.getRow() - fd;
//中支行
int end = postion.getRow() + bd;
//相同列
int col =postion.getCol();
int count = 0;
for (int i = start; i <= end ; i++) {
if(node.getValue()!=CheckerBoard.checkerboard[i][col].getValue()){
count = 0;
}else {
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*
* 左上右下的胜利判断
* 1.前面的距离【x小,y小】取小距离
* 2.后面的距离【x打,y大】取小距离*/
private boolean lurd(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
//行前边距离
int fdRow = frontDistance(postion.getRow());
//行的后边距离
int bdRow = backDistance(postion.getRow());
//列的前后距离
int fdCol = frontDistance(postion.getCol());
int bdCol = backDistance(postion.getCol());
//以行作为标准
//行向前 列向后
int fd = Math.min(fdRow,bdCol);
//行向后 列向前
int bd = Math.min(bdRow,fdCol);
//行的起始位置
int startrow = postion.getRow() - fd;
//行的截止位置
int endrow = postion.getRow() + bd;
//列的起始位置
int startcol = postion.getCol() + fd;
//列的截止为止
int endcol = postion.getCol() - bd;
int count = 0;
for(int row =startrow,col =startcol;row<=endrow && col<=endcol;row++,col++){
if(node.getValue() != CheckerBoard.checkerboard[row][col].getValue()){
count = 0;
}else {
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*
* 左下右上的胜利判断
* 1.前面的距离【x小,y小】取小距离
* 2.后面的距离【x打,y大】取小距离*/
private boolean ldru(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
//行前边距离
int fdRow = frontDistance(postion.getRow());
//行的后边距离
int bdRow = backDistance(postion.getRow());
//列的前后距离
int fdCol = frontDistance(postion.getCol());
int bdCol = backDistance(postion.getCol());
//取前边小值
int fd = Math.min(fdRow,bdCol);
int bd = Math.min(bdRow,fdCol);
//行的起始位置
int startrow = postion.getRow() - fd;
//行的截止位置
int endrow = postion.getRow() + bd;
//列的起始位置
int startcol = postion.getCol() + fd;
//列的截止为止
int endcol = postion.getCol() - bd;
int count = 0;
for(int row =startrow,col =startcol;row<=endrow && col>=endcol;row++,col--){
if(node.getValue() != CheckerBoard.checkerboard[row][col].getValue()){
count = 0;
}else {
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*通过中间值去找起始值
* mid
* */
private int frontDistance(int mid){
return mid>4?mid -1:mid;
}
private int backDistance(int mid){
int len = CheckerBoard.checkerboard.length;
return mid + 4 <len?4:len - mid -1;
}
5.胜利了
设置状态标记 , 此时赢了
该落子落子
落子后检查是否有赢得标记,如果有,则将状态回到最初【游戏初始化】,赢了标记留着没有开始,又是有人赢,必须要点击start才能选先手
重来
mainFrame
package view;
import controller.MainFrameController;
import model.Stamp;
import util.Constant;
import javax.swing.plaf.synth.SynthOptionPaneUI;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
/*
* 直接继承窗体
* 主界面
* 1.监听用户点击事件*/
public class MainFrame extends BaseFrame{
//创建处理器
MainFrameController mainFrameController = new MainFrameController();
public MainFrame(){
//添加鼠标事件监听
super.addMouseListener(new MouseAdapter() {
//单机事件的处理
@Override
public void mouseClicked(MouseEvent e) {
//获取点击位置
Point point = e.getPoint();
if(!Stamp.isGamestart()){
if(!Stamp.isWin()) {
//选先手,开始游戏
mainFrameController.chooseStart(point);
}else {
if (mainFrameController.reply(point)) {
repaint();
}
}
}else{
//游戏开始 落子操作
boolean flag = mainFrameController.palcing(point);
repaint();
}
}
});
}
@Override
public void paint(Graphics g) {
if(!Stamp.isGamestart()) {
//清空窗口
g.clearRect(0,0,Constant.WINDOW_W,Constant.WINDOW_H);
System.out.println("清空");
super.paint(g);
}
if(Stamp.isGamestart() && Stamp.getCurrent()!= null) {
//绘制落子
try {
Stamp.getCurrent().playNode(g);
if(Stamp.isWin()){
mainFrameController.mesg(g);
//所有状态初始化
mainFrameController.init();
System.out.println("重来");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
MainFrameController
package controller;
import model.CheckerBoard;
import model.Node;
import model.Postion;
import model.Stamp;
import util.Constant;
import java.awt.*;
/*
* 主界面操作处理
* */
public class MainFrameController extends BaseFrameController {
//用来选先手方法 开始游戏
//根据坐标 判断点击的是那个一个图标【白,黑】
//修改状态开始游戏
public void chooseStart(Point point) {
//选择白子 白色先手
if (Constant.WHITE_ICON_X <= point.x && point.x <= Constant.WHITE_ICON_X + Constant.ICON_W &&
Constant.WHITE_ICON_Y <= point.y && point.y <= Constant.WHITE_ICON_Y + Constant.ICON_H) {
Stamp.setGamestart(true);
Stamp.setWhich(1);
return;
}
//选择黑色 黑子先手
if (Constant.BLACK_ICON_X <= point.x && point.x <= Constant.BLACK_ICON_X + Constant.ICON_W &&
Constant.BLACK_ICON_Y <= point.y && point.y <= Constant.BLACK_ICON_Y + Constant.ICON_H) {
Stamp.setGamestart(true);
Stamp.setWhich(-1);
return;
}
}
/*
* 落子操作
* 1.能否落子
* 2.更改状态*/
public boolean palcing(Point point) {
Node node = findNode(point);
//如果有有合适的就更改状态
if (node != null) {
node.setValue(Stamp.getWhich());
if (node.getValue() == Constant.WHITE) {
node.setPath(Constant.WHITE_PATH);
System.out.println("白色");
} else if (node.getValue() == Constant.BLACK) {
node.setPath(Constant.BLACK_PATH);
System.out.println("黑色");
}
Stamp.setCurrent(node);
//落子结束
//判断是否胜利
if(isWin(node)){
Stamp.setWin(true);//设置赢了的标记
String str;
if(node.getValue() == Constant.WHITE){
str = "白色";
}else{
str = "黑色";
}
System.out.println(str +"赢了");
}else {
Stamp.setWhich(Stamp.getWhich() * -1);
}
return true;
}
return false;
}
/*判断是否胜利
* 横向
* 竖向
* 左上
* 右上*/
private boolean isWin(Node node){
if(horizon()){
System.out.println("水平方向胜利");
return true;
}
if(vertical()){
System.out.println("垂直方向胜利");
return true;
}
if(lurd()){
System.out.println("左上方向胜利");
return true;
}
if(ldru()){
System.out.println("左上方向胜利");
return true;
}
return false;
}
/*水平方向判断
* 找到起始位置
* 找到终止点*/
private boolean horizon(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
int fd = frontDistance(postion.getCol());
int bd = backDistance(postion.getCol());
//起始列
int start = postion.getCol() - fd;
//终止列
int end = postion.getCol() + bd;
//同一行
int row = postion.getRow();
int count = 0;
for(int i = start;i<=end;i++){
if(node.getValue() != CheckerBoard.checkerboard[row][i].getValue()){
count = 0;
}else {
//如果有5可连续棋子 ,赢了
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*垂直方向是否胜利*/
private boolean vertical(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
int fd = frontDistance(postion.getRow());
int bd = backDistance(postion.getRow());
//起始行
int start = postion.getRow() - fd;
//中支行
int end = postion.getRow() + bd;
//相同列
int col =postion.getCol();
int count = 0;
for (int i = start; i <= end ; i++) {
if(node.getValue()!=CheckerBoard.checkerboard[i][col].getValue()){
count = 0;
}else {
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*
* 左上右下的胜利判断
* 1.前面的距离【x小,y小】取小距离
* 2.后面的距离【x打,y大】取小距离*/
private boolean lurd(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
//行前边距离
int fdRow = frontDistance(postion.getRow());
//行的后边距离
int bdRow = backDistance(postion.getRow());
//列的前后距离
int fdCol = frontDistance(postion.getCol());
int bdCol = backDistance(postion.getCol());
//以行作为标准
//行向前 列向后
int fd = Math.min(fdRow,fdCol);
//行向后 列向前
int bd = Math.min(bdRow,bdCol);
//行的起始位置
int startrow = postion.getRow() - fd;
//行的截止位置
int endrow = postion.getRow() + bd;
//列的起始位置
int startcol = postion.getCol() - fd;
//列的截止为止
int endcol = postion.getCol() + bd;
int count = 0;
for(int row =startrow,col =startcol;row<=endrow && col<=endcol;row++,col++){
if(node.getValue() != CheckerBoard.checkerboard[row][col].getValue()){
count = 0;
}else {
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*
* 左下右上的胜利判断
* 1.前面的距离【x小,y小】取小距离
* 2.后面的距离【x打,y大】取小距离*/
private boolean ldru(){
Node node = Stamp.getCurrent();
//当前点的位置信息
Postion postion = Stamp.getCurrent().getPostionl();
//行前边距离
int fdRow = frontDistance(postion.getRow());
//行的后边距离
int bdRow = backDistance(postion.getRow());
//列的前后距离
int fdCol = frontDistance(postion.getCol());
int bdCol = backDistance(postion.getCol());
//取前边小值
int fd = Math.min(fdRow,bdCol);
int bd = Math.min(bdRow,fdCol);
//行的起始位置
int startrow = postion.getRow() - fd;
//行的截止位置
int endrow = postion.getRow() + bd;
//列的起始位置
int startcol = postion.getCol() + fd;
//列的截止为止
int endcol = postion.getCol() - bd;
int count = 0;
for(int row =startrow,col =startcol;row<=endrow && col>=endcol;row++,col--){
if(node.getValue() != CheckerBoard.checkerboard[row][col].getValue()){
count = 0;
}else {
count++;
if(count == 5){
return true;
}
}
}
return false;
}
/*通过中间值去找起始值
* mid
* */
private int frontDistance(int mid){
return mid>4?mid -1:mid;
}
private int backDistance(int mid){
int len = CheckerBoard.checkerboard.length;
return mid + 4 <len?4:len - mid -1;
}
/*找合适落子的位置
* 遍历整个棋盘
* */
private Node findNode(Point point) {
for (Node[] nodes : CheckerBoard.checkerboard) {
for (Node node : nodes) {
if (node.clickNode(point.x, point.y) && node.getValue() == Constant.NONE) {
return node;
}
}
}
return null;
}
/*将所有标记重置*/
public void init() {
Stamp.setWhich(Constant.NONE);
Stamp.setGamestart(false);
for (Node[] nodes : CheckerBoard.checkerboard) {
for (Node node : nodes) {
node.setPath(null);
node.setValue(0);
}
}
}
public boolean reply(Point point) {
if(Constant.START_ICONX <= point.x && point.x <= Constant.START_ICONX+ Constant.ICON_W &&
Constant.START_ICONy <= point.y && point.y <= Constant.START_ICONy + Constant.ICON_H){
Stamp.setWin(false);
return true;
}
return false;
}
/*
* 给出提示
* */
public void mesg(Graphics g) {
String msg;
if(Stamp.getWhich() == 1){
msg = "白方获胜,大笨蛋";
}else {
msg = "黑色赢了,大白痴";
}
g.setFont(new Font("微软雅黑",Font.BOLD,45));
g.drawString(msg,150,400);
}
}
游戏效果
需要源码留邮箱。。。