Fork me on GitHub

五子棋

前言

突发奇想,使用java来写一个游戏,今日实现的程序为:五子棋。

实现后的效果图

代码实现

五子棋的棋盘画板类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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();
}
}
五子棋配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;
}
线条类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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;
}
}
坐标类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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;
}
}
棋子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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;
}
字符串绘制类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}
五子棋辅助类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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());
}
}
启动逻辑主类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
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));
}
}
启动调用代码

简单一句即可

1
new GoBang(GoBangBoardConfig.builder().build()).showGraph();

结语

到此一个简单的五子棋程序便实现完成了,启动后,黑白双方交互下棋,有一人胜利即结束,程序为版本一,有诸多地方和功能能够更好的改进,欢迎留言交流!

-------------本文结束感谢您的阅读-------------
如果您对博主的原创满意,欢迎您继续支持下博主~