AI智能
改变未来

SudokuSolver 2.0:用C++实现的数独解题程序 【二】

本篇是SudokuSolver 2.0:用C++实现的数独解题程序 【一】的续篇。

filterRowCandidatesEx 接口

u8 CQuizDealer::filterRowCandidatesEx(u8 row){u8 vals[10] = {0}; // last item denotes sum of zerosu8 base = row * 9;for (u8 col = 0; col < 9; ++col) {vals[col] = m_seqCell[base + col].val;if (vals[col] == 0) {vals[9] += 1;}}if (vals[9] == 0)return RET_PENDING;u8 blkBase = (row / 3) * 3;u8 blkTakens[21] = {0};setBlkRowTakens(blkBase, row % 3, blkTakens);if (blkTakens[0] == 0 && blkTakens[7] == 0 && blkTakens[14] == 0)return RET_PENDING;u8 ret = RET_PENDING;for (u8 idx = 0; idx < 3; ++idx) {if (ret = filterRowByPolicy1(row, idx, vals, blkTakens))return ret;}for (u8 idx = 0; idx < 3; ++idx) {if (ret = filterRowByPolicy2(row, idx, vals, blkTakens))return ret;}return RET_PENDING;}

filterRowCandidatesEx 接口实现对由参数 row 指定的行实施第二类候选值集合收缩算法。第 3 行到第 12 行的代码段是把该行当前各个 cell 的值填到数组 vals 中,并用 vals[9] 记录 0-cell 数量,若vals[9] 为 0,则说明该行没有 0-cell,即不具备收缩价值,直接返回 RET_PENGING,好让调用者进一步去考察其它行。

第 13 行到第 17 行的代码段是调用 setBlkRowTakens 接口填制 blkTakens 数组。blkTakens 数组实际记录了三个集合。举例说明一下:

000 000 000
023 010 780
100 406 009

400 050 001
900 000 006
060 000 090

第二行从左至右每 3 个一节,被划分为三节,这三节分别落在第 1 宫、第 2 宫、第 3 宫。事实上,每一行都可以依次方法划分为三节,每一节对应左、中、右各一个宫。blkTakens 数组的单元,每 7 个一组分成三组,每组记录一个宫中除去指定的行之外已填数字的集合。在上例中,填入blkTakens 数组的三个集合分别是 {1}、{4, 6}、{9},对应到 blkTakens 数组的单元取值,则有:

blkTakens[0] = 1,blkTakens[1] = 1;
blkTakens[7] = 2,blkTakens[8] = 4,blkTakens[9] = 6;
blkTakens[14] = 1,blkTakens[15] = 9。

第 2 行有 4 个空位(0-cell),具体分布是第一节 1 个,第二节 2 个,第三节 1 个。而blkTakens 数组的中宫集合,即 {4, 6} ,里的元素不能填到第 2 行的第二节(不然中宫里会出现两个 4 或两个 6),于是 4 和 6 只能填到第 2 行的第一节和第三节的空位上,而 [2,9] 空位的候选值里不能有 6(因为第 9 列里已经有 6),于是可以推定 [2,9] = 4,进而又有 [2,1] = 6。

仍以上面的例子为例,考察第 4 行,则填入blkTakens 数组的三个集合分别是 {6,9}、{}、{6,9},对应到 blkTakens 数组的单元取值,则有:

blkTakens[0] = 2,blkTakens[1] = 9,blkTakens[2] = 6;
blkTakens[7] = 0;
blkTakens[14] = 2,blkTakens[15] = 6,blkTakens[16] = 9。

第 4 行有 6 个空位,具体分布是每一节各 2 个。而blkTakens 数组的左宫集合与右宫集合相等,更一般而言,它们的交集为 {6, 9} ,易知这个交集里的元素只能填到第 4 行的第二节的空位上,而 [4,6] 空位的候选值里不能有 6(因为第 6 列里已经有 6),于是可以推定 [4,6] = 9,进而又有 [4,4] = 6。

上面例子里的第 2 行和第 4 行收缩示例实际就是filterRowByPolicy2和filterRowByPolicy1 里分别实现的方法。

setBlkRowTakens 接口

void CQuizDealer::setBlkRowTakens(u8 blkBase, u8 innRow, u8* pTakens){for (u8 idx = 0; idx < 3; ++idx) {u8 blk = blkBase + idx;u8* pVals = pTakens + idx * 7;u8 colBase = idx * 3;u8 row = block2row(blk, ((innRow + 1) % 3) * 3);setRowTakensInBlk(row * 9, colBase, pVals);row = block2row(blk, ((innRow + 2) % 3) * 3);setRowTakensInBlk(row * 9, colBase, pVals);}}

其中调用的setRowTakensInBlk 接口为:

void CQuizDealer::setRowTakensInBlk(u8 rowBase, u8 colBase, u8* pVals){for (u8 col = colBase; col < colBase + 3; ++col) {u8 val = m_seqCell[rowBase + col].val;if (val != 0) {u8 sum = pVals[0] + 1;pVals[sum] = val;pVals[0] = sum;}}}

filterRowByPolicy1接口

u8 CQuizDealer::filterRowByPolicy1(u8 row, u8 idx, u8* pVals, u8* pBlkTakens){u8 colBase = idx * 3;u8 v1 = pVals[colBase], v2 = pVals[colBase + 1], v3 = pVals[colBase + 2];u8 zeroSum = (u8)(v1 == 0) + (u8)(v2 == 0) + (u8)(v3 == 0);if (zeroSum == 0)return RET_PENDING;u8 isVals[7] = {0};if (idx == 0)intersection(pBlkTakens + 7, pBlkTakens + 14, isVals);else if (idx == 1)intersection(pBlkTakens, pBlkTakens + 14, isVals);elseintersection(pBlkTakens, pBlkTakens + 7, isVals);if (isVals[0] == 0)return RET_PENDING;return shrinkRowCandidatesP1(isVals, pVals, zeroSum, row, colBase);}

filterRowCandidatesEx 接口里调用filterRowByPolicy1 的代码段如下:

19     for (u8 idx = 0; idx < 3; ++idx) {20         if (ret = filterRowByPolicy1(row, idx, vals, blkTakens))21             return ret;22     }

沿用上面的例子说明一下 filterRowByPolicy1接口的实现代码:

000 000 000
023 010 780
100 406 009

400 050 001
900 000 006
060 000 090

考察第四行,上面代码段里至多会调用filterRowByPolicy1接口三次,idx 由 0 开始,逐次加一。

先看 idx = 0 的情形,四个传入参数分别为:row = 3(第四行的行下标为 3)、idx = 0、vals 中记录了第四行各 cell 的取值、blkTakens 中记录了三个集合,即左宫集合 {6, 9},中宫集合 {},右宫集合 {6, 9}。这一次调用是求中宫集合与右宫集合的交集,并试图往第四行第一节的空位上填值。

第 3 行到第 7 行的代码段,考察第四行的第一节三个 cell 中有几个空位,若没有空位,则无需填值;此时有两个空位,继续往下走。

第 8 行到第 16 行的代码段,求集合交集。因为 {} ∩{6, 9} = {},即交集为空,没有明确要填空的值,直接返回 RET_PENDING(即 0)。回到filterRowCandidatesEx 接口,idx 加一,再次调用filterRowByPolicy1。

此时 idx = 1,要试图往第四行第二节填空,第二节有空位,继续考察左宫集合 {6, 9} 与右宫集合 {6, 9} 的交集,交集不为空,进一步调用shrinkRowCandidatesP1 接口,发现可以对第四行做收缩处理,返回 RET_OK,回到filterRowCandidatesEx 接口,继续向上级调用返回 RET_OK。

intersection 函数实现求两个集合的交集,具体实现为:

void intersection(u8* pA, u8* pB, u8* pOut){u8 sumA = pA[0], sumB = pB[0];if (sumA == 0 || sumB == 0)return;for (u8 pos = 1; pos <= sumA; ++pos)for (u8 idx = 1; idx <= sumB; ++idx)if (pB[idx] == pA[pos]) {u8 sum = pOut[0] + 1;pOut[sum] = pA[pos];pOut[0] = sum;break;}}

shrinkRowCandidatesP1接口

u8 CQuizDealer::shrinkRowCandidatesP1(u8* pBlk, u8* pRow, u8 zeroSum, u8 row, u8 colBase){for (u8 col = colBase; col < colBase + 3; ++col) {if (pRow[col] != 0)if (!removeVal(pBlk, pRow[col]))return RET_PENDING;}if (zeroSum < pBlk[0]) {printf(\"shrinking row %d with ply1[blk:%d] went WRONG: 0Sum [%d] < vSum [%d]\\n\", (int)row + 1, (int)colBase / 3 + 1, (int)zeroSum, (int)pBlk[0]);return RET_WRONG;}u8 rowBase = row * 9;u8 exVals[4] = {0};if (zeroSum > pBlk[0]) {calcExCols(exVals, pBlk, pRow, rowBase, colBase, true);if (zeroSum - exVals[0] < pBlk[0]) {printf(\"shrinking row %d with ply1[blk:%d] went WRONG: 0Sum [%d] - xSum [%d] < vSum [%d]\\n\", (int)row + 1, (int)colBase / 3 + 1, (int)zeroSum, (int)exVals[0], (int)pBlk[0]);return RET_WRONG;}if (zeroSum - exVals[0] > pBlk[0])return RET_PENDING;}u8 ret = RET_PENDING;bool shrunken = false;for (u8 col = colBase; col < colBase + 3; ++col) {if (pRow[col] != 0 || inSet(col, exVals))continue;ret = shrinkCandidates(rowBase + col, pBlk);if (ret == RET_WRONG) {printf(\"shrinking [%d,%d] with row-ply1 went WRONG\\n\", (int)row + 1, (int)col + 1);return RET_WRONG;}if (ret == RET_OK)shrunken = true;}if (shrunken) {ret = RET_OK;printf(\"row %d shrunken ply-1 by blk %d\\n\", (u8)row + 1, (u8)colBase / 3 + 1);}return ret;}

shrinkRowCandidatesP1 接口实现考虑的情形比前述例子里的情形要复杂一些。

第 3 行到第 11 行的代码段是对传入的交集集合 pBlk 做进一步的削减处理,因为交集中某个数字如果已经在要填入的对应行的对应节中出现,则需要从交集中剔除(removeVal 函数实现在上一篇里有)。做完剔除处理后,集合里的数字都是待填值,此时若空位数小于待填值的数量,则必然会导致无法求解的局面,因此返回 RET_WRONG,好让上层调用尽快做出调整。

第 12 行到第 22 行的代码段的逻辑还要复杂一些,通过调用calcExCols 接口考察对应行的对应节中的空位的候选值情况,若某个空位的候选值集合和做完剔除处理的待填值集合无交集,则把该空位的列下标记入 exVals 数组。exVals 里记录的空位是不能填值的,因此,此时若空位数减去 exVals 里的空位数小于待填值的数量,还是会导致无法求解的局面,同样需要返回 RET_WRONG。

第 23 行到第 40 行的代码段是遍历对应行的对应节的空位,调用 shrinkCandidates 接口试图对空位的候选值做收缩处理。

上面用到的 inSet 函数实现为:

bool inSet(u8 val, u8* pVals){for (u8 idx = 1; idx <= pVals[0]; ++idx)if (val == pVals[idx])return true;return false;}

calcExCols 接口

void CQuizDealer::calcExCols(u8* pEx, u8* pBlk, u8* pRow, u8 rowBase, u8 colBase, bool ply1){u8 lower = (ply1 ? colBase : 0);u8 upper = (ply1 ? colBase + 3 : 9);for (u8 col = lower; col < upper; ++col) {if (ply1) {if (pRow[col] != 0)continue;}else {if (pRow[col] != 0 || (col >= colBase && col <= colBase + 2))continue;}u8 vals[10] = {0};intersection(m_seqCell[rowBase + col].candidates, pBlk, vals);if (vals[0] == 0) {u8 sum = pEx[0] + 1;pEx[sum] = col;pEx[0] = sum;}}}

calcExCols 接口为shrinkRowCandidatesP1 和shrinkRowCandidatesP2 共用。

shrinkCandidates 接口

u8 CQuizDealer::shrinkCandidates(u8 cellPos, u8* pVals){u8 vals[10] = {0};intersection(m_seqCell[cellPos].candidates, pVals, vals);if (vals[0] == 0)return RET_WRONG;if (vals[0] != m_seqCell[cellPos].candidates[0]) {for (u8 idx = 0; idx <= pVals[0]; ++idx)m_seqCell[cellPos].candidates[idx] = vals[idx];if (vals[0] == 1)return RET_OK;}return RET_PENDING;}

shrinkCandidates 接口对下标为 cellPos 的 0-cell 做候选值收缩处理,逻辑不难,不做额外解释。

filterRowByPolicy2接口

u8 CQuizDealer::filterRowByPolicy2(u8 row, u8 idx, u8* pVals, u8* pBlkTakens){u8 colBase = idx * 3;u8 v1 = pVals[colBase], v2 = pVals[colBase + 1], v3 = pVals[colBase + 2];u8 zeroSum = pVals[9] - (u8)(v1 == 0) - (u8)(v2 == 0) - (u8)(v3 == 0);if (zeroSum == 0)return RET_PENDING;u8* pBlk = pBlkTakens + (idx * 7);if (pBlk[0] == 0)return RET_PENDING;return shrinkRowCandidatesP2(pBlk, pVals, zeroSum, row, colBase);}

filterRowByPolicy2 和filterRowByPolicy1 类似。

继续沿用上面的例子说明一下 filterRowByPolicy2 接口的实现代码:

000 000 000
023 010 780
100 406 009

400 050 001
900 000 006
060 000 090

考察第二行,filterRowCandidatesEx 接口里至多会调用filterRowByPolicy2接口三次,idx 由 0 开始,逐次加一。

先看 idx = 0 的情形,四个传入参数分别为:row = 3(第四行的行下标为 3)、idx = 0、vals 中记录了第四行各 cell 的取值、blkTakens 中记录了三个集合,即左宫集合 {1},中宫集合 {4, 6},右宫集合 {9}。这一次调用是考察左宫集合 {1},并试图往第二行的第二节和第三节的空位上填值。

第 3 行到第 7 行的代码段,考察第二行的第二节和第三节共有几个空位,若没有空位,则无需填值;此时有三个空位,继续往下走。

第 8 行到第 10 行的代码段,求 idx = 0 对应的集合,即左宫集合 {1}。不为空,继续调用shrinkRowCandidatesP2 接口做进一步处理,后者会因为左宫集合 {1}里的 1 已经出现在第二行第二节里而返回 RET_PENDING。

回到filterRowCandidatesEx 接口,idx 加一,再次调用filterRowByPolicy2。

此时 idx = 1,要试图往第二行的第一节和第三节填空,此两节共有两个空位,而 idx = 1 对应中宫集合 {4, 6},不为空,进一步调用shrinkRowCandidatesP2 接口,后者发现可以对第二行做收缩处理,返回 RET_OK,回到filterRowCandidatesEx 接口,继续向上级调用返回 RET_OK。

shrinkRowCandidatesP2接口

u8 CQuizDealer::shrinkRowCandidatesP2(u8* pBlk, u8* pRow, u8 zeroSum, u8 row, u8 colBase){u8 rowBase = row * 9;for (u8 col = 0; col < 9; ++col) {if (col >= colBase && col <= colBase + 2)continue;if (pRow[col] != 0)if (!removeVal(pBlk, pRow[col]))return RET_PENDING;}if (zeroSum < pBlk[0]) {printf(\"shrinking row %d with ply2[blk:%d] went WRONG: 0Sum [%d] < vSum [%d]\\n\", (int)row + 1, (int)colBase / 3 + 1, (int)zeroSum, (int)pBlk[0]);return RET_WRONG;}u8 exVals[7] = {0};if (zeroSum > pBlk[0]) {calcExCols(exVals, pBlk, pRow, rowBase, colBase, false);if (zeroSum - exVals[0] < pBlk[0]) {printf(\"shrinking row %d with ply2[blk:%d] went WRONG: 0Sum [%d] - xSum [%d] < vSum [%d]\\n\", (int)row + 1, (int)colBase / 3 + 1, (int)zeroSum, (int)exVals[0], (int)pBlk[0]);return RET_WRONG;}if (zeroSum - exVals[0] > pBlk[0])return RET_PENDING;}u8 ret = RET_PENDING;bool shrunken = false;for (u8 col = 0; col < 9; ++col) {if (pRow[col] != 0 || (col >= colBase && col <= colBase + 2) || inSet(col, exVals))continue;ret = shrinkCandidates(rowBase + col, pBlk);if (ret == RET_WRONG) {printf(\"shrinking [%d,%d] with row-ply2 went WRONG\\n\", (int)row + 1, (int)col + 1);return RET_WRONG;}if (ret == RET_OK)shrunken = true;}if (shrunken) {ret = RET_OK;printf(\"row %d shrunken ply-2 by blk %d\\n\", (u8)row + 1, (u8)colBase / 3 + 1);}return ret;}

shrinkRowCandidatesP2 接口实现和shrinkRowCandidatesP1 类似。

filterColCandidatesEx 接口

u8 CQuizDealer::filterColCandidatesEx(u8 col){u8 vals[10] = {0}; // last item denotes sum of zerosfor (u8 row = 0; row < 9; ++row) {u8 celIdx = row * 9 + col;vals[row] = m_seqCell[celIdx].val;if (vals[row] == 0) {vals[9] += 1;}}if (vals[9] == 0)return RET_PENDING;u8 blkBase = col / 3;u8 blkTakens[21] = {0};setBlkColTakens(blkBase, col % 3, blkTakens);if (blkTakens[0] == 0 && blkTakens[7] == 0 && blkTakens[14] == 0)return RET_PENDING;u8 ret = RET_PENDING;for (u8 idx = 0; idx < 3; ++idx) {if (ret = filterColByPolicy1(col, idx, vals, blkTakens))return ret;}for (u8 idx = 0; idx < 3; ++idx) {if (ret = filterColByPolicy2(col, idx, vals, blkTakens))return ret;}return RET_PENDING;}

filterColCandidatesEx 接口实现和filterRowCandidatesEx 接口的实现实质是一样的,相当于对 9×9 方阵旋转 90 度后做一次 filterRowCandidatesEx。

setBlkColTakens 和 setColTakensInBlk接口

void CQuizDealer::setBlkColTakens(u8 blkBase, u8 innCol, u8* pTakens){for (u8 idx = 0; idx < 3; ++idx) {u8 blk = blkBase + idx * 3;u8* pVals = pTakens + idx * 7;u8 rowBase = idx * 27;u8 col = block2col(blk, (innCol + 1) % 3);setColTakensInBlk(col, rowBase, pVals);col = block2col(blk, (innCol + 2) % 3);setColTakensInBlk(col, rowBase, pVals);}}
void CQuizDealer::setColTakensInBlk(u8 col, u8 rowBase, u8* pVals){for (u8 idx = rowBase; idx < rowBase + 27; idx += 9) {u8 val = m_seqCell[idx + col].val;if (val != 0) {u8 sum = pVals[0] + 1;pVals[sum] = val;pVals[0] = sum;}}}

filterColByPolicy1和 shrinkColCandidatesP1接口

u8 CQuizDealer::filterColByPolicy1(u8 col, u8 idx, u8* pVals, u8* pBlkTakens){u8 segRowBase = idx * 3;u8 v1 = pVals[segRowBase], v2 = pVals[segRowBase + 1], v3 = pVals[segRowBase + 2];u8 zeroSum = (u8)(v1 == 0) + (u8)(v2 == 0) + (u8)(v3 == 0);if (zeroSum == 0)return 0;u8 isVals[7] = {0};if (idx == 0)intersection(pBlkTakens + 7, pBlkTakens + 14, isVals);else if (idx == 1)intersection(pBlkTakens, pBlkTakens + 14, isVals);elseintersection(pBlkTakens, pBlkTakens + 7, isVals);if (isVals[0] == 0)return RET_PENDING;return shrinkColCandidatesP1(isVals, pVals, zeroSum, col, segRowBase);}
u8 CQuizDealer::shrinkColCandidatesP1(u8* pBlk, u8* pCol, u8 zeroSum, u8 col, u8 segRowBase){for (u8 idx = segRowBase; idx < segRowBase + 3; ++idx) {if (pCol[idx] != 0)if (!removeVal(pBlk, pCol[idx]))return RET_PENDING;}if (zeroSum < pBlk[0]) {printf(\"shrinking col %d with ply1[vblk:%d] went WRONG: 0Sum [%d] < vSum [%d]\\n\", (int)col + 1, (int)segRowBase / 3 + 1, (int)zeroSum, (int)pBlk[0]);return RET_WRONG;}u8 exVals[4] = {0};if (zeroSum > pBlk[0]) {calcExRows(exVals, pBlk, pCol, col, segRowBase, true);if (zeroSum - exVals[0] < pBlk[0]) {printf(\"shrinking col %d with ply1[vblk:%d] went WRONG: 0Sum [%d] - xSum [%d] < vSum [%d]\\n\", (int)col + 1, (int)segRowBase / 3 + 1, (int)zeroSum, (int)exVals[0], (int)pBlk[0]);return RET_WRONG;}if (zeroSum - exVals[0] > pBlk[0])return RET_PENDING;}u8 ret = RET_PENDING;bool shrunken = false;for (u8 row = segRowBase; row < segRowBase + 3; ++row) {if (pCol[row] != 0 || inSet(row, exVals))continue;ret = shrinkCandidates(row * 9 + col, pBlk);if (ret == RET_WRONG) {printf(\"shrinking [%d,%d] with col-ply1 went WRONG\\n\", (int)row + 1, (int)col + 1);return RET_WRONG;}if (ret == RET_OK)shrunken = true;}if (shrunken) {ret = RET_OK;printf(\"col %d shrunken ply-1 by vblk %d\\n\", (u8)col + 1, (u8)segRowBase / 3 + 1);}return ret;}

filterColByPolicy2和shrinkColCandidatesP2接口

u8 CQuizDealer::filterColByPolicy2(u8 col, u8 idx, u8* pVals, u8* pBlkTakens){u8 segRowBase = idx * 3;u8 v1 = pVals[segRowBase], v2 = pVals[segRowBase + 1], v3 = pVals[segRowBase + 2];u8 zeroSum = pVals[9] - (u8)(v1 == 0) - (u8)(v2 == 0) - (u8)(v3 == 0);if (zeroSum == 0)return 0;u8* pBlk = pBlkTakens + (idx * 7);if (pBlk[0] == 0)return RET_PENDING;return shrinkColCandidatesP2(pBlk, pVals, zeroSum, col, segRowBase);}
u8 CQuizDealer::shrinkColCandidatesP2(u8* pBlk, u8* pCol, u8 zeroSum, u8 col, u8 segRowBase){for (u8 row = 0; row < 9; ++row) {if (row >= segRowBase && row <= segRowBase + 2)continue;if (pCol[row] != 0)if (!removeVal(pBlk, pCol[row]))return RET_PENDING;}if (zeroSum < pBlk[0]) {printf(\"shrinking col %d with ply2[vblk:%d] went WRONG: 0Sum [%d] < vSum [%d]\\n\", (int)col + 1, (int)segRowBase / 3 + 1, (int)zeroSum, (int)pBlk[0]);return RET_WRONG;}u8 exVals[7] = {0};if (zeroSum > pBlk[0]) {calcExRows(exVals, pBlk, pCol, col, segRowBase, false);if (zeroSum - exVals[0] < pBlk[0]) {printf(\"shrinking col %d with ply2[vblk:%d] went WRONG: 0Sum [%d] - xSum [%d] < vSum [%d]\\n\", (int)col + 1, (int)segRowBase / 3 + 1, (int)zeroSum, (int)exVals[0], (int)pBlk[0]);return RET_WRONG;}if (zeroSum - exVals[0] > pBlk[0])return RET_PENDING;}u8 ret = 0;bool shrunken = false;for (u8 row = 0; row < 9; ++row) {if (pCol[row] != 0 || (row >= segRowBase && row <= segRowBase + 2) || inSet(row, exVals))continue;ret = shrinkCandidates(row * 9 + col, pBlk);if (ret == RET_WRONG) {printf(\"shrinking [%d,%d] with col-ply2 went wrong\\n\", (int)row + 1, (int)col + 1);return RET_WRONG;}if (ret == RET_OK)shrunken = true;}if (shrunken) {ret = RET_OK;printf(\"col %d shrunken ply-2 by vblk %d\\n\", (u8)col + 1, (u8)segRowBase / 3 + 1);}return ret;}

calcExRows接口

void CQuizDealer::calcExRows(u8* pEx, u8* pBlk, u8* pCol, u8 col, u8 segRowBase, bool ply1){u8 lower = (ply1 ? segRowBase : 0);u8 upper = (ply1 ? segRowBase + 3 : 9);for (u8 row = lower; row < upper; ++row) {if (ply1) {if (pCol[row] != 0)continue;}else {if (pCol[row] != 0 || (row >= segRowBase && row <= segRowBase + 2))continue;}u8 vals[10] = {0};intersection(m_seqCell[row * 9 + col].candidates, pBlk, vals);if (vals[0] == 0) {u8 sum = pEx[0] + 1;pEx[sum] = row;pEx[0] = sum;}}}

其他细微代码修改

printCandidates 函数

去掉了末尾的一行语句,即printf(\”\\n\”);

showQuiz 接口

void CQuizDealer::showQuiz(){...
printf(\"\\nSteps:%u\\nCandidates:\\n\", m_steps);u8 count = 1;u8 foremost = 0;for (std::set<u8>::iterator it = m_setBlank.begin(); it != m_setBlank.end(); ++it, ++count) {u8 pos = *it;u8* pVals = m_seqCell[pos].candidates;if (foremost == 0 && pVals[0] == 1)foremost = pos;printCandidates(pos, pVals);if (count % 3 == 0)printf(\"\\n\");elseprintf(\" \");}if (count % 3 != 1)printf(\"\\n\");if (foremost != 0)printf(\"The foremost cell with one candidate at [%d,%d]\\n\", (int)(foremost / 9 + 1), (int)(foremost % 9 + 1));if (m_guessLevel != 0)printf(\"\\nAt guess level %d [%d,%d] %d\\n\", (int)m_guessLevel, (int)(m_guessPos / 9 + 1), (int)(m_guessPos % 9 + 1), (int)m_guessValPos);}

parse 和 guess 接口

开头增加一条语句:++m_steps;

nextGuess 接口

void CQuizDealer::nextGuess(){...
++m_steps;Snapshot* pTop = m_stkSnap.top();...

step 接口

去掉语句:nextGuess();之后的一条语句:m_steps++;

showOrderList 函数

// Sudoku Solver 1.0 2021/9/20#define STR_VER \"Sudoku Solver 2.0 2021/10/2 by readalps\\n\\n\"void showOrderList(){printf(STR_VER);...}

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » SudokuSolver 2.0:用C++实现的数独解题程序 【二】