前言
突发奇想,使用java来写一个游戏,今日实现的程序为:五子棋。
实现后的效果图
代码实现
五子棋的棋盘画板类
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author jack
*/
public class GoBangBoard extends JPanel {
private List<LineBean> lineList;
private List<StrBean> strList;
public GoBangBoard(List<LineBean> lineList) {
this.lineList = lineList;
this.strList = new ArrayList<>();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (LineBean lineBean : lineList) {
g.drawLine(lineBean.getStartX(), lineBean.getStartY(),
lineBean.getEndX(), lineBean.getEndY());
}
// 字体高度
g.setFont(new Font("宋体", Font.PLAIN, 20));
for (StrBean strBean : strList) {
g.setColor(strBean.getTextColor());
g.drawString(strBean.getStr(), strBean.getX() - 10, strBean.getY() - 22);
}
}
public void drawString(String str, int x, int y, Color textColor) {
strList.add(new StrBean(str, x, y, textColor));
repaint();
}
}
五子棋配置类
import lombok.Builder;
import lombok.Data;
/**
* Description:画板配置
*
* @author jack
* @version 1.0
*/
@Data
@Builder
public class GoBangBoardConfig {
private GoBangBoardConfig() {}
private GoBangBoardConfig(int sideCount, int sideLen, int successCount) {
this.sideCount = sideCount;
this.sideLen = sideLen;
this.successCount = successCount;
}
/***
* <p>
* 横纵格子数
* </p>
*/
private int sideCount;
/***
* <p>
* 边长
* </p>
*/
private int sideLen;
/***
* <p>
* 成功所需要连的棋子数,譬如五子棋即为5
* </p>
*/
private int successCount;
}
线条类
import lombok.Getter;
import lombok.Setter;
/**
* Description:线条
*
* @author jack
* @version 1.0
*/
@Getter
@Setter
public class LineBean {
public LineBean(int startX, int startY, int endX, int endY) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
}
private int startX;
private int startY;
private int endX;
private int endY;
public LineBean add(int borderMargin) {
this.startX += borderMargin;
this.startY += borderMargin;
this.endX += borderMargin;
this.endY += borderMargin;
return this;
}
}
坐标类
import lombok.Getter;
import lombok.Setter;
import java.util.Objects;
/**
* Description:坐标类
*
* @author jack
* @version 1.0
*/
@Getter
@Setter
public class P {
private int x;
private int y;
public P(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof P)) {
return false;
}
if (this.hashCode() != obj.hashCode()) {
return false;
}
P other = (P) obj;
return x == other.x && y == other.y;
}
}
棋子类
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* Description:棋子类
*
* @author jack
* @version 1.0
*/
@Getter
@Setter
@Builder
public class Point {
private Point() {}
private Point(P p, short state) {
this.p = p;
this.state = state;
}
private P p;
/***
* <p>
* 棋子的状态
* 0-空白、1-黑棋、2-白棋
* </p>
*/
private short state;
}
字符串绘制类
import lombok.Data;
import java.awt.*;
/**
* Description:字符串绘制类
*
* @author jack
* @version 1.0
*/
@Data
public class StrBean {
public StrBean(String str, int x, int y, Color textColor) {
this.str = str;
this.x = x;
this.y = y;
this.textColor = textColor;
}
private String str;
private int x;
private int y;
private Color textColor;
}
五子棋辅助类
import com.jack.game.piece.bean.GoBangBoardConfig;
/**
* Description:五子棋 辅助类
*
* @author jack
* @version 1.0
*/
public class GoBangHelper {
private static final int MIN_SUCCESS_COUNT = 5;
private static final int MIN_SIDE_COUNT = 10;
private static final int MIN_SIDE_LEN = 400;
public static int getSuccessCount(GoBangBoardConfig config) {
return Math.max(MIN_SUCCESS_COUNT, config.getSuccessCount());
}
public static int getSideCount(GoBangBoardConfig config) {
return Math.max(MIN_SIDE_COUNT, config.getSideCount());
}
public static int getSideLen(GoBangBoardConfig config) {
return Math.max(MIN_SIDE_LEN, config.getSideLen());
}
}
启动逻辑主类
import com.jack.game.piece.bean.*;
import com.jack.game.piece.bean.Point;
import com.jack.game.piece.helper.GoBangHelper;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Description:五子棋
*
* @author jack
* @version 1.0
*/
public class GoBang {
private static final int TOP = 0;
private static final int RIGHT_TOP = 1;
private static final int RIGHT = 2;
private static final int RIGHT_BOTTOM = 3;
private static final int BOTTOM = 4;
private static final int LEFT_BOTTOM = 5;
private static final int LEFT = 6;
private static final int LEFT_TOP = 7;
private static final List<Integer> DIR_LIST;
/***
* <p>
* 空白
* </p>
*/
private static final short BLANK_STATE = 0;
/***
* <p>
* 黑棋
* </p>
*/
private static final short BLACK_STATE = 1;
/***
* <p>
* 白棋
* </p>
*/
private static final short WHITE_STATE = 2;
private static final short[] STATE_ARR;
private static final String[] TEXT_ARR;
private static final Color[] TEXT_COLOR_ARR;
/***
* <p>
* 当前回合的状态
* </p>
*/
private int curStateStartOffset = 0;
private static final Map<Short, String> SUCCESS_MSG_MAP;
static {
DIR_LIST = new ArrayList<>();
DIR_LIST.add(TOP);
DIR_LIST.add(RIGHT_TOP);
DIR_LIST.add(RIGHT);
DIR_LIST.add(RIGHT_BOTTOM);
DIR_LIST.add(BOTTOM);
DIR_LIST.add(LEFT_BOTTOM);
DIR_LIST.add(LEFT);
DIR_LIST.add(LEFT_TOP);
SUCCESS_MSG_MAP = new HashMap<>();
SUCCESS_MSG_MAP.put(BLACK_STATE, "恭喜黑棋方胜利!");
SUCCESS_MSG_MAP.put(WHITE_STATE, "恭喜白棋方胜利!");
STATE_ARR = new short[]{BLACK_STATE, WHITE_STATE};
TEXT_ARR = new String[]{"●", "●"};
TEXT_COLOR_ARR = new Color[] {Color.BLACK, Color.WHITE};
}
/***
* <p>
* 所有点
* </p>
*/
private Point[][] allPointArr;
/***
* <p>
* 灵敏距离范围
* </p>
*/
private double allowLenRange;
/***
* <p>
* 2个棋子之间的间隔
* </p>
*/
private double interval;
private int sideCount;
private int sideLen;
private int successCount;
private boolean isOver = false;
public GoBang(GoBangBoardConfig boardConfig) {
successCount = GoBangHelper.getSuccessCount(boardConfig);
sideCount = GoBangHelper.getSideCount(boardConfig);
allPointArr = new Point[sideCount][sideCount];
sideLen = GoBangHelper.getSideLen(boardConfig);
interval = sideLen * 1.0 / (sideCount - 1);
allowLenRange = interval / 3;
for (int i = 0; i < sideCount; i++) {
int x = (int) (i * interval);
for (int j = 0; j < sideCount; j++) {
int y = (int) (j * interval);
P p = new P(x + BORDER_MARGIN, y + BORDER_MARGIN + Y_BORDER_MARGIN);
Point point = Point.builder()
.p(p)
.state(BLANK_STATE)
.build();
allPointArr[i][j] = point;
}
}
}
private static final int BORDER_MARGIN = 30;
private static final int Y_BORDER_MARGIN = 30;
public void showGraph() {
JFrame jFrame = new JFrame();
List<LineBean> lineList = new ArrayList<>();
for (int i = 0; i < sideCount; i++) {
int x = (int) (i * interval);
lineList.add(new LineBean(x, 0, x, sideLen).add(BORDER_MARGIN));
}
for (int i = 0; i < sideCount; i++) {
int y = (int) (i * interval);
lineList.add(new LineBean(0, y, sideLen, y).add(BORDER_MARGIN));
}
GoBangBoard goBangBoard = new GoBangBoard(lineList);
jFrame.add(goBangBoard);
jFrame.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (!isOver) {
clickPoint(e.getX(), e.getY(), goBangBoard);
}
}
});
jFrame.setSize(sideLen + BORDER_MARGIN * 2,
sideLen + BORDER_MARGIN * 2 + Y_BORDER_MARGIN);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
private void clickPoint(int clickX, int clickY, GoBangBoard goBangBoard) {
short state = STATE_ARR[curStateStartOffset];
P p = getClickP(clickX, clickY);
if (p == null) {
// 点击的不在灵敏度范围内
return;
}
int offsetX = transPosX(p.getX());
int offsetY = transPosY(p.getY());
Point curPoint = allPointArr[offsetX][offsetY];
if (curPoint.getState() != BLANK_STATE) {
// 该点已用
return;
}
curPoint.setState(state);
// 刷新渲染
goBangBoard.drawString(TEXT_ARR[curStateStartOffset], p.getX(), p.getY(), TEXT_COLOR_ARR[curStateStartOffset]);
// 检查是否成功
if (checkCurSuccess(curPoint, offsetX, offsetY)) {
isOver = true;
System.out.println(SUCCESS_MSG_MAP.get(state));
JOptionPane.showMessageDialog(goBangBoard, SUCCESS_MSG_MAP.get(state));
}
curStateStartOffset = (curStateStartOffset + 1) % STATE_ARR.length;
}
private boolean checkCurSuccess(Point curPoint, int offsetX, int offsetY) {
short state = curPoint.getState();
if (state == BLANK_STATE) {
// 空白
return false;
}
// 检查当前点周围是否满足
int[] countArr = new int[DIR_LIST.size()];
for (int dir : DIR_LIST) {
// 得减去自身
countArr[dir] = getAroundCount(offsetX, offsetY, state, dir) - 1;
}
if (countArr[TOP] + countArr[BOTTOM] == successCount - 1) {
// 竖向成功
return true;
}
if (countArr[LEFT] + countArr[RIGHT] == successCount - 1) {
// 横向成功
return true;
}
if (countArr[LEFT_TOP] + countArr[RIGHT_BOTTOM] == successCount - 1) {
// 左上-右下向成功
return true;
}
// 左下-右上向成功
return countArr[LEFT_BOTTOM] + countArr[RIGHT_TOP] == successCount - 1;
}
private int getAroundCount(int x, int y, short state, int dir) {
Point point = allPointArr[x][y];
short curState = point.getState();
if (curState == state) {
// 同色棋子
int offsetX;
int offsetY;
switch (dir) {
case TOP:
// 上
offsetX = x;
offsetY = y - 1;
break;
case RIGHT_TOP:
// 右上
offsetX = x + 1;
offsetY = y - 1;
break;
case RIGHT:
// 右
offsetX = x + 1;
offsetY = y;
break;
case RIGHT_BOTTOM:
// 右下
offsetX = x + 1;
offsetY = y + 1;
break;
case BOTTOM:
// 下
offsetX = x;
offsetY = y + 1;
break;
case LEFT_BOTTOM:
// 左下
offsetX = x - 1;
offsetY = y + 1;
break;
case LEFT:
// 左
offsetX = x - 1;
offsetY = y;
break;
case LEFT_TOP:
// 左上
offsetX = x - 1;
offsetY = y - 1;
break;
default:
return 1;
}
if (isValidOffset(offsetX, offsetY)) {
return getAroundCount(offsetX, offsetY, curState, dir) + 1;
}
return 1;
} else {
// 不同色
return 0;
}
}
private boolean isValidOffset(int offsetX, int offsetY) {
int max = allPointArr.length;
return offsetX >= 0 && offsetX < max && offsetY >= 0 && offsetY < max;
}
/***
* <p>
* 通过坐标找到最近的点
* </p>
* @author jack
*
* @param realX 点击的实际点 x
* @param realY 点击的实际点 y
* @return com.jack.game.piece.bean.P
*/
private P getClickP(int realX, int realY) {
double margin = Integer.MAX_VALUE;
int resX = realX;
int resY = realY;
for (Point[] points : allPointArr) {
for (Point point : points) {
int x = point.getP().getX();
int y = point.getP().getY();
if (realX == x && realY == y) {
return new P(resX, resY);
}
int marginX = Math.abs(x - realX);
int marginY = Math.abs(y - realY);
double tmp = Math.sqrt(marginX * marginX + marginY * marginY);
if (tmp < margin) {
margin = tmp;
resX = x;
resY = y;
}
}
}
if (margin > allowLenRange) {
// 最接近的也不在灵敏范围内
// 不识别为坐标点
return null;
}
return new P(resX, resY);
}
private int transPosX(int realX) {
return (int) (Math.ceil((realX - BORDER_MARGIN) / interval));
}
private int transPosY(int realY) {
return (int) (Math.ceil((realY - BORDER_MARGIN - Y_BORDER_MARGIN) / interval));
}
}
启动调用代码
简单一句即可
new GoBang(GoBangBoardConfig.builder().build()).showGraph();
结语
到此一个简单的五子棋程序便实现完成了,启动后,黑白双方交互下棋,有一人胜利即结束,程序为版本一,有诸多地方和功能能够更好的改进,欢迎留言交流!
如果您看到了这里,欢迎和我沟通交流!
一个95后码农
个人博客:fy-blog