设计一个人工智能俄罗斯方块,让其可自行进行游戏。
在手动基础上增加AI, 主要是运用了El-Tetris 算法的特征点。
// 在每一列中尝试放置当前图形
static void tryPlace(GamingArea gamingArea, Shape shape, Map<Double, Move> moveMap) {
for (int col = 0; col < gamingArea.getAreaWidth() - shape.getWidth() + 1; col++) {
int x = col;
int y = gamingArea.getDropHeight(shape, x);
Move move = new Move();
move.x = x;
move.y = y;
move.shape = shape;
gamingArea.undo();
int result = gamingArea.place(shape, x, y);
// 成功放置
if (result <= GamingArea.ROW_FULL) {
// 表示当前方块放置之后的高度。
int landingHeight = gamingArea.getLandingHeight(col, shape);
// 表示方块放置之后,消行层数与当前方块贡献出的方格数乘积
int rowsEliminated = gamingArea.getRowsEliminated(shape, x);
// 表示当前方块落下后被消减的行数。
int rowsTransition = gamingArea.getRowsTransition();
// 代表容器中水平方向上变换的次数。
int columnTransition = gamingArea.getColumnTransition();
// 代表方块放置之后,空洞的个数
int numOfHoles = gamingArea.getNumberOfHoles();
// 方块放置后,"井"深的"连加和" 每个结果都是累加
int wellSum = gamingArea.getWellSum();
double score = calcScore(landingHeight, rowsEliminated, rowsTransition, columnTransition, numOfHoles, wellSum);
move.score = score;
moveMap.put(score, move);
}
}
gamingArea.undo();
}
// 根据El-Tetris 算法的特征点,计算分数
static double calcScore(int landingHeight, int rowsEliminated, int rowsTransition, int columnTransition, int numOfHoles, int wellSum) {
return (-4.500158825082766 * landingHeight) +
(3.4181268101392694 * rowsEliminated) +
(-3.2178882868487753 * rowsTransition) +
(-9.348695305445199 * columnTransition) +
(-7.899265427351652 * numOfHoles) +
(-3.3855972247263626 * wellSum);
}
这是AI能运行的重要组成部分。
/*
AI
*/
// 放置之后的高度
public int getLandingHeight(int col, Shape shape) {
return getColumnHeight(col) - shape.getHeight() / 2;
}
// 放置后,消行层数与当前方块贡献出的方格数乘积
public int getRowsEliminated(Shape shape, int col) {
// 计算消除的行数
int rowsCleared = 0;
for (int row = 0; row < getMaxHeight(); row++) {
if (isFilledCol(row)) {
++rowsCleared;
}
}
// 当前方块贡献的方格数
int cout = 0;
for (int i = 0; i < shape.getPoints().length; i++) {
if (shape.getPoints()[i].y < rowsCleared) {
cout++;
}
}
return rowsCleared * cout;
}
// 方块放置后,水平向上方块的变换次数
public int getRowsTransition() {
int rowsTransition = 0;
for (int row = 0; row < height; row++) {
for (int col = -1; col < width; col++) {
if (isFilled(col, row) ^ isFilled(col + 1, row)) {
rowsTransition++;
}
}
}
return rowsTransition;
}
// 方块放置后,垂直向上方块的变换次数
public int getColumnTransition() {
int columnTransition = 0;
for (int col = 0; col < width; col++) {
for (int row = -1; row < height; row++) {
if (isFilled(col, row) ^ isFilled(col, row + 1)) {
columnTransition++;
}
}
}
return columnTransition;
}
// 空洞的个数
public int getNumberOfHoles() {
int numOfHoles = 0;
for (int col = 0; col < width; col++) {
boolean isHole = false;
int colH = getColumnHeight(col);
for (int row = -1; row < colH; row++) {
if (!isFilled(col, row)) {
isHole = true;
} else {
if (isHole) {
numOfHoles++;
}
isHole = false;
}
}
}
return numOfHoles;
}
// 井
public int getWellSum() {
List<Integer> wellsH = new ArrayList<>();
for (int col = 0; col < width; col++) {
int wellH = 0;
for (int row = 0; row < height; row++) {
if (!isFilled(col, row)) {
if (isFilled(col - 1, row) && isFilled(col + 1, row)) {
wellH++;
}
} else {
if (wellH > 0) {
wellsH.add(wellH);
wellH = 0;
}
}
}
}
int wellSum = 0;
for (int h : wellsH) {
for (int i = 1; i <= h; i++) {
wellSum += i;
}
}
return wellSum;
}
计算游戏界面砖块如何放置的算法。
还有利用测试尽量找出程序BUG
例如 这是在测试图形的宽度是否符合我们心中的预期
Test
public void geHightTest(){
assertEquals(4,i.getHeight());
assertEquals(3,l.getHeight());
assertEquals(3,j.getHeight());
assertEquals(2,s.getHeight());
assertEquals(2,z.getHeight());
assertEquals(2,o.getHeight());
assertEquals(2,t.getHeight());
assertEquals(1,shape_1.getHeight());
assertEquals(2,shape_2.getHeight());
assertEquals(2,shape_3.getHeight());
assertEquals(3,shape_5.getHeight());
assertEquals(4,shape_6.getHeight());
}
原文:https://www.cnblogs.com/Ga7l/p/14496056.html