如何为井字游戏创建简单、中等和困难的关卡?

Gok*_*oku 1 java android tic-tac-toe

目前我正在 Android 中创建 tic-tac-toe-game

我已成功创建游戏,但面临一些问题

这是我迄今为止尝试过的代码

这是我的看板视图

public class BoardView extends View implements GestureDetector.OnGestureListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
    private static final int STROKE_WIDTH = 10;
    private static final int SWEEPER_WIDTH = 20;

    private float[] gridLinePoints;
    private Paint gridPaint;

    private PointF[][] centerPoints;
    private Paint signPaint;

    private List<SignData> signDataList;

    private @Constants.WinLinePosition int winLinePosition;
    private Paint winLinePaint;

    private GestureDetector clickDetector;
    private OnBoardInteractionListener onBoardInteractionListener;

    private ValueAnimator clickAnimator;
    private ValueAnimator winLineAnimator;
    private ValueAnimator resetAnimator;

    private float signRadius;
    private float winLineLength;
    private float sweeperStartPosition;

    private Paint sweeperPaint;
    private int[] sweeperColors;
    private float[] sweeperStops;

    public BoardView(Context context) {
        super(context);
        init();
    }

    public BoardView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public BoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.M)
    private void init() {
        gridLinePoints = new float[16];

        centerPoints = new PointF[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                centerPoints[i][j] = new PointF();
            }
        }

        signDataList = new ArrayList<>();

        winLinePosition = Constants.NONE;

        gridPaint = new Paint();
        gridPaint.setColor(getContext().getResources().getColor(R.color.holo_green_dark, null));
        gridPaint.setAntiAlias(true);
        gridPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
        gridPaint.setStrokeCap(Paint.Cap.ROUND);

        signPaint = new Paint();
        signPaint.setColor(getContext().getResources().getColor(R.color.holo_orange_dark, null));
        signPaint.setAntiAlias(true);
        signPaint.setStyle(Paint.Style.STROKE);
        signPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
        signPaint.setStrokeCap(Paint.Cap.ROUND);

        winLinePaint = new Paint();
        winLinePaint.setColor(getContext().getResources().getColor(R.color.holo_red_dark, null));
        winLinePaint.setAntiAlias(true);
        winLinePaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
        winLinePaint.setStrokeCap(Paint.Cap.ROUND);

        clickDetector = new GestureDetector(getContext(), this);

        clickAnimator = new ValueAnimator();
        clickAnimator.setDuration(150);
        clickAnimator.setInterpolator(new DecelerateInterpolator());
        clickAnimator.addUpdateListener(this);
        clickAnimator.addListener(this);

        winLineAnimator = new ValueAnimator();
        winLineAnimator.setDuration(150);
        winLineAnimator.setInterpolator(new DecelerateInterpolator());
        winLineAnimator.addUpdateListener(this);
        winLineAnimator.addListener(this);

        resetAnimator = new ValueAnimator();
        resetAnimator.setDuration(500);
        resetAnimator.setInterpolator(new AccelerateInterpolator());
        resetAnimator.addUpdateListener(this);
        resetAnimator.addListener(this);

        sweeperPaint = new Paint();
        sweeperPaint.setAntiAlias(true);
        sweeperPaint.setStyle(Paint.Style.FILL);

        sweeperColors = new int[3];
        sweeperColors[0] = Color.parseColor("#0000DDFF");
        sweeperColors[1] = Color.parseColor("#FF00DDFF");
        sweeperColors[2] = Color.parseColor("#0000DDFF");

        sweeperStops = new float[3];
        sweeperStops[0] = 0;
        sweeperStops[1] = 0.5f;
        sweeperStops[2] = 1;

        setLayerType(LAYER_TYPE_SOFTWARE, sweeperPaint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        getLayoutParams().height = getMeasuredWidth();

        setGridLinePoints();
        setCenterPoints();
        setAnimationValues();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawGrid(canvas);
        super.onDraw(canvas);

        if (resetAnimator.isRunning()) {
            canvas.clipRect(0, sweeperStartPosition, getMeasuredWidth(), getMeasuredWidth());

            setSweeperGradient();
            canvas.drawRect(0, sweeperStartPosition, getMeasuredWidth(), sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperPaint);
        }

        drawSigns(canvas);
        drawWinLine(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if ((!isEnabled()) || (clickAnimator.isRunning()) || (isAnimationFlagSet())) {
            return super.onTouchEvent(event);
        } else {
            return clickDetector.onTouchEvent(event);
        }
    }

    private boolean isAnimationFlagSet() {
        for (SignData signData : signDataList) {
            if (signData.isAnimationFlag()) {
                return true;
            }
        }
        return false;
    }

    private void setGridLinePoints() {
        int side = getMeasuredWidth();
        float padding = dpToPx(STROKE_WIDTH / 2f);

        gridLinePoints[0] = gridLinePoints[4] = gridLinePoints[9] = gridLinePoints[13] = padding;
        gridLinePoints[1] = gridLinePoints[3] = gridLinePoints[8] = gridLinePoints[10] = side / 3f;
        gridLinePoints[2] = gridLinePoints[6] = gridLinePoints[11] = gridLinePoints[15] = side - padding;
        gridLinePoints[5] = gridLinePoints[7] = gridLinePoints[12] = gridLinePoints[14] = (2 * side) / 3f;
    }

    private void setCenterPoints() {
        float a = getMeasuredWidth() / 6f;

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                centerPoints[i][j].x = a + (j * (2 * a));
                centerPoints[i][j].y = a + (i * (2 * a));
            }
        }
    }

    private void setAnimationValues() {
        clickAnimator.setFloatValues(0, (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH));
        winLineAnimator.setFloatValues(0, getMeasuredWidth());
        resetAnimator.setFloatValues(-dpToPx(SWEEPER_WIDTH), getMeasuredWidth());
    }

    private void setSweeperGradient() {
        float axis = sweeperStartPosition + (dpToPx(SWEEPER_WIDTH / 2f));

        LinearGradient horizontalGradient = new LinearGradient(0, axis, getMeasuredWidth(), axis,
                sweeperColors, sweeperStops, Shader.TileMode.CLAMP);

        LinearGradient verticalGradient = new LinearGradient(getMeasuredWidth() / 2f, sweeperStartPosition,
                getMeasuredWidth() / 2f, sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperColors, sweeperStops,
                Shader.TileMode.CLAMP);

        ComposeShader shader = new ComposeShader(horizontalGradient, verticalGradient, PorterDuff.Mode.MULTIPLY);

        sweeperPaint.setShader(shader);
    }

    private void drawGrid(Canvas canvas) {
        canvas.drawLines(gridLinePoints, gridPaint);
    }

    private void drawSigns(Canvas canvas) {
        for (int i = 0; i < signDataList.size(); i++) {
            SignData signData = signDataList.get(i);

            switch (signData.getSign()) {
                case Constants.CIRCLE:
                    drawCircle(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
                    break;
                case Constants.CROSS:
                    drawCross(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
                    break;
                case Constants.EMPTY:
                    break;
            }
        }
    }

    private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
        float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);

        canvas.drawCircle(center.x, center.y, radius, signPaint);
    }

    private void drawCross(Canvas canvas, PointF center, boolean animationFlag) {
        float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);

        canvas.drawLine(center.x - radius, center.y - radius, center.x + radius, center.y + radius, signPaint);
        canvas.drawLine(center.x - radius, center.y + radius, center.x + radius, center.y - radius, signPaint);
    }

    private void drawWinLine(Canvas canvas) {
        float length = winLineLength;

        float a = getMeasuredWidth() / 6f;

        float padding = dpToPx(STROKE_WIDTH);

        switch (winLinePosition) {
            case Constants.NONE:
                break;
            case Constants.ROW_1:
                canvas.drawLine(padding, a, length - padding, a, winLinePaint);
                break;
            case Constants.ROW_2:
                canvas.drawLine(padding, a + (2 * a), length - padding, a + (2 * a), winLinePaint);
                break;
            case Constants.ROW_3:
                canvas.drawLine(padding, a + (4 * a), length - padding, a + (4 * a), winLinePaint);
                break;
            case Constants.COLUMN_1:
                canvas.drawLine(a, padding, a, length - padding, winLinePaint);
                break;
            case Constants.COLUMN_2:
                canvas.drawLine(a + (2 * a), padding, a + (2 * a), length - padding, winLinePaint);
                break;
            case Constants.COLUMN_3:
                canvas.drawLine(a + (4 * a), padding, a + (4 * a), length - padding, winLinePaint);
                break;
            case Constants.DIAGONAL_1:
                canvas.drawLine(padding, padding, length - padding, length - padding, winLinePaint);
                break;
            case Constants.DIAGONAL_2:
                canvas.drawLine(getMeasuredWidth() - padding, padding, padding + getMeasuredWidth()
                        - length, length - padding, winLinePaint);
                break;
        }
    }

    void addSignToBoard(@Constants.Sign int sign, int row, int column) {
        SignData signData = new SignData();
        signData.setSign(sign);
        signData.setRow(row);
        signData.setColumn(column);
        signData.setAnimationFlag(true);

        if (clickAnimator.isRunning()) {
            clickAnimator.end();
        }

        signDataList.add(signData);
        clickAnimator.start();
    }

    void showWinLine(@Constants.WinLinePosition int winLinePosition) {
        this.winLinePosition = winLinePosition;

        winLineAnimator.start();
    }

    void resetBoard() {
        if (!resetAnimator.isRunning()) {
            resetAnimator.start();
        }
    }

    boolean isAlreadyAdded(int row, int column) {
        for (int i = 0; i < signDataList.size(); i++) {
            SignData signData = signDataList.get(i);

            if ((signData.getRow() == row) && (signData.getColumn() == column)) {
                return true;
            }
        }

        return false;
    }

    private float dpToPx(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        float x = e.getX();
        float y = e.getY();

        int row = detectIndexOfPartition(y);
        int column = detectIndexOfPartition(x);

        if ((row != -1) && (column != -1)) {
            onBoardInteractionListener.onBoardClick(BoardView.this, row, column);
        }

        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    private int detectIndexOfPartition(float value) {
        float maxValue = getMeasuredWidth();
        float totalNumberOfPartitions = 3;

        float lengthOfSinglePartition = maxValue / totalNumberOfPartitions;

        return (int) (value / lengthOfSinglePartition);
    }

    public void setOnBoardInteractionListener(OnBoardInteractionListener onBoardInteractionListener) {
        this.onBoardInteractionListener = onBoardInteractionListener;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        if (animation == clickAnimator) {
            signRadius = (float) animation.getAnimatedValue();
        } else if (animation == winLineAnimator) {
            winLineLength = (float) animation.getAnimatedValue();
        } else if (animation == resetAnimator) {
            sweeperStartPosition = (float) animation.getAnimatedValue();
        }
        invalidate();
    }

    @Override
    public void onAnimationStart(Animator animation) {

    }

    @Override
    public void onAnimationEnd(Animator animation) {
        if (animation == clickAnimator) {
            SignData signData = signDataList.get(signDataList.size() - 1);
            signData.setAnimationFlag(false);
            onBoardInteractionListener.onSignAdded(signData.getSign(), signData.getRow(), signData.getColumn());
            signRadius = 0;
        } else if (animation == resetAnimator) {
            signDataList.clear();
            winLinePosition = Constants.NONE;
            onBoardInteractionListener.onBoardReset();
        }
    }

    @Override
    public void onAnimationCancel(Animator animation) {

    }

    @Override
    public void onAnimationRepeat(Animator animation) {

    }

    interface OnBoardInteractionListener {

        void onBoardClick(BoardView board, int row, int column);

        void onSignAdded(@Constants.Sign int sign, int row, int column);

        void onBoardReset();
    }

    private class SignData {
        private @Constants.Sign int sign;
        private int row;
        private int column;
        private boolean animationFlag;

        @Constants.Sign int getSign() {
            return sign;
        }

        void setSign(@Constants.Sign int sign) {
            this.sign = sign;
        }

        int getRow() {
            return row;
        }

        void setRow(int row) {
            this.row = row;
        }

        int getColumn() {
            return column;
        }

        void setColumn(int column) {
            this.column = column;
        }

        boolean isAnimationFlag() {
            return animationFlag;
        }

        void setAnimationFlag(boolean animationFlag) {
            this.animationFlag = animationFlag;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的大脑课

class Brain {
    private static Brain INSTANCE;

    private @Constants.Sign
    int[][] board = new int[3][3];

    private int rowOfResult;
    private int columnOfResult;

    private int depth;

    private @Constants.Sign
    int computerSign;
    private @Constants.Sign
    int playerSign;

    private OnProcessCompleteListener onProcessCompleteListener;

    private static final int HORIZONTAL = 0;
    private static final int VERTICAL = 1;
    private static final int DIAGONAL = 2;

    @IntDef({HORIZONTAL, VERTICAL, DIAGONAL})
    @interface DirectionOfWinLine {

    }

    // References used by isWin function.
    private int[] winSequence = new int[3];
    private int[] row = new int[3];
    private int[] column = new int[3];
    private int[] diagonal1 = new int[3];
    private int[] diagonal2 = new int[3];

    private Brain() {
    }

    static Brain getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Brain();
        }
        return INSTANCE;
    }

    void play() {
        if (onProcessCompleteListener == null) {
            return;
        }
        calculateNextMove(computerSign, depth);

        onProcessCompleteListener.onNextMoveCalculated(rowOfResult, columnOfResult);
    }

    private int calculateNextMove(@Constants.Sign int sign, int depth) {

        if (isWin(computerSign, false)) {
            return 10 - depth;
        } else if (isWin(playerSign, false)) {
            return depth - 10;
        }

        if (depth >= 9) {
            return 0;
        }

        List<Integer> scores = new ArrayList<>(), rowIndices = new ArrayList<>(), columnIndices = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[i][j] == Constants.EMPTY) {
                    board[i][j] = sign;
                    scores.add(calculateNextMove(getOppositeSign(sign), depth + 1));
                    rowIndices.add(i);
                    columnIndices.add(j);
                    board[i][j] = Constants.EMPTY;
                }
            }
        }

        if (sign == computerSign) {
            int maxScore = -100;
            for (int i = 0; i < scores.size(); i++) {
                if (scores.get(i) > maxScore) {
                    maxScore = scores.get(i);
                }
            }
            return randomizeScore(maxScore, scores, rowIndices, columnIndices);

        } else {
            int minScore = 100;
            for (int i = 0; i < scores.size(); i++) {
                if (scores.get(i) < minScore) {
                    minScore = scores.get(i);
                }
            }
            return randomizeScore(minScore, scores, rowIndices, columnIndices);
        }
    }

    private int randomizeScore(int score, List<Integer> scores, List<Integer> rowIndices, List<Integer> columnIndices) {
        List<Integer> equalScoreIndices = new ArrayList<>();

        for (int i = 0; i < scores.size(); i++) {
            if (scores.get(i) == score) {
                equalScoreIndices.add(i);
            }
        }

        Random rand = new Random();
        int randomIndex = equalScoreIndices.get(rand.nextInt(equalScoreIndices.size()));

        rowOfResult = rowIndices.get(randomIndex);
        columnOfResult = columnIndices.get(randomIndex);

        return score;
    }

    private boolean isWin(@Constants.Sign int sign, boolean notifyWinEnabled) {
        for (int i = 0; i < 3; i++) {
            winSequence[i] = sign;
        }

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {

                if (i == j) {
                    diagonal1[i] = board[i][j];
                }
                if ((i + j) == 2) {
                    diagonal2[i] = board[i][j];
                }

                row[j] = board[i][j];
                column[j] = board[j][i];
            }

            if (isEqual(row, winSequence)) {
                if (notifyWinEnabled) {
                    notifyWin(sign, HORIZONTAL, i + 1);
                }
                return true;
            } else if (isEqual(column, winSequence)) {
                if (notifyWinEnabled) {
                    notifyWin(sign, VERTICAL, i + 1);
                }
                return true;
            }
        }

        if (isEqual(diagonal1, winSequence)) {
            if (notifyWinEnabled) {
                notifyWin(sign, DIAGONAL, 1);
            }
            return true;
        } else if (isEqual(diagonal2, winSequence)) {
            if (notifyWinEnabled) {
                notifyWin(sign, DIAGONAL, 2);
            }
            return true;
        }

        return false;
    }

    private boolean isEqual(

Sta*_*Kou 5

  1. 实现难度:为了支持简单和中等难度,我建议你只使用随机。你已经实现了HARD难度,所以你只需要犯“错误”的难度较小的逻辑,并且这个错误可以随机实现。

     private void calculateNextMoveRandom() {
         Random rand = new Random();
    
         int randomRow;
         int randomColumn;
    
         while (true) {
             randomRow = rand.nextInt(3);
             randomColumn = rand.nextInt(3);
    
             if (Constants.EMPTY == board[randomRow][randomColumn]) {
                 rowOfResult = randomRow;
                 columnOfResult = randomColumn;
                 return;
             }
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)

2. **位图标记:** 您可以使用`BitmapFactory.decodeResource()`在屏幕上绘制位图。
    private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
        int iconSize = (int) LayoutUtil.getPixelFromDp(MARKER_SIZE, getContext());
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
        Bitmap scaled = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
        canvas.drawBitmap(scaled, center.x - (iconSize >> 1), center.y - (iconSize >> 1), signPaint);
    }
Run Code Online (Sandbox Code Playgroud)
3. **随机难度:** 只需使用随机设置难度即可。
    brain.setDifficulty(new Random().nextInt(3));
Run Code Online (Sandbox Code Playgroud)

这是我的拉取请求:(堕落链接)
https://github.com/SuperSaiyanGoku3/MyGame/pull/1

在此输入图像描述