JAVA实现的俄罗斯方块的简单AI

寒假里初步自学了Java。该项目是一个简单的自我检验。编写代码的时候发现游戏实现起来不算太难,所以增加了一些难度让程序能够自己玩俄罗斯方块。

游戏部分

这里部分实现起来实际上并不复杂,实现的时候尽力遵循了MVC模型。

handler
handler
TetrisMove类 监听键盘输入
Game类
Lattices类
Draw类

WDSA控制图形,handler则相当于他们对应的处理码。这里handler作为Game类的一个静态变量存在,这样做的好处是当获取到键盘信息时TetrisMove类可以立即直接改变handler的值。
Game类定时将handler传递给Lattice类,该类储存了格点数组,包含了用处理其变化的函数。获得handler值后,会调用相应的函数,完成移动,变化,加速动作。
实际上还有Pattern类,其代码如下:

package Pattern;
/**
 * 有关坐标系的问题,向下向右为正
 */
public class Pattern {
	public boolean[][] mat = new boolean[4][4];
	public int top,buttom,left,right;
	public int type;
	public int currtype = 0;
	public Pattern(boolean[][] mat, int type) {
		super();
		this.mat = mat;
		this.type = type;
		calRegion();
	}
	private void calRegion() {
		top = 3;
		left = 3;
		right = 0;
		buttom = 0;
		for(int i = 0;i < 4; i++) {
			for(int j = 0;j < 4; j++) {
				if(mat[i][j]) {
					if(top>i) {
						top = i;
					}
					if(left>j) {
						left = j;
					}
					if(buttom<i) {
						buttom = i;
					}
					if(right<j) {
						right = j; 
					}
				}
			}
		}
	}
	public void print_info() {
		System.out.println("top = " +top);
		System.out.println("left = " +left);
		System.out.println("buttom = " +buttom);
		System.out.println("right = " +right);
		System.out.println("currtype = " +currtype);
	}
	public void rotate() {
		boolean[][] tem  = new boolean[4][4];
		for(int i = 0;i < 4; i++) {
			for(int j = 0;j < 4; j++) {
				tem[3-j][i] = mat[i][j];
			}
		}
		mat = tem;
		currtype = (currtype + 1)%type;
		calRegion();
		
	}
	public void derotate() {
		boolean[][] tem  = new boolean[4][4];
		for(int i = 0;i < 4; i++) {
			for(int j = 0;j < 4; j++) {
				tem[j][3-i] = mat[i][j];
			}
		}
		mat = tem;
		currtype = (type+(currtype - 1))%type;
		calRegion();	
	}
}

在该类中规定了每个图案的默认大小为4x4,用top,right,buttom,left控制其有效区域,type这个参数则是为了之后AI类使用而保留的。
图案的变化在代码中是利用旋转实现的即
tem[3-j][i] = mat[i][j];tem[j][3-i] = mat[i][j];

AI部分

设计AI的时候,首先想到的方案是图案出现时先进行左右平移,然后下落,在所有结果中找出最优的方案。因为俄罗斯方块的游戏难度实际上不大,该方法的时间复杂度只与格点数组的宽度相关,所以直接就选择了该方法。
(实际上该方法并不能一定找到最优的方案,因为有些区域需要先下落一段距离再左右平移才可以到达,但是考虑到只是实现一个简单的AI,让游戏不死即可,我就没有再做进一步的优化)
然后的问题是怎么在所有结果中找最后解,一个朴素的方法就是计分。利用格点数组对一些方面进行计分,选择分数最高或者最低的结果。
我根据自己对这个游戏的理解,得出以下结论:

  1. 方块数量应该越少越好
  2. 方块位置应该越低越好
  3. 应该避免出现被方块封闭的空格

根据以上三点,定义了三组变量:

  1. private int Scoreblock = 1000;//每一个空格的分数
  2. private int[] ScoreRow ; //方块在某一行的分数
  3. private int ScoreSide = 100;//空格左边是方块时的分数private int ScoreUP = 5000;//空格上边是方块时的分数

分数的值需要进行尝试。
下面是AImove的完整代码:

package Game;

import java.util.ArrayList;
import Pattern.TetrisLattice;

public class TetrisAI {
	private int width;
	private int height;
	private TetrisLattice AILattice;
	private int[] ScoreRow ; //方块在某一行的分数
	private int ScoreSide = 100;//空格左边是方块时的分数
	private int ScoreUP = 5000;//空格上边是方块时的分数
	private int Scoreblock = 1000;//每一个空格的分数
	public ArrayList<Integer> HandlerList = new ArrayList<Integer>();
	public TetrisAI(int width, int height) {
		super();
		this.width = width;
		this.height = height;
		ScoreRow = new int[height];
		setScoreRow();
	}
	public void test_print(int a) {
		System.out.println(a);
	}
	public void calcWay(TetrisLattice lattices) {
		HandlerList.clear();
		int minScore = 0x7FFFFFFF;
		int handler = 0;
		int k = 0;
		int typetimes = 0;
		for(int i= -width+1;i<width;i++) {
			AILattice = lattices.copy();
			int type = AILattice.getPatternType();
			for(int t = 0;t<type;t++) {
				k=i;
				AILattice = lattices.copy();
				for(int kt = 0;kt<t;kt++) {
					AILattice.Handler(0);
				}
				while(k!=0){
					if(k<0) {
						AILattice.Handler(3);
						k++;
					}
					else {
						AILattice.Handler(1);
						
						k--;
					}
				}
				while(!AILattice.blockFall());
				int Score = calcScore();
				
				if(minScore>Score) {
					minScore = Score;
					handler = i;
					typetimes = t;
				}
			}
		}
		
		for(int i=0;i<typetimes;i++) {
			HandlerList.add(0);
		}
		if(handler<0) {
			HandlerList.add(3);
		}
		else if(handler>0) {
			HandlerList.add(1);
		}

	}
	private int calcScore() {
		int score = 0;
		for(int i=0;i<height;i++) {
			for(int j=0;j<width;j++) {
				if(AILattice.Lattices[i][j]==2) {
					score+= Scoreblock-ScoreRow[i];
				}
				else if(AILattice.Lattices[i][j]==0) {
					score += getsurroundScore(i, j);
				}
			}
		}
		return score;
	}
	private void setScoreRow() {
		//ScoreRow数组的循序与Lattice的循序一致
		for(int i=0;i<height;i++) {
			ScoreRow[i] = i*10;
		}
	}
	private int getsurroundScore(int y,int x) {
		//空格周围的分数
		int score = 0;
		if(x==0) {
			score += ScoreSide;
			if(AILattice.Lattices[y][x+1]==2) {
				score += ScoreSide;
			}
			
		}
		else if(x==(width-1)) {
			score += ScoreSide;
			if(AILattice.Lattices[y][x-1]==2) {
				score += ScoreSide;
			}
		}
		else {
			if(AILattice.Lattices[y][x-1]==2) {
				score += ScoreSide;
			}
			if(AILattice.Lattices[y][x+1]==2) {
				score += ScoreSide;
			}
		}
		
		if(y!=0&&AILattice.Lattices[y-1][x]==2) {
			score += ScoreUP;
		}
		
		return score;
	}
	
}

代码

GitHub

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值