android 自定义五子棋

来源:互联网 发布:最小公倍数的算法 编辑:程序博客网 时间:2024/05/17 08:18

中午休息的时候,坐我旁边的同事喜欢玩五子棋,玩的很入迷。作为吃瓜群众的我,偶尔看他玩一两局。于是乎这两天想着实现一个简易的五子棋小游戏。本文重点是图形绘制。
首先看效果图:
这里写图片描述
下面讲解下思路:
1.绘制棋盘区域
针对手机这种宽度比长度小的,默认取宽度的1/11为棋盘的单位长度,然后根据在onSizeChanged()中获取到测量宽高尺寸getMeasuredWidth()和getMeasuredHeight(),然后在onDraw()中进行画线。

@Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        width = getMeasuredWidth();        height = getMeasuredHeight();        itemW = width / 11;        row = height / itemW;        column = width / itemW;        coos = new Point[row][column];        //確定坐标点        for (int i = 0; i < row; i++) {            for (int j = 0; j < column; j++) {                coos[i][j] = new Point(j * itemW, i * itemW);            }        }        //新建 Bitmap 对象,用来双缓冲        if (bitmapBuffer == null)            bitmapBuffer = Bitmap.createBitmap(width, height,                    Bitmap.Config.ARGB_8888);        bitmapCanvas = new Canvas(bitmapBuffer);    }
  @Override    protected void onDraw(Canvas canvas) {        //先画线        for (int i = 0; i < row; i++) {//水平线            canvas.drawLine(0, itemW * i, width, itemW * i, paintLine);            //确定每个点的坐标        }        for (int j = 0; j < column; j++) {//竖直线            canvas.drawLine(itemW * j, 0, itemW * j, height, paintLine);        }    }

2.绘制棋子
棋子有两种,即红黑(别人家的都是黑白,嘿嘿),可以找美工要俩棋子的图片,然后往canvas上绘制图片,也可以自己绘制,这样减少apk的资源包占用。绘制过程中,需要做渐变的处理,系统提供了很多种渐变的支持,我们用RadialGradient渐变。
绘制黑棋过程:

Paint paint = new Paint();paint.setColor(Color.BLACK);                paint.setShader(new RadialGradient(currPoint.x, currPoint.y, itemW / 3, Color.WHITE, Color.BLACK , Shader.TileMode.CLAMP));bitmapCanvas.drawCircle(currPoint.x, currPoint.y, itemW / 3, paint);//画棋子if (currPoint != null) {   canvas.drawBitmap(bitmapBuffer, 0, 0, null);}
/**     * 根据点击的位置确定坐标     *     * @param pressX     * @param pressY     */    private Point measureCoo(float pressX, float pressY) {        int x = (int) (pressX / itemW + 0.5f);        int y = (int) (pressY / itemW + 0.5f);        Log.d(TAG, "y=" + y + "  x=" + x + " column=" + column);        if (y >= row || x >= column) return null;        return coos[y][x];    }

应用了双缓冲机制,目的是保留上次绘制的状态,防止下次绘制清空上次的绘制。
3.监听用户交互事件
重写onTouchEvent(MotionEvent event)事件,监听手指抬起的动作,然后向目标位置绘制五子棋。

@Override    public boolean onTouchEvent(MotionEvent event) {        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_UP:                if (!isEditAble) {                    Toast.makeText(context, "重新开局!", Toast.LENGTH_LONG).show();                    resetData();                    invalidate();                    return true;                }                float pressX = event.getX();                float pressY = event.getY();                currPoint = measureCoo(pressX, pressY);                if (currPoint == null || blackPoints.contains(currPoint) || redPoints.contains(currPoint))                    return true;                if (type == Type.TYPE_BLACK) blackPoints.add(currPoint);//保存到记录中,用于计算输赢                else redPoints.add(currPoint);                paint.setColor(type == Type.TYPE_BLACK ? Color.BLACK : Color.RED);                paint.setShader(new RadialGradient(currPoint.x, currPoint.y, itemW / 3, Color.WHITE, type == Type.TYPE_BLACK ? Color.BLACK : Color.RED, Shader.TileMode.CLAMP));                if (calculateVictory(type)) { //如果胜了,直接弹出toast                    isEditAble = false;//胜利了,本局结束                    paint.setStyle(Paint.Style.STROKE);                    paint.setPathEffect(new DashPathEffect(new float[]{2, 2}, 1));                    Toast.makeText(context, type == Type.TYPE_BLACK ? "黑棋胜利了" : "红旗胜利了", Toast.LENGTH_LONG).show();                }                bitmapCanvas.drawCircle(currPoint.x, currPoint.y, itemW / 3, paint);                invalidate();                paint.setStyle(Paint.Style.FILL);                paint.setPathEffect(null);                type = Type.change(type);                break;        }        return true;    }

4.处理判断输赢的算法。
上面代码中calculateVictory()为处理判断输赢,目前想到了这样一个时间复杂度为O(N*k)的算法。但是感觉还是不够完善。如果有更好的建议希望能提出,在此先谢过。

private boolean calculateVictory(Type type) {        if (type == Type.TYPE_BLACK) {            if (blackPoints.size() < 5) return false;            if (horizonOrVerticalOk(blackPoints) || gradientOk(blackPoints)) return true;        } else {            if (redPoints.size() < 5) return false;            if (horizonOrVerticalOk(redPoints) || gradientOk(redPoints)) return true;        }        return false;    }    List<Integer> xlistY = new ArrayList<>();    List<Integer> ylistX = new ArrayList<>();    /**     * 水平方向满足连续5个则获胜     *     * @param points     * @return     */    private boolean horizonOrVerticalOk(List<Point> points) {        int xCount = 1, yCount = 1;        xlistY.clear();        ylistX.clear();        Point currPoint = points.get(points.size() - 1);//获取当前棋子        int currX = currPoint.x;        int currY = currPoint.y;        xlistY.add(currY);        ylistX.add(currX);        for (int j = 0; j < points.size() - 1; j++) {            if (currX == points.get(j).x) {                xlistY.add(points.get(j).y);                xCount++;            }            if (currY == points.get(j).y) {                ylistX.add(points.get(j).x);                yCount++;            }        }        if (xCount >= 5) {            for (int m = 0; m < xlistY.size(); m++) {//由于不确定最后一个棋子所处的位置,所以需要遍历                int targetY = xlistY.get(m);                int pos = targetY / itemW;                try {                    if (xlistY.contains((pos + 1) * itemW) && xlistY.contains((pos + 2) * itemW) && xlistY.contains((pos + 3) * itemW) && xlistY.contains((pos + 4) * itemW))                        return true;                } catch (Exception e) {                    e.printStackTrace();                }            }        }        if (yCount >= 5) {            for (int n = 0; n < ylistX.size(); n++) {                int xVal = ylistX.get(n);                int pos = xVal / itemW;                try {                    if (ylistX.contains((pos + 1) * itemW) && ylistX.contains((pos + 2) * itemW) && ylistX.contains((pos + 3) * itemW) && ylistX.contains((pos + 4) * itemW))                        return true;                } catch (Exception e) {                    e.printStackTrace();                }            }        }        return false;    }    /**     * 倾斜方向上满足连续5个则获胜     *     * @param points     * @return     */    private boolean gradientOk(List<Point> points) {        int xCount = 1, yCount = 1;        xlistY.clear();        ylistX.clear();        Point currPoint = points.get(points.size() - 1);//获取当前棋子        int currX = currPoint.x;        int currY = currPoint.y;        xlistY.add(currY);        ylistX.add(currX);        for (int j = 0; j < points.size()-1; j++) {            if ((points.get(j).x - currX) == (points.get(j).y) - currY) {                xlistY.add(points.get(j).y);                xCount++;            }            if ((currX - points.get(j).x) == (points.get(j).y) - currY) {                ylistX.add(points.get(j).x);                yCount++;            }        }        if (xCount >= 5) {            for (int m = 0; m < xlistY.size(); m++) {                int yVal = xlistY.get(m);                int pos = yVal / itemW;                try {                    if (xlistY.contains((pos + 1) * itemW) && xlistY.contains((pos + 2) * itemW) && xlistY.contains((pos + 3) * itemW) && xlistY.contains((pos + 4) * itemW))                        return true;                } catch (Exception e) {                    e.printStackTrace();                }            }        }        if (yCount >= 5) {            for (int n = 0; n < ylistX.size(); n++) {                int xVal = ylistX.get(n);                int pos = xVal / itemW;                try {                    if (ylistX.contains((pos + 1) * itemW) && ylistX.contains((pos + 2) * itemW) && ylistX.contains((pos + 3) * itemW) && ylistX.contains((pos + 4) * itemW))                        return true;                } catch (Exception e) {                    e.printStackTrace();                }            }        }        return false;    }

核心内容基本是这些。

0 0
原创粉丝点击