Android:实现井字棋小游戏

 

井字棋实现方式有许多,最简单的方法是将9个imagView组成棋盘,然后通过一些逻辑设计进行游戏。本文采用的是自定义View的方式进行游戏设计,通过继承View进行棋盘,选中状态绘制,效果如下:

 

1.棋盘绘制

  通过选取设置宽高中最小值作为控件宽高,并平均分为3段作为每小格的长度,在onDraw()方法中绘制棋盘,代码如下:

//重写onMeasure()设置宽高
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int size = Math.min(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.getSize(heightMeasureSpec));setMeasuredDimension(size,size);}//棋盘画笔
private Paint mPaint;@Overrideprotected void onDraw(Canvas canvas) {//每小格长度length = getWidth()/3;//棋盘绘制for(int i = 0;i < 4;i++){canvas.drawLine(length*i,0,length*i,3*length,mPaint);canvas.drawLine(0,length*i,length*3,length*i,mPaint);}
}

 

 2.选中状态绘制

  为了区分选中操作的玩家,新建枚举类型:

//代表不同玩家(NONE用来表示平局情况获胜玩家)public enum Player{USER_ONE,USER_TWO,NONE}

 当玩家点击屏幕时,我们需要知道他所选的格子是哪一个,通过实现 View.OnTouchListener 接口进行监听,代码如下(其中的判断是避免滑动情况):

 // down 事件 坐标private float lastX;private float lastY;@Overridepublic boolean onTouch(View v, MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:lastX = x;lastY = y;break;case MotionEvent.ACTION_UP:// UP 事件坐标 与 Down事件坐标 距离不能太远if(Math.abs(lastX-x)

 其中calculateTouchItem方法功能为计算选中格子,代码如下:

//被选格子(玩家选择前先通过此数组判断是否可选,选择后将对应格子值设为非0)
private int[] locations = new int[9];
//每个格子对应字母编号,将通过字母组合判断是否获胜
private String[] items = new String[9];
//玩家所选格子对应字母集合
private List user1Selected = new ArrayList<>();
private List user2Selected = new ArrayList<>();
//已选格子信息集合(格子所选玩家,格子中心点坐标)
private List mList = new ArrayList<>();//当前回合玩家
private Player currentPlayer;private void calculateTouchItem(float x, float y) {//判断所在行列int j = (int)x/length;int i = (int)y/length;//判断是否在棋盘内if(i < 3 && j < 3){//判断是否可选if(locations[i*3+j] == 0){//创建选中格子信息并添加到选中格子list中ItemInformation itemInformation = new ItemInformation(items[i*3+j],currentPlayer,i,j);mList.add(itemInformation);//将格子设为已选状态locations[i*3+j] = 1;//将选中格子字母编号添加到当前玩家选中格子list中if(currentPlayer == Player.USER_ONE){user1Selected.add(items[i*3+j]);}else {user2Selected.add(items[i*3+j]);}//重绘invalidate();}}}

其中ItemInformation保存选中格子相关信息,具体如下:

//存储选中格子信息private class ItemInformation{private int i;private int j;//选中格子字母编号private String location;private Player player;public ItemInformation(String location, Player player,int i,int j) {this.location = location;this.player = player;this.i = i;this.j = j;}public String getLocation() {return location;}public Player getPlayer() {return player;}public int getI() {return i;}public int getJ() {return j;}}

 九个格子字母编号和数字编号如下:

 A(0)B(1)C(2)
D(3)E(4)F(5)
G(6)H(7)I(8)

计算好选中格子后调用invalidate()方法进行重绘,修改onDraw()方法,代码如下:

//是否第一次加载
private boolean isFirst = true;//玩家图案对应颜色
private int userColorOne;
private int userColorTwo;@Overrideprotected void onDraw(Canvas canvas) {length = Math.min(getWidth(),getHeight())/3;//棋盘绘制for(int i = 0;i < 4;i++){canvas.drawLine(length*i,0,length*i,3*length,mPaint);canvas.drawLine(0,length*i,length*3,length*i,mPaint);}//选中格子绘制if(!isFirst){for(int i = 0;i < mList.size();i++){switch (mList.get(i).getPlayer()){case USER_ONE:userPaint.setColor(userColorOne);drawUserSelected(canvas,mList.get(i));break;case USER_TWO:userPaint.setColor(userColorTwo);drawUserSelected(canvas,mList.get(i));break;}}//查看游戏状态是否结束checkStatus();}if(isFirst){isFirst = false;}}

 遍历所有选中格子,通过对应ItemInformation信息知道所选玩家,设置对应颜色,再调用drawUserSelected()方法绘制,具体代码如下:

//玩家选择图案画笔
private Paint userPaint; //绘制选中格子private void drawUserSelected(Canvas canvas, ItemInformation itemInformation) {//计算格子中心坐标int centerX = itemInformation.getJ() * length + length/2;int centerY = itemInformation.getI() * length + length/2;userPaint.setStrokeWidth(5f);//玩家一 绘制 × 图案if(itemInformation.getPlayer() == Player.USER_ONE){float delta = (float) Math.sqrt(0.08*length*length);canvas.drawLine(centerX-delta,centerY-delta,centerX+delta,centerY+delta,userPaint);canvas.drawLine(centerX+delta,centerY-delta,centerX-delta,centerY+delta,userPaint);}else {//玩家二 绘制 ○ 图案float radius =  0.4f * length;canvas.drawCircle(centerX,centerY,radius,userPaint);}}

 绘制后就调用checkStatus()方法进行判断,是否有玩家获胜,是否还有未选格子,如游戏还能继续则切换玩家。具体代码如下:

//查看游戏状态private void checkStatus(){// 避免非用户点击情况下 onDraw()方法调用 造成 当前玩家的切换if(mList.size() == 0 || mList.size()==lastCount) return;lastCount = mList.size();//查看是否有人获胜boolean isSuccess = checkIsSuccessful();if(isSuccess){if(mListener != null){mListener.onSuccess(currentPlayer);}setOnTouchListener(null);}else {//判断是否平局if(mList.size() == 9){if(mListener != null){mListener.onSuccess(Player.NONE);}return;}//切换当前用户switch (currentPlayer){case USER_TWO:currentPlayer = Player.USER_ONE;break;case USER_ONE:currentPlayer = Player.USER_TWO;break;}}Log.e("Chess:",currentPlayer+"");}

  其中checkIsSuccessful()方法判断有人获胜,具体代码如下:

private boolean checkIsSuccessful() {boolean isSuccess = false;String tmp = "";if(currentPlayer == Player.USER_ONE){if(user1Selected.size() >= 3){//将玩家所选格子字母编号排序Collections.sort(user1Selected);//回溯法判断是否获胜searchResult(user1Selected,tmp,0);isSuccess = result.size() > 0;}}else {if(user2Selected.size() >= 3){Collections.sort(user2Selected);searchResult(user2Selected,tmp,0);isSuccess = result.size() > 0;}}return isSuccess;}

 通过调用searchResult()方法进行结果查找,具体代码如下:

//获胜的所有格子字母组合(按字典序排序)
private List successResult = new ArrayList<>();
//存储玩家获胜的字母组合
private List result = new ArrayList<>();//回溯法 将所有情况进行判断private void searchResult(List userSelected,String tmp,int index) {if(tmp.length() == 3){System.out.println(tmp);if(successResult.contains(tmp)){result.add(tmp);}return;}for(int i = index;i < userSelected.size();i++){tmp += userSelected.get(i);searchResult(userSelected,tmp,i+1);tmp = tmp.substring(0,tmp.length()-1);}}

 其中successResult存储了所有获胜情况下的字母组合(按字典序),在checkIsSuccessful()方法中先调用Collections.sort()将玩家所选格子字母编号进行排序,在通过回溯法将所有3个字母组合与successResult结果进行比较,如果存在即获胜,将结果加入result中,在checkIsSuccessful()方法中可通过result长度可知是否获胜。如果有人获胜则回调游戏结束接口,接口如下:

//游戏结束监听器private OnSuccessListener mListener;//游戏结束回调接口public interface OnSuccessListener{public void onSuccess(Player player);}//设置游戏结束回调接口public void setOnSuccessListener(OnSuccessListener listener){mListener = listener;}

  回调代码在checkStatus()方法,如下:

 //查看游戏状态private void checkStatus(){// 避免非用户点击情况下 onDraw()方法调用 造成 当前玩家的切换if(mList.size() == 0 || mList.size()==lastCount) return;lastCount = mList.size();//查看是否有人获胜boolean isSuccess = checkIsSuccessful();if(isSuccess){if(mListener != null){mListener.onSuccess(currentPlayer);}setOnTouchListener(null);}else {//判断是否平局if(mList.size() == 9){if(mListener != null){mListener.onSuccess(Player.NONE);}return;}//切换当前用户switch (currentPlayer){case USER_TWO:currentPlayer = Player.USER_ONE;break;case USER_ONE:currentPlayer = Player.USER_TWO;break;}}Log.e("Chess:",currentPlayer+"");}

到此我们的自定义view也完成了,上面代码中的变量在构造函数中,进行初始化,其中部分属性可设为自定义属性供用户设置,自定义属性及变量初始化如下:

    public ChessView(Context context) {this(context,null);}public ChessView(Context context, @Nullable AttributeSet attrs) {this(context, attrs,0);}public ChessView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context,attrs);}private void init(Context context,AttributeSet attributeSet) {initData();TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.ChessView);lineColor = typedArray.getColor(R.styleable.ChessView_lineColor,Color.BLACK);userColorOne = typedArray.getColor(R.styleable.ChessView_user_color_one,Color.BLACK);userColorTwo = typedArray.getColor(R.styleable.ChessView_user_color_two,Color.RED);typedArray.recycle();userPaint = new Paint();userPaint.setStyle(Paint.Style.STROKE);userPaint.setAntiAlias(true);mPaint = new Paint();mPaint.setColor(lineColor);mPaint.setStyle(Paint.Style.STROKE);mPaint.setAntiAlias(true);currentPlayer = Player.USER_ONE;}private void initData(){items[0] = "A";items[1] = "B";items[2] = "C";items[3] = "D";items[4] = "E";items[5] = "F";items[6] = "G";items[7] = "H";items[8] = "I";successResult.add("ABC");successResult.add("DEF");successResult.add("GHI");successResult.add("ADG");successResult.add("BEH");successResult.add("CFI");successResult.add("AEI");successResult.add("CEG");}

  我们可在布局中使用此自定义view并实现游戏结束接口,具体就不介绍了,代码都有注释,布局代码和activity代码如下:


public class ChessActivity extends AppCompatActivity implements View.OnClickListener {private ChessView chessView;private Button chessButton;//游戏结束信息提醒private AlertDialog.Builder alertDialog;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_chess);initDialog();//判断是否为第一次进入游戏 是的话需点击开始游戏,否则直接进行游戏Intent intent = getIntent();chessButton = findViewById(R.id.chess_button);chessView = findViewById(R.id.chess_view);if(intent.getIntExtra("re",0) == 1){chessButton.setVisibility(View.INVISIBLE);//使棋盘接收点击事件chessView.setOnTouchListener();}else {chessButton.setText("开始游戏");chessButton.setOnClickListener(this);}chessView.setOnSuccessListener(new ChessView.OnSuccessListener() {@Overridepublic void onSuccess(ChessView.Player player) {//根据回调结果显示结束信息switch (player){case NONE:alertDialog.setMessage("平局!");  //设置提示信息break;case USER_ONE:alertDialog.setMessage("×方获胜!");  //设置提示信息break;case USER_TWO:alertDialog.setMessage("○方获胜!");  //设置提示信息break;}alertDialog.show(); //显示}});}//初始化Dialogprivate void initDialog(){alertDialog = new AlertDialog.Builder(this);alertDialog.setTitle("游戏信息");  //设置标题alertDialog.setCancelable(false);   //是否点击屏幕可取消//确定按钮点击事件alertDialog.setPositiveButton("再来一局", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {Intent intent = new Intent(ChessActivity.this,ChessActivity.class);intent.putExtra("re",1);startActivity(intent);finish();}});//取消按钮点击事件alertDialog.setNegativeButton("返回", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {chessButton.setVisibility(View.VISIBLE);chessButton.setText("再来一局");chessButton.setOnClickListener(ChessActivity.this);}});}@Overridepublic void onClick(View v) {if (v.getId() == R.id.chess_button) {if (chessButton.getText().toString().equals("开始游戏")) {Log.e("ChessActivity:","开始游戏");chessView.setOnTouchListener();} else {Intent intent = new Intent(ChessActivity.this, ChessActivity.class);intent.putExtra("re", 1);startActivity(intent);finish();}}}
}

 代码已上传GitHub,地址 : https://github.com/YangRT/Chess


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部