Android 數(shù)獨小游戲

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

先看看效果圖

sudoku-o3.gif

數(shù)獨設(shè)計思路

先看布局,我們可以看到數(shù)獨由9x9的格子組成,每個格子中間有一個數(shù)字。

  • Cell (單個格子、android 中我們可以先用TextView代替)

  • Grid (由3x3Cell組成)

  • Borad (由3x3Grid組成)

數(shù)獨是由9x9 的格子組成,我們可以分為3個3x3Grid 組成
布局方式用RelativeLayout來布局。

數(shù)獨檢查游戲結(jié)束

數(shù)獨游戲在每一行,每一列 和每個Grid1-9數(shù)字不能重復(fù)

每次輸入時檢查是否游戲結(jié)束和錯誤。

代碼實現(xiàn)

首先我們使用自定義RelativeLayout來實現(xiàn) Grid

public class Grid extends RelativeLayout {
    ...
}

我們定義3x3Cell數(shù)組

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來實現(xiàn) Board

public class Board extends RelativeLayout{
    ...
}

實現(xiàn)也是跟Grid實現(xiàn)一樣,只是Grid作為子View

private List<List<Grid>> mGridArray = new ArrayList<>(); //3x3 的Grid
private List<List<TextView>> mCellArray; //9x9的Cell
private TextView mCurrentCell; //當(dāng)前選中的Cell

private String mErrorTextColor = "#ff0000";//錯誤時候的文字顏色
private String mLightTextColor = "#ffffff";//選中時候的文字顏色
private String mDefaultTextColor = "#000000";//默認(rèn)文字顏色
private String mLightBgColor = "#4fe8fc";//選中的背景顏色
private String mDefaultBgColor = "#ffffff";//默認(rèn)背景顏色

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);//設(shè)置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

數(shù)獨的map 我們用81位長度的字符串來表示 0表示需要補全的,1-9 為默認(rèn)的數(shù)字

找一個默認(rèn)的地圖

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位的字符串 轉(zhuǎn)為 9x9 的數(shù)組
            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());
    //這里做了個判斷 如果是有數(shù)字 則高亮所有一樣的數(shù)字 否則高亮選中行和列
    if (lightText) {
        lightSameNumber(row, column, false);
    } else {
        lightRowAndColumn(row, column);
    }
}

遍歷Cell數(shù)組選中相同的數(shù)字

 /**
  * 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數(shù)組高亮行和列

/**
 * 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));
}

輸入數(shù)字

每次輸入我們需要判斷游戲是否結(jié)束(數(shù)字重復(fù)或者完成數(shù)獨)

/**
 * 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()方法中包括檢查錯誤的方法,重復(fù)的數(shù)字需要高亮。需要檢查行、列和宮(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 格子中是否有重復(fù)的數(shù)字

/**
 * 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格子的內(nèi)容有重復(fù)的數(shù)字則返回錯誤
                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;
}

游戲的邏輯已經(jīng)完成了。大家可以去下載代碼運行玩玩!

工程已經(jīng)放在GITHUB

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 周末公司活動安排在一旅游景點,湊巧也是婚紗攝影基地。 暴曬悶熱,本以為是拍婚紗照的淡季,路過一草坪時,竟然數(shù)了有二...
    依布茶卡閱讀 237評論 0 0
  • 下班時廠車沒有了。走到公交站臺,7點43分。坐3路車,到鳩江飯店下,用了35分鐘,再走回家,又用了25分鐘。大寶對...
    一個平凡的人閱讀 212評論 0 0

友情鏈接更多精彩內(nèi)容