1.最近写了个棋盘游戏,大致实现如下功能:棋盘设定(n子棋,m*m棋盘)、重玩、后退(悔棋)、前进、保存、加载保存的棋盘状态。
2.先上最终效果图:
3.思路:第一步先画棋盘
int width = getWidth(); int height = getHeight(); mChildWidth = width / Max; mChildHeight = height / Max; //竖 for (int i = 1; i < Max; i++) { canvas.drawLine(mChildWidth * i, 0, mChildWidth * i, height, mLinePaint); } //横 for (int i = 1; i < Max; i++) { canvas.drawLine(0, mChildHeight * i, width, mChildHeight * i, mLinePaint); }第二步,画黑白棋子,此时必须监听onTouch事件,将棋手所点的坐标转化为二维数组(比如第一行第一列即为0,0位置。。。以此类推)
/** * 获取下棋子的位置(数组下标) * * @param event */ private Point getPoint(MotionEvent event) { float fx = event.getX(); float fy = event.getY(); int x = 0, y = 0; for (int i = 0; i < Max; i++) { if (fx < mChildWidth * (i + 1)) { x = i; break; } } for (int i = 0; i < Max; i++) { if (fy < mChildHeight * (i + 1)) { y = i; break; } } Point point = new Point(x, y); return point; }
/** * 获取下棋子的位置(坐标) * * @param point */ private Position getPosition(Point point) { float x = 0, y = 0; for (int i = 0; i < Max; i++) { if (point.getX() == i) { x = i * mChildWidth + mChildWidth / 2; } } for (int i = 0; i < Max; i++) { if (point.getY() == i) { y = i * mChildHeight + mChildHeight / 2; } } Position position = null; if (mChessPieces == -1) { position = new Position(x, y, Color.BLACK, mChessPieces, point); } else { position = new Position(x, y, Color.WHITE, mChessPieces, point); } return position; }
public class Point implements Serializable { public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public Point setX(int x) { this.x = x; return this; } public int getY() { return y; } public Point setY(int y) { this.y = y; return this; } // @Override // public int hashCode() { // return x + y; // } @Override public boolean equals(Object obj) { if (this == obj) return true; Point other = (Point) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } }
@Override public boolean onTouchEvent(MotionEvent event) { if (canTouch && event.getAction() == MotionEvent.ACTION_DOWN) { mPositionUndoList.clear();//一旦重新下棋子,不能redo Point point = getPoint(event); if (mScore[point.getY()][point.getX()].getScore() == 0) { Position position = getPosition(point); mPositionList.add(position); mScore[point.getY()][point.getX()] = position; if (mChessPieces == -1) { mBlackPositionList.add(position); calculator(mBlackPositionList, "黑子"); } else if (mChessPieces == 1) { mWhitePositionList.add(position); calculator(mWhitePositionList, "白子"); } postInvalidate(); exchangeChess(); event.setAction(MotionEvent.ACTION_CANCEL); } else { //有棋子 onResultListener.onError("当前位置有棋子,换个位置!"); } } return super.onTouchEvent(event); } /** * 换棋手 */ private void exchangeChess() { if (mChessPieces == -1) { mChessPieces = 1; mCirclePaint.setColor(Color.WHITE); if (onCurrentColorListener != null) onCurrentColorListener.onCurrentColor("白子"); } else { mChessPieces = -1; mCirclePaint.setColor(Color.BLACK); if (onCurrentColorListener != null) onCurrentColorListener.onCurrentColor("黑子"); } }
第三步,也就是本游戏的核心,计算输赢的算法。基本思想就是每次棋手下棋子后,就去计算横、竖、斜杠、反斜杠四个方向同一颜色的棋子的个数是否到达n子棋,若到达,即为相应的棋手方获胜,反之继续,直至棋子总数==棋盘m*m数时,平手。
/** * 计算输赢 */ private void calculator(List<Position> myChessPosition, String color) { mSuccessPointList.clear(); if (myChessPosition.size() < COUNT) { return; } if (color.equals("黑子")) { mColor = Color.BLACK; } else { mColor = Color.WHITE; } List<Point> myPointList = new ArrayList<>(); Point temPoint = new Point(0, 0); for (int i = 0; i < myChessPosition.size(); i++) { myPointList.add(myChessPosition.get(i).getPoint()); } int count = 0; Point point = myPointList.get(myPointList.size() - 1);//获取最后下的棋子的位置 int x = point.getX(); int y = point.getY(); temPoint.setX(x).setY(y); //横 while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempX = temPoint.getX(); if (!(tempX < Max) || count >= COUNT) { break; } temPoint.setX(tempX + 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } temPoint.setX(x).setY(y); mSuccessPointList.remove(0); count--;//重复算了x,y这点 while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempX = temPoint.getX(); if (tempX < 1 || count >= COUNT) { break; } temPoint.setX(tempX - 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } //竖 mSuccessPointList.clear(); count = 0; temPoint.setX(x).setY(y); while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempY = temPoint.getY(); Log.d("横+", "count=" + count + ",tempY=" + tempY); if (!(tempY < Max) || count >= COUNT) { break; } temPoint.setY(tempY + 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } mSuccessPointList.remove(0); temPoint.setX(x).setY(y); count--;//重复算了x,y这点 while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempY = temPoint.getY(); Log.d("横-", "count=" + count + ",tempY=" + tempY); if (tempY < 1 || count >= COUNT) { break; } temPoint.setY(tempY - 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } //正斜向 / mSuccessPointList.clear(); count = 0; temPoint.setX(x).setY(y); while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempX = temPoint.getX(); int tempY = temPoint.getY(); Log.d("横+", "count=" + count + ",tempY=" + tempY); if (tempY < 1 || !(tempX < Max) || count >= COUNT) { break; } temPoint.setX(tempX + 1).setY(tempY - 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } mSuccessPointList.remove(0); temPoint.setX(x).setY(y); count--; while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempX = temPoint.getX(); int tempY = temPoint.getY(); Log.d("横+", "count=" + count + ",tempY=" + tempY); if (tempX < 1 || !(tempY < Max) || count >= COUNT) { break; } temPoint.setX(tempX - 1).setY(tempY + 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } //反斜向 \ mSuccessPointList.clear(); count = 0; temPoint.setX(x).setY(y); while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempX = temPoint.getX(); int tempY = temPoint.getY(); Log.d("横+", "count=" + count + ",tempY=" + tempY); if (!(tempY < Max) || !(tempX < Max) || count >= COUNT) { break; } temPoint.setX(tempX + 1).setY(tempY + 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } mSuccessPointList.remove(0); temPoint.setX(x).setY(y); count--; while (myPointList.contains(temPoint)) { mSuccessPointList.add(new Point(temPoint.getX(), temPoint.getY())); count++; int tempX = temPoint.getX(); int tempY = temPoint.getY(); Log.d("横+", "count=" + count + ",tempY=" + tempY); if (tempX < 1 || tempY < 1 || count >= COUNT) { break; } temPoint.setX(tempX - 1).setY(tempY - 1); } if (count == COUNT) { resultMsg = "本局结束," + color + "胜!!!"; new Thread(new CircleThread()).start(); return; } if (mPositionList.size() == Max * Max) { resultMsg = "本局结束,平局!!!"; mSuccessPointList.clear(); onResultListener.onComplete(resultMsg); } }
第四步,就是判断胜负出结果后,棋子闪动动画,这里用的是改变圆半径,通过线程控制重绘次数的方法。
private Handler circleHandler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { canTouch = false; mCircleSize = (Integer) msg.obj;//圆半径 invalidate();//重新绘制 } else if (msg.what == 2) { onResultListener.onComplete(resultMsg); } } };
private class CircleThread implements Runnable { @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(200); m++; Message msg = new Message(); if (m > 6) { msg.what = 2; circleHandler.sendMessage(msg); return; } msg.what = 1; if (m % 2 == 0) { msg.obj = 45; } else { msg.obj = 40; } circleHandler.sendMessage(msg); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
private ArrayList<Position> mPositionList = new ArrayList<>();//棋盘共下的子 private ArrayList<Position> mBlackPositionList = new ArrayList<>();//棋盘下的黑子 private ArrayList<Position> mWhitePositionList = new ArrayList<>();//棋盘共下的白子 private ArrayList<Position> mPositionUndoList = new ArrayList<>();//存储悔棋的步骤 // private ArrayList<Position> mSuccessPositionList = new ArrayList<>(); private ArrayList<Point> mSuccessPointList = new ArrayList<>();
上面就是为了实现如上功能所设置的List,虽然有点多。。。
第六步,保存棋盘及加载棋盘状态,利用就是将序列化后的相关数据保存到外存,当需要时再由外存读入。重绘保存的数据信息,即可重现保存棋盘的模样。
/** * 保存棋盘 */ public void save() { if (mPositionList.size() == 0) { return; } Map<String, Object> map = new HashMap<>(); map.put("mScore", mScore); map.put("mPositionList", mPositionList); map.put("mBlackPositionList", mBlackPositionList); map.put("mWhitePositionList", mWhitePositionList); File filePath = new File(BaseApplication.instance.getCacheDir(), "gameTTT"); FileSaveHandler.saveObject(filePath.getPath(), map); reset(); } /** * 加载保存的棋盘 */ public void load() { File filePath = new File(BaseApplication.instance.getCacheDir(), "gameTTT"); Map<String, Object> map = (Map<String, Object>) FileSaveHandler.readObject(filePath.getPath()); mScore = (Position[][]) map.get("mScore"); mPositionList = (ArrayList<Position>) map.get("mPositionList"); mBlackPositionList = (ArrayList<Position>) map.get("mBlackPositionList"); mWhitePositionList = (ArrayList<Position>) map.get("mWhitePositionList"); if (mPositionList.size() == 0) { return; } Position position = mPositionList.get(mPositionList.size() - 1); if (position.getColor() == Color.WHITE) { calculator(mWhitePositionList, "白子"); } else { calculator(mBlackPositionList, "黑子"); } postInvalidate(); }
最后,已上传代码至github。https://github.com/Selina592732122/GameTTT