Android 数独小游戏

作者: zhangman523 | 来源:发表于2019-04-26 09:13 被阅读3次

    原文https://www.zhangman523.cn/401.html

    先看看效果图

    sudoku-o3.gif

    数独设计思路

    先看布局,我们可以看到数独由9x9的格子组成,每个格子中间有一个数字。

    • Cell (单个格子、android 中我们可以先用TextView代替)

    • Grid (由3x3Cell组成)

    • Borad (由3x3Grid组成)

    数独是由9x9 的格子组成,我们可以分为3个3x3Grid 组成
    布局方式用RelativeLayout来布局。

    数独检查游戏结束

    数独游戏在每一行,每一列 和每个Grid1-9数字不能重复

    每次输入时检查是否游戏结束和错误。

    代码实现

    首先我们使用自定义RelativeLayout来实现 Grid

    public class Grid extends RelativeLayout {
        ...
    }
    

    我们定义3x3Cell数组

    private List<List<TextView>> mTextArrays;
    

    TextView来表示单个格子

    初始化Cell

    mTextArrays = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        List<TextView> viewList = new ArrayList<>();
        for (int j = 0; j < 3; j++) {
            TextView textView = new TextView(context);
            textView.setWidth(TEXT_SIZE);
            textView.setHeight(TEXT_SIZE);
            textView.setBackgroundColor(Color.WHITE);
            textView.setId(View.generateViewId());
            textView.setGravity(Gravity.CENTER);
            addView(textView);
            viewList.add(textView);
            LayoutParams params = (LayoutParams) textView.getLayoutParams();
            if (j == 0) {
                if (i == 0) {
                    params.addRule(RelativeLayout.ALIGN_PARENT_START);
                    params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
                } else if (i == 1) {
                    params.addRule(RelativeLayout.BELOW, mTextArrays.get(0).get(0).getId());
                    params.topMargin = DensityUtils.dp2px(context,1);
                } else {
                    params.addRule(RelativeLayout.BELOW, mTextArrays.get(1).get(0).getId());
                    params.topMargin = DensityUtils.dp2px(context,1);
                }
            } else if (j == 1) {
                params.addRule(RelativeLayout.RIGHT_OF, viewList.get(j - 1).getId());
                params.addRule(ALIGN_TOP, viewList.get(j - 1).getId());
                params.leftMargin = DensityUtils.dp2px(context,1);
            } else {
                params.addRule(RelativeLayout.RIGHT_OF, viewList.get(j - 1).getId());
                params.addRule(ALIGN_TOP, viewList.get(j - 1).getId());
                params.leftMargin = DensityUtils.dp2px(context,1);
            }
        }
        mTextArrays.add(viewList);
    }
    

    使用自定义RelativeLayout来实现 Board

    public class Board extends RelativeLayout{
        ...
    }
    

    实现也是跟Grid实现一样,只是Grid作为子View

    private List<List<Grid>> mGridArray = new ArrayList<>(); //3x3 的Grid
    private List<List<TextView>> mCellArray; //9x9的Cell
    private TextView mCurrentCell; //当前选中的Cell
    
    private String mErrorTextColor = "#ff0000";//错误时候的文字颜色
    private String mLightTextColor = "#ffffff";//选中时候的文字颜色
    private String mDefaultTextColor = "#000000";//默认文字颜色
    private String mLightBgColor = "#4fe8fc";//选中的背景颜色
    private String mDefaultBgColor = "#ffffff";//默认背景颜色
    
    private String mDisableTextColor = "#e2e2e2";//不可编辑的文字颜色
    

    初始化 mGridArray 的逻辑和Grid 一样就不贴代码了。自己去看>_<!

    mCellArray = new ArrayList<>();
    for (int i = 0; i < 9; i++) {//初始化Cell Array
        List<TextView> cellArray = new ArrayList<>();
        for (int j = 0; j < 9; j++) {
            int x = i < 3 ? 0 : i < 6 ? 1 : 2; //3x3 的格子
            int y = j < 3 ? 0 : j < 6 ? 1 : 2;
            Grid grid = mGridArray.get(x).get(y);
            List<List<TextView>> gridTextArrays = grid.getTextArrays();
            TextView cell = gridTextArrays.get(i - x * 3).get(j - y * 3);
            cell.setTag(R.id.row, i);//设置tag 信息
            cell.setTag(R.id.column, j);
            cell.setTag(R.id.isLoad, false);
            cell.setTextColor(Color.parseColor(mDefaultTextColor));
            cell.setBackgroundColor(Color.parseColor(mDefaultBgColor));
            cell.setOnClickListener(this);
            cellArray.add(j, cell);
        }
        mCellArray.add(i, cellArray);
    }
    

    加载map

    数独的map 我们用81位长度的字符串来表示 0表示需要补全的,1-9 为默认的数字

    找一个默认的地图

    005406000000000201007380000062700090050023804704109060823590010490867020576031948
    

    加载方法

    /**
     * load sudoku map
     *
     * @param map map
     */
    public void loadMap(String map) {
        if (TextUtils.isEmpty(map)) return;
        for (int i = 0; i < mCellArray.size(); i++) {
            List<TextView> array = mCellArray.get(i);
            for (int j = 0; j < array.size(); j++) {
                TextView cell = array.get(j);//将81位的字符串 转为 9x9 的数组
                String s = map.substring(9 * i + j, 9 * i + j + 1);
                if (!"0".equals(s)) {
                    cell.setText(s);
                    cell.setTag(R.id.isLoad, true);
                    cell.setTextColor(Color.parseColor(mDisableTextColor));
                }
            }
        }
    }
    

    点击需要改变背景 高亮行和列。

    /**
    * Highlight the  grid by row and column
    *
    * @param row    row
    * @param column column
    */
    private void lightUpCellByRowAndColumn(int row, int column) {
        boolean lightText = !TextUtils.isEmpty(mCellArray.get(row).get(column).getText().toString());
        //这里做了个判断 如果是有数字 则高亮所有一样的数字 否则高亮选中行和列
        if (lightText) {
            lightSameNumber(row, column, false);
        } else {
            lightRowAndColumn(row, column);
        }
    }
    

    遍历Cell数组选中相同的数字

     /**
      * Highlight the same number
      *
      * @param row     row
      * @param column  column
      * @param isError error
      */
    private void lightSameNumber(int row, int column, boolean isError) {
        String value = mCellArray.get(row).get(column).getText().toString();
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (value.equals(mCellArray.get(i).get(j).getText().toString())) {
                    //change number color
                    if (i == row && column == j) {
                        //If it is wrong, change the text color without changing the background.
                        if (isError || mCellArray.get(i).get(j).getCurrentTextColor() == Color.parseColor(mErrorTextColor)) {
                            mCellArray.get(i).get(j).setBackgroundColor(Color.parseColor(mErrorTextColor));
                            mCellArray.get(i).get(j).setTextColor(Color.parseColor(mLightTextColor));
                        } else {
                            mCellArray.get(i).get(j).setBackgroundColor(Color.parseColor(mLightBgColor));
                            mCellArray.get(i).get(j).setTextColor(Color.parseColor(mLightTextColor));
                        }
                    } else {
                        if (mCellArray.get(i).get(j).getCurrentTextColor() == Color.parseColor(mErrorTextColor)) {
                            mCellArray.get(i).get(j).setTextColor(Color.parseColor(mErrorTextColor));
                        } else {
                            mCellArray.get(i).get(j).setTextColor(Color.parseColor(mLightTextColor));
                        }
                        mCellArray.get(i).get(j).setBackgroundColor(Color.parseColor(mLightBgColor));
                    }
                } else {
                    if ((Boolean) mCellArray.get(i).get(j).getTag(R.id.isLoad)) {
                        mCellArray.get(i).get(j).setTextColor(Color.parseColor(mDisableTextColor));
                    } else {
                        mCellArray.get(i).get(j).setTextColor(Color.parseColor(mDefaultTextColor));
                    }
                    mCellArray.get(i).get(j).setBackgroundColor(Color.parseColor(mDefaultBgColor));
                }
            }
        }
    }
    

    遍历Cell数组高亮行和列

    /**
     * Highlight row and column
     *
     * @param row    row
     * @param column column
     */
    private void lightRowAndColumn(int row, int column) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (i == row || j == column) {
                    if (mCellArray.get(i).get(j).getCurrentTextColor() == Color.parseColor(mErrorTextColor)) {
                        mCellArray.get(i).get(j).setTextColor(Color.parseColor(mErrorTextColor));
                    }
                    mCellArray.get(i).get(j).setBackgroundColor(Color.parseColor(mLightBgColor));
                } else {
                    if ((Boolean) mCellArray.get(i).get(j).getTag(R.id.isLoad)) {
                        mCellArray.get(i).get(j).setTextColor(Color.parseColor(mDisableTextColor));
                    } else {
                        if (mCellArray.get(i).get(j).getCurrentTextColor() == Color.parseColor(mErrorTextColor)) {
                            mCellArray.get(i).get(j).setTextColor(Color.parseColor(mErrorTextColor));
                        } else {
                            mCellArray.get(i).get(j).setTextColor(Color.parseColor(mDefaultTextColor));
                        }
                    }
                    mCellArray.get(i).get(j).setBackgroundColor(Color.parseColor(mLightTextColor));
                }
            }
        }
        mCellArray.get(row).get(column).setBackgroundColor(Color.parseColor(mLightBgColor));
    }
    

    输入数字

    每次输入我们需要判断游戏是否结束(数字重复或者完成数独)

    /**
     * Enter a number from 1-9
     *
     * @param number number
     */
    public void inputText(String number) {
        if (mCurrentCell == null) return;
        if (!(Boolean) mCurrentCell.getTag(R.id.isLoad)) {
            mCurrentCell.setText(number);
            boolean gameOver = checkFinish();
            if (gameOver) {
                if (mGameOverCallBack != null) mGameOverCallBack.gameOver();
            }
        }
    }
    

    checkFinish()方法中包括检查错误的方法,重复的数字需要高亮。需要检查行、列和宫(3x3的Grid)

    /**
     * check game error
     *
     * @param row    row
     * @param column column
     * @return boolean
     */
    private boolean checkGameError(int row, int column) {
        boolean result = false;
        result = checkSection(row, column);
        if (result) return result;
        //check row
        for (int i = 0; i < 9; i++) {
            String value = mCellArray.get(i).get(column).getText().toString();
            if (TextUtils.isEmpty(value)) continue;
            for (int j = i; j < 9; j++) {
                if (i == j) continue;
                if (value.equals(mCellArray.get(j).get(column).getText().toString())) {
                    Log.d(TAG, String.format("row error,value:%1$s in row:%2$d and column:%3$d", value, row, column));
                    result = true;
                    break;
                }
            }
        }
    
        if (result) return result;
    
        //check column
        for (int i = 0; i < 9; i++) {
            String value = mCellArray.get(row).get(i).getText().toString();
            if (TextUtils.isEmpty(value)) continue;
            for (int j = i; j < 9; j++) {
                if (i == j) continue;
                if (value.equals(mCellArray.get(row).get(j).getText().toString())) {
                    Log.d(TAG, String.format("column error,value:%1$s in row:%2$d and column:%3$d", value, row, column));
                    result = true;
                    break;
                }
            }
        }
        return result;
    }
    

    检查3x3 格子中是否有重复的数字

    /**
     * check duplicate numbers in the 3x3 grid
     *
     * @param row    row
     * @param column column
     * @return true or false
     */
    private boolean checkSection(int row, int column) {
        boolean result = false;
        String value = mCellArray.get(row).get(column).getText().toString();
        if (TextUtils.isEmpty(value)) {
            return result;
        }
        int start_i = row < 3 ? 0 : (row < 6 ? 3 : 6);//3x3 格子的边界
        int start_j = column < 3 ? 0 : (column < 6 ? 3 : 6);
        int end_i = start_i + 3;
        int end_j = start_j + 3;
    
        for (int i = start_i; i < end_i; i++) {
            for (int j = start_j; j < end_j; j++) {
                if (i == row && j == column) continue;
                if (value.equals(mCellArray.get(i).get(j).getText().toString())) {//如果3x3格子的内容有重复的数字则返回错误
                    Log.d(TAG, String.format("section error,value:%1$s in row:%2$d and column:%3$d", value, row, column));
                    result = true;
                    break;
                }
            }
        }
        return result;
    }
    

    游戏的逻辑已经完成了。大家可以去下载代码运行玩玩!

    工程已经放在GITHUB

    相关文章

      网友评论

        本文标题:Android 数独小游戏

        本文链接:https://www.haomeiwen.com/subject/flpagqtx.html