1、实训目标
通过设计和开发移动应用产品,学习和掌握以下方法和技术
Alpha-Beta剪枝算法学习及应用
2、实训模块
1.棋盘绘制
绘制五子棋棋盘
2.五子棋的人人对弈实现
实现双方手动下五子棋,定义下棋规则,判断下棋输赢
3.五子棋的人机对弈实现
初步实现人机对弈,采用估值函数和搜索树
4.基于极大值极小值算法的优化
初步进行对弈优化
5.基于Alpha-Beta剪枝算法优化
对算法进一步优化
运行界面:
核心算法记录:
一.极大极小值算法
二.alpha-beta剪枝
源码如下:
1.GoBangTest.java
package wuziqi;
//运行入口
public class GoBangTest {
public static void main(String[] args) {
GoBangFrame frame=new GoBangFrame();
frame.start();
}
}
2.ChessBean.java
package wuziqi;
//棋子
public class ChessBean implements Comparable{
//棋子的坐标(0,0)-(14,14)
private int X;
private int Y;
//棋子属于哪个玩家 0:空(没有下棋子) 1:黑子 2:白子
private int player;
//落子顺序
private int orderNumber;
private int offense;
private int defense;
private int sum;
private StringBuffer buffer=new StringBuffer();
public ChessBean(){
}
//定义非空参数构造器,需要先定义一个无参的构造器
public ChessBean(int x,int y,int player,int orderNumber){
this.X=x;
this.Y=y;
this.player=player;
this.orderNumber=orderNumber;
}
public int getX() {
return X;
}
public void setX(int x) {
X = x;
}
public int getY() {
return Y;
}
public void setY(int y) {
Y = y;
}
public int getPlayer() {
return player;
}
public void setPlayer(int player) {
this.player = player;
}
public int getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(int orderNumber) {
this.orderNumber = orderNumber;
}
public int getOffense() {
return offense;
}
public void setOffense(int offense) {
this.offense = offense;
}
public int getDefense() {
return defense;
}
public void setDefense(int defense) {
this.defense = defense;
}
public int getSum() {
return sum;
}
public void setSum(int sum) {
this.sum = sum;
}
public StringBuffer getBuffer() {
return buffer;
}
public void setBuffer(StringBuffer buffer) {
this.buffer = buffer;
}
@Override
public int compareTo(ChessBean o) {
if(this.getSum()>o.getSum()){
return -1;
}else {
if(this.getSum()
3.GoBangContantes.java
package wuziqi;
//存放常量
public class GoBangContantes {
//游戏界面宽和高
public static final int GAME_WIDTH=900;
public static final int GAME_HEIGHT=700;
//棋盘宽和高
public static final int PANEL_WIDTH=650;
public static final int PANEL_HEIGHT=700;
//线的大小和数量
public static final int LINE_SIZE=40;
public static final int LINE_NUMBER=15;
//棋盘的偏移量和star
public static final int OFFSET=40;
public static final int STAR=10;
//定义棋子
public static final int EMPTY=0;
public static final int BLACK=1;
public static final int WHITE=2;
}
4.GoBangFrame.java
package wuziqi;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.TitledBorder;
//游戏窗口类
public class GoBangFrame extends JFrame{
private GoBangPanel panel;
private JCheckBox showorder;
private JButton nudo;
private JButton newGame;
private JLabel label1,label2;
private JPanel panel1,panel2,panel3,panel4,panel5,panel6;
private JTextArea textArea;
private JRadioButton model,robot,intel,radioButton4,first,radioButton6;
private ButtonGroup buttonGroup1,buttonGroup2,buttonGroup3;
private JComboBox depth,nodeCount;
//启动游戏窗口
public void start(){
panel=new GoBangPanel();
add(panel,BorderLayout.WEST);
JPanel rightPanel=new JPanel();
//设置垂直布局
rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
//多行文本框
panel1=new JPanel();
panel1.setBorder(new TitledBorder("在键盘上单击鼠标右键,查看各个估值"));
panel1.setLayout(new BorderLayout());
textArea=new JTextArea();
textArea.setEditable(false);
panel1.add(new JScrollPane(textArea));
rightPanel.add(panel1);
//模式
panel2=new JPanel();
panel2.setBorder(new TitledBorder("模式"));
//单选框
model=new JRadioButton("人人对战");
model.setSelected(true);
robot=new JRadioButton("人机对战");
//单选框互斥
buttonGroup1=new ButtonGroup();
buttonGroup1.add(model);
buttonGroup1.add(robot);
panel2.add(model);
panel2.add(robot);
rightPanel.add(panel2);
//智能
panel3=new JPanel();
panel3.setBorder(new TitledBorder("智能"));
//单选框
intel=new JRadioButton("估值函数");
intel.setSelected(true);
radioButton4=new JRadioButton("估值函数+搜索树");
//单选框互斥
buttonGroup2=new ButtonGroup();
buttonGroup2.add(intel);
buttonGroup2.add(radioButton4);
panel3.add(intel);
panel3.add(radioButton4);
rightPanel.add(panel3);
//搜索树
panel4=new JPanel();
panel4.setBorder(new TitledBorder("搜索树"));
label1=new JLabel("搜索深度");
depth=new JComboBox<>(new Integer[] {1,2,3});
label2=new JLabel("每层节点");
nodeCount=new JComboBox<>(new Integer[] {1,2,3});
panel4.add(label1);
panel4.add(depth);
panel4.add(label2);
panel4.add(nodeCount);
rightPanel.add(panel4);
//其他
panel5=new JPanel();
panel5.setBorder(new TitledBorder("其他"));
showorder=new JCheckBox("显示顺序");
nudo=new JButton("悔棋");
newGame=new JButton("新游戏");
showorder.addMouseListener(mouseListener);
nudo.addMouseListener(mouseListener);
newGame.addMouseListener(mouseListener);
panel5.add(showorder);
panel5.add(nudo);
panel5.add(newGame);
rightPanel.add(panel5);
//人机模式
panel6=new JPanel();
panel6.setBorder(new TitledBorder("人机模式"));
//单选框
first=new JRadioButton("人类先手");
first.setSelected(true);
radioButton6=new JRadioButton("机器先手");
//单选框互斥
buttonGroup3=new ButtonGroup();
buttonGroup3.add(first);
buttonGroup3.add(radioButton6);
panel6.add(first);
panel6.add(radioButton6);
rightPanel.add(panel6);
add(rightPanel);
//游戏窗口设置
setSize(GoBangContantes.GAME_WIDTH,GoBangContantes.GAME_HEIGHT);
setLocation(200, 200);
setTitle("五子棋");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
//监听器
private MouseListener mouseListener=new MouseListener() {
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent e) {
Object object=e.getSource();
if(object==nudo){
if(model.isSelected())
panel.huiqi();
else if(robot.isSelected())
panel.huiqi2();
}else if(object==showorder){
//显示状态传到panel进行判断
panel.showOrder(showorder.isSelected());
}else if(object==newGame){
panel.newGame(model.isSelected()?true:false,intel.isSelected()?true:false,
(int)depth.getSelectedItem(),(int)nodeCount.getSelectedItem(),
first.isSelected()?true:false,showorder.isSelected(),textArea);
}
}
};
}
5.GoBangPanel.java
package wuziqi;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.List;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.security.GuardedObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
//左侧棋盘
public class GoBangPanel extends JPanel{
private int currentPlayer=GoBangContantes.BLACK;
//true为游戏结束
private boolean isGameOver=false;
//记录棋子的步数
private int count=0;
//模式true为人人对战 false为人机对战
private boolean model;
//true:估值函数 false:估值函数+搜索树
private boolean intel;
//搜索深度和节点数量
private int depth;
private int nodeCount;
//true为人类先手,false为机器先手
private boolean first;
//
private JTextArea textArea;
//显示状态
private boolean showOrder;
private ChessBean chessBeanBytree;
private int x,y;
ChessBean[][] chessBeans=new ChessBean[GoBangContantes.LINE_NUMBER][GoBangContantes.LINE_NUMBER];
//创建棋盘
public GoBangPanel(){
//setSize(650,700);
setPreferredSize(new Dimension(GoBangContantes.PANEL_WIDTH, GoBangContantes.PANEL_HEIGHT));
setBackground(Color.ORANGE);
//棋盘绑定鼠标移动事件
addMouseMotionListener(mouseMotionListener);
//棋盘绑定鼠标点击事件
addMouseListener(mouseListener);
//对棋盘上的棋子初始化
for(int i=0;i=GoBangContantes.OFFSET&&x_mv<=GoBangContantes.OFFSET+(GoBangContantes.LINE_NUMBER-1)*GoBangContantes.LINE_SIZE
&&y_mv>=GoBangContantes.OFFSET&&y_mv<=GoBangContantes.OFFSET+(GoBangContantes.LINE_NUMBER-1)*GoBangContantes.LINE_SIZE)
{
x=(x_mv-GoBangContantes.OFFSET/2)/GoBangContantes.LINE_SIZE;
y=(y_mv-GoBangContantes.OFFSET/2)/GoBangContantes.LINE_SIZE;
repaint();
}
}
@Override
public void mouseDragged(MouseEvent arg0) {
// TODO Auto-generated method stub
}
};
//绘制棋盘
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d=(Graphics2D) g;
g2d.setStroke(new BasicStroke(2));//加粗线
drawLine(g2d);//画棋盘
drawStar(g2d);//画天元和星
drawTrips(g2d);//画提醒框
drawNumber(g2d);//画坐标
drawChess(g2d);//绘制棋子
drawOrderNumber(g2d);//绘制下棋顺序
}
//绘制棋盘
private void drawLine(Graphics2D g2d){
//横线
for(int i=0;i0;i--)
{
FontMetrics fn=g2d.getFontMetrics();
int height=fn.getAscent();
g2d.drawString(16-i+"",10,i*GoBangContantes.LINE_SIZE+height/2);
int width=fn.stringWidth(((char)(64+i))+"");
g2d.drawString(((char)(64+i))+"",GoBangContantes.LINE_SIZE*i-width/2,
GoBangContantes.OFFSET+GoBangContantes.LINE_NUMBER*GoBangContantes.LINE_SIZE);
}
}
//绘制棋子
private void drawChess(Graphics2D g2d) {
for(int i=0;ibean.getOrderNumber())
bean=tmp;
}
}
}
}
return bean;
}
//绘制下棋数字顺序
private void drawOrderNumber(Graphics2D g2d) {
if(showOrder){
g2d.setColor(Color.red);
for(int i=0;i=GoBangContantes.OFFSET&&x_mv<=GoBangContantes.OFFSET+(GoBangContantes.LINE_NUMBER-1)*GoBangContantes.LINE_SIZE
&&y_mv>=GoBangContantes.OFFSET&&y_mv<=GoBangContantes.OFFSET+(GoBangContantes.LINE_NUMBER-1)*GoBangContantes.LINE_SIZE)
{
x=(x_mv-GoBangContantes.OFFSET/2)/GoBangContantes.LINE_SIZE;
y=(y_mv-GoBangContantes.OFFSET/2)/GoBangContantes.LINE_SIZE;
if(e.getButton()==MouseEvent.BUTTON1){
//点击鼠标左键
if(model){
//人人对战
//落子
if(chessBeans[x][y].getPlayer()==GoBangContantes.EMPTY)
{
//为空落子
chessBeans[x][y]=new ChessBean(x,y,currentPlayer,count);
count++;
currentPlayer=3-currentPlayer;
checkWin(chessBeans[x][y]);
repaint();
}
}else{
//人机对战
if(intel){
//估值函数
//落子
if(chessBeans[x][y].getPlayer()==GoBangContantes.EMPTY)
{
//为空落子
chessBeans[x][y]=new ChessBean(x,y,currentPlayer,count);
count++;
currentPlayer=3-currentPlayer;
boolean win=checkWin(chessBeans[x][y]);
repaint();
//机器下棋
if(!win){
List list=getSortedBean(currentPlayer);
if(list.size()>0){
//获取最大的分值
ChessBean bean=list.get(0);
bean.setPlayer(currentPlayer);
bean.setOrderNumber(count);
count++;
currentPlayer=3-currentPlayer;
chessBeans[bean.getX()][bean.getY()]=bean;
checkWin(chessBeans[bean.getX()][bean.getY()]);
repaint();
}
}
}
}else{
//估值函数+搜索树
//落子
if(chessBeans[x][y].getPlayer()==GoBangContantes.EMPTY)
{
//为空落子
chessBeans[x][y]=new ChessBean(x,y,currentPlayer,count);
count++;
currentPlayer=3-currentPlayer;
boolean win=checkWin(chessBeans[x][y]);
repaint();
//机器下棋
if(!win){
getByTree2(0,currentPlayer,chessBeans,-Integer.MAX_VALUE,Integer.MAX_VALUE);
ChessBean bean=chessBeanBytree;
bean.setPlayer(currentPlayer);
bean.setOrderNumber(count);
count++;
currentPlayer=3-currentPlayer;
chessBeans[bean.getX()][bean.getY()]=bean;
checkWin(chessBeans[bean.getX()][bean.getY()]);
repaint();
/*ChessBean bean=getByTree(0,currentPlayer,chessBeans);
if(bean!=null)
{
bean.setPlayer(currentPlayer);
bean.setOrderNumber(count);
count++;
currentPlayer=3-currentPlayer;
chessBeans[bean.getX()][bean.getY()]=bean;
checkWin(chessBeans[bean.getX()][bean.getY()]);
repaint();
}*/
}
}
}
}
}else if(e.getButton()==MouseEvent.BUTTON3){
//点击鼠标右键
ChessBean bean=chessBeans[x][y];
int offense=getValue(bean,currentPlayer);
int defense=getValue(bean,3-currentPlayer);
int sum=offense+defense;
chessBeans[x][y].getBuffer().append("点(" + x + "," + y + ")的" + "攻击:" + offense + " "
+ "防御:" + defense + " " + "总和:" + (sum) + "\n\n");
textArea.append(chessBeans[x][y].getBuffer().toString());
}
}
}
@Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
};
//根据搜索树进行查询
protected ChessBean getByTree(int depth, int currentPlayer,ChessBean[][] chessBeans) {
//模拟下棋,克隆棋盘
ChessBean[][] chessBeans2=clone(chessBeans);
//计算空位置的得分
List list=getSortedBean(currentPlayer,chessBeans2);
if(this.depth==depth){
//搜索到指定深度
return list.get(0);
}
for(int i=0;iLevel.ALIVE_4.score){
return chessBean;
}else{
//模拟下棋,继续递归
chessBeans2[chessBean.getX()][chessBean.getY()].setPlayer(currentPlayer);
return getByTree(depth+1,3-currentPlayer , chessBeans2);
}
}
return null;
}
//极大极小值搜索
protected int getByTree2(int depth, int currentPlayer,ChessBean[][] chessBeans, int alpha, int beta) {
//模拟下棋,克隆棋盘
ChessBean[][] chessBeans2=clone(chessBeans);
//计算空位置的得分
List list=getSortedBean(currentPlayer,chessBeans2);
if(this.depth==depth){
//搜索到指定深度
return list.get(0).getSum();
}
for(int i=0;iLevel.ALIVE_4.score){
score=chessBean.getSum();
}else{
//模拟下棋,继续递归
chessBeans2[chessBean.getX()][chessBean.getY()].setPlayer(currentPlayer);
score=getByTree2(depth+1,3-currentPlayer , chessBeans2,alpha,beta);
}
if (depth % 2 == 0) {
// 自己,找最大值
if (score > alpha) {
alpha = score;
if (depth == 0) {
// 结果
chessBeanBytree=chessBean;
// System.out.println(chessBeansForTree);
}
}
if (alpha >= beta) {
// 剪枝
score = alpha;
return score;
}
} else {
if (score < beta) {
beta = score;
}
if (alpha >= beta) {
// 剪枝
score = beta;
return score;
}
}
}
return depth % 2 == 0 ? alpha : beta;
}
private List getSortedBean(int currentPlayer, ChessBean[][] chessBeans) {
List list=new ArrayList();
//初始化
for(int i=0;i=4)
{
result=true;
}
if(checkChessCount(chessBean,0,-1)+checkChessCount(chessBean,0,1)>=4)
{
result=true;
}
if(checkChessCount(chessBean,1,1)+checkChessCount(chessBean,-1,-1)>=4)
{
result=true;
}
if(checkChessCount(chessBean,1,-1)+checkChessCount(chessBean,-1,1)>=4)
{
result=true;
}
if(result){
//游戏结束
JOptionPane.showMessageDialog(GoBangPanel.this, "游戏结束");
isGameOver=true;
}
return result;
}
protected List getSortedBean(int currentPlayer) {
List list=new ArrayList();
//初始化
for(int i=0;i= 2
|| levelCount[Level.GO_4.index] >= 1 && levelCount[Level.ALIVE_3.index] >= 1)// 双活4,冲4活三
score = 10000;
else if (levelCount[Level.ALIVE_3.index] >= 2)// 双活3
score = 5000;
else if (levelCount[Level.SLEEP_3.index] >= 1 && levelCount[Level.ALIVE_3.index] >= 1)// 活3眠3
score = 1000;
else if (levelCount[Level.ALIVE_2.index] >= 2)// 双活2
score = 100;
else if (levelCount[Level.SLEEP_2.index] >= 1 && levelCount[Level.ALIVE_2.index] >= 1)// 活2眠2
score = 10;
score = Math.max(score, Math.max(Math.max(level1.score, level2.score), Math.max(level3.score, level4.score)));
return score;
}
//计算某个方向的棋形
private Level getLevel(ChessBean bean, int currentPlayer, Direction dire) {
String left="";
String right="";
if(dire==Direction.HENG){
left=getStringByDire(bean,currentPlayer,-1,0);
right=getStringByDire(bean,currentPlayer,1,0);
}else if(dire==Direction.SHU){
left=getStringByDire(bean,currentPlayer,0,1);
right=getStringByDire(bean,currentPlayer,0,-1);
}else if(dire==Direction.PIE){
left=getStringByDire(bean,currentPlayer,1,1);
right=getStringByDire(bean,currentPlayer,-1,-1);
}else if(dire==Direction.NA){
left=getStringByDire(bean,currentPlayer,-1,1);
right=getStringByDire(bean,currentPlayer,1,-1);
}
//正方向
String str=left+currentPlayer+right;
//chessBeans[bean.getX()][bean.getY()].getBuffer().append("(" + (bean.getX() + 1) + "," + (bean.getY()- 1) + ")" + dire + "\t" + str + "\t");
//反向
String strres=new StringBuffer(str).reverse().toString();
for(Level level:Level.values()){
//根据正则表达式进行比较
Pattern pattern=Pattern.compile(level.regex[currentPlayer-1]);
Matcher matcher=pattern.matcher(str);
//为true为1
boolean b1=matcher.find();
Matcher matcher2=pattern.matcher(strres);
//为true为1
boolean b2=matcher2.find();
if(b1||b2){
//匹配成功
return level;
}
}
return Level.NULL;
}
private String getStringByDire(ChessBean bean, int currentPlayer2, int x,int y) {
boolean res=false;
if(y>0||(y==0&&x<0))
{
//反向拼接
res=true;
}
int x_tmp=bean.getX();
int y_tmp=bean.getY();
String str="";
for(int i=0;i<5;i++){
x_tmp+=x;
y_tmp+=y;
if(x_tmp>=0&&x_tmp=0&&y_tmp=0&&x_tmp=0&&y_tmp0){
ChessBean chessBean=getLastBean();
chessBeans[chessBean.getX()][chessBean.getY()].setOrderNumber(0);
chessBeans[chessBean.getX()][chessBean.getY()].setPlayer(GoBangContantes.EMPTY);
count--;
repaint();
}
else{
JOptionPane.showMessageDialog(GoBangPanel.this, "请先下棋");
}
}
}
public void huiqi2() {
if (isGameOver) {
JOptionPane.showMessageDialog(GoBangPanel.this, "请先开始新游戏!");
} else {
if (count > 2) {
for (int i = 0; i < 2; i++) {
ChessBean tempBean =getLastBean();
currentPlayer = tempBean.getPlayer();
chessBeans[tempBean.getX()][tempBean.getY()].setPlayer(GoBangContantes.EMPTY); //
chessBeans[tempBean.getX()][tempBean.getY()].setOrderNumber(0);
count--;
repaint();
}
} else {
JOptionPane.showMessageDialog(GoBangPanel.this, "你还没下棋呢!");
}
}
}
// 棋型信息
public static enum Level {
CON_5("长连", 0, new String[] { "11111", "22222" }, 100000),
ALIVE_4("活四", 1, new String[] { "011110", "022220" }, 10000),
GO_4("冲四", 2, new String[] { "011112|0101110|0110110", "022221|0202220|0220220" }, 500),
DEAD_4("死四", 3, new String[] { "211112", "122221" }, -5),
ALIVE_3("活三", 4, new String[] { "01110|010110", "02220|020220" }, 200),
SLEEP_3("眠三", 5,
new String[] { "001112|010112|011012|10011|10101|2011102", "002221|020221|022021|20022|20202|1022201" },
50),
DEAD_3("死三", 6, new String[] { "21112", "12221" }, -5),
ALIVE_2("活二", 7, new String[] { "00110|01010|010010", "00220|02020|020020" }, 5),
SLEEP_2("眠二", 8,
new String[] { "000112|001012|010012|10001|2010102|2011002",
"000221|002021|020021|20002|1020201|1022001" },
3),
DEAD_2("死二", 9, new String[] { "2112", "1221" }, -5), NULL("null", 10, new String[] { "", "" }, 0);
private String name;
private int index;
private String[] regex;// 正则表达式
int score;// 分值
// 构造方法
private Level(String name, int index, String[] regex, int score) {
this.name = name;
this.index = index;
this.regex = regex;
this.score = score;
}
// 覆盖方法
@Override
public String toString() {
return this.name;
}
};
// 方向
private static enum Direction {
HENG, SHU, PIE, NA
};
// 位置分
private static int[][] position = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0 },
{ 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 6, 6, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 6, 6, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0 },
{ 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0 },
{ 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0 },
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
}
未经原作者允许,请勿转载!