------------------------------------------
void Sudoku::Bug_1()
{
int r, c, rr, cc;
int num = 0;
for (r = 0; r < 9; ++r)
{
for (c = 0; c < 9; ++c)
{
if (m_anCell[r][c].n == 0)
{
}
else if (m_anCell[r][c].n == 2)
{
}
else if (m_anCell[r][c].n == 3)
{
rr = r;
cc = c;
++num;
if (num > 1) // 3个候选数的数格个数超过1个
{
return;
}
}
else // 其他情况, 不满足BUG数格
return;
}
}
if (num != 1) return;
// BUG + 1数格位置为 (r,c), 找出该数格要填的候选数
struct _ANBug_1
{
int num; // 候选数
int rowNum; // 该候选数在行上的个数
int colNum; // 该候选数在列上的个数
int squareNum; // 该候选数在Box上的个数
};
int i, j, k;
_ANBug_1 bug[3]; // 候选数为 3 个
memset(bug, 0, sizeof(bug));
for (i = 0; i < m_anCell[rr][cc].n; ++i)
{
bug[i].num = m_anCell[rr][cc].candi[i];
}
for (i = 0; i < 9; ++i)
{
for (j = 0; j < m_anCell[rr][i].n; ++j)
{
for (k = 0; k < 3; ++k)
{
if (m_anCell[rr][i].candi[j] == bug[k].num)
{
++bug[k].rowNum;
break;
}
}
}
for (j = 0; j < m_anCell[i][cc].n; ++j)
{
for (k = 0; k < 3; ++k)
{
if (m_anCell[i][cc].candi[j] == bug[k].num)
{
++bug[k].colNum;
break;
}
}
}
}
int m = (rr/3)*3 + (cc/3); // box的序号(0-8)
int _rowBegin = (m/3)*3;
int _rowEnd = _rowBegin + 3;
for (r = _rowBegin; r< _rowEnd; ++r)
{
int _colBegin = (m%3) * 3;
int _colEnd = _colBegin + 3;
for (c = _colBegin; c < _colEnd; ++c)
{
for (j = 0; j < m_anCell[r][c].n; ++j)
{
for (k = 0; k < 3; ++k)
{
if (m_anCell[r][c].candi[j] == bug[k].num)
{
++bug[k].squareNum;
break;
}
}
}
}
}
//
for (i = 0; i < 3; ++i)
{
if (bug[i].rowNum == bug[i].colNum && bug[i].rowNum == bug[i].squareNum
&& bug[i].rowNum == 3)
{
printf("BUG+1数格:(%d,%d), 必须填写候选数:%d/n", rr, cc, bug[i].num);
break;
}
// printf("候选数:%d rowNum %d colNum %d boxNum %d/n"
// , bug[i].num, bug[i].rowNum, bug[i].colNum, bug[i].squareNum);
}
}
void Sudoku::XY_Wing()
{
int arrCandi[2];
_CandiPt_Map mapCndiPt_row[2];
_CandiPt_Map mapCndiPt_col[2];
_CandiPt_Map mapCndiPt_box[2];
_CandiPt_Map::iterator _iCPLoop;
_CandiPt_Map::iterator _iCPTmp;
int r, c, rr, cc;
int i, j, m, k;
_Point pt;
_Point arrDelPt[5]; // 最多删除5个位置的候选数
for (r = 0; r < 9; ++r)
{
for (c = 0; c < 9; ++c)
{
if (m_anCell[r][c].n != 2)
continue;
arrCandi[0] = m_anCell[r][c].candi[0];
arrCandi[1] = m_anCell[r][c].candi[1];
// 在(r,c)XY的同行,不同box中查找XZ/YZ
for (i = 0; i < 9; ++i)
{
if (i/3 == c/3)
continue;
if (m_anCell[r][i].n != 2)
continue;
pt.r = r;
pt.c = i;
if (m_anCell[r][i].candi[0] == arrCandi[0])
{
mapCndiPt_row[0].insert(_CandiPt_Pair(m_anCell[r][i].candi[1], pt));
}
else if (m_anCell[r][i].candi[0] == arrCandi[1])
{
mapCndiPt_row[1].insert(_CandiPt_Pair(m_anCell[r][i].candi[1], pt));
}
else if (m_anCell[r][i].candi[1] == arrCandi[0])
{
mapCndiPt_row[0].insert(_CandiPt_Pair(m_anCell[r][i].candi[0], pt));
}
else if (m_anCell[r][i].candi[1] == arrCandi[1])
{
mapCndiPt_row[1].insert(_CandiPt_Pair(m_anCell[r][i].candi[0], pt));
}
}
// 在(r,c)XY的同列,不同box中查找YZ/XZ
for (i = 0; i < 9; ++i)
{
if (i/3 == r/3)
continue;
if (m_anCell[i][c].n != 2)
continue;
pt.r = i;
pt.c = c;
if (m_anCell[i][c].candi[0] == arrCandi[0])
{
mapCndiPt_col[0].insert(_CandiPt_Pair(m_anCell[i][c].candi[1], pt));
}
else if (m_anCell[r][i].candi[0] == arrCandi[1])
{
mapCndiPt_col[1].insert(_CandiPt_Pair(m_anCell[i][c].candi[1], pt));
}
else if (m_anCell[i][c].candi[1] == arrCandi[0])
{
mapCndiPt_col[0].insert(_CandiPt_Pair(m_anCell[i][c].candi[0], pt));
}
else if (m_anCell[r][i].candi[1] == arrCandi[1])
{
mapCndiPt_col[1].insert(_CandiPt_Pair(m_anCell[i][c].candi[0], pt));
}
}
// 在(r,c)的同一个box中查找XZ/YZ
m = (r/3)*3 + (c/3); // box的序号(0-8)
int _rowBegin = (m/3)*3;
int _rowEnd = _rowBegin + 3;
for (rr = _rowBegin; rr < _rowEnd; ++rr)
{
int _colBegin = (m%3) * 3;
int _colEnd = _colBegin + 3;
for (cc = _colBegin; cc < _colEnd; ++cc)
{
if (m_anCell[rr][cc].n != 2)
continue;
if (rr == r && cc == c)
continue;
pt.r = rr;
pt.c = cc;
if (m_anCell[rr][cc].candi[0] == arrCandi[0])
{
mapCndiPt_box[0].insert(_CandiPt_Pair(m_anCell[rr][cc].candi[1], pt));
}
else if (m_anCell[rr][cc].candi[0] == arrCandi[1])
{
mapCndiPt_box[1].insert(_CandiPt_Pair(m_anCell[rr][cc].candi[1], pt));
}
else if (m_anCell[rr][cc].candi[1] == arrCandi[0])
{
mapCndiPt_box[0].insert(_CandiPt_Pair(m_anCell[rr][cc].candi[0], pt));
}
else if (m_anCell[rr][cc].candi[1] == arrCandi[1])
{
mapCndiPt_box[1].insert(_CandiPt_Pair(m_anCell[rr][cc].candi[0], pt));
}
}
}
/*
// 输出,测试处理是否正确
printf("/n-----------/nmapCndiPt_row[0] val:%d (%d,%d)/n", arrCandi[0], r, c);
for (_iCPLoop = mapCndiPt_row[0].begin(); _iCPLoop != mapCndiPt_row[0].end(); ++_iCPLoop)
{
printf("[%d](%d,%d) ", _iCPLoop->first, _iCPLoop->second.r,
_iCPLoop->second.c);
}
printf("/n");
*/
// 1, XY -- XZ 同行, 不同Box; XY -- YZ 同Box, 不同行
_CandiPt_Map *pM1;
_CandiPt_Map *pM2;
int nLoop = 0;
do
{
if (nLoop == 0)
{
pM1 = &mapCndiPt_row[0];
pM2 = &mapCndiPt_box[1];
}
else if (nLoop == 1)
{
pM1 = &mapCndiPt_row[1];
pM2 = &mapCndiPt_box[0];
}
++nLoop;
for (_iCPLoop = pM1->begin(); _iCPLoop != pM1->end(); ++_iCPLoop)
{
_iCPTmp = pM2->find(_iCPLoop->first);
if (_iCPTmp != pM2->end())
{
k = 0;
_Point & p1 = _iCPLoop->second;
_Point & p2 = _iCPTmp->second;
if (p2.r == p1.r) continue;
int _colBegin = (p1.c/3) * 3;
int _colEnd = _colBegin + 3;
for (cc = _colBegin; cc < _colEnd; ++cc)
{
for (j = 0; j < m_anCell[p2.r][cc].n; ++j)
{
if (m_anCell[p2.r][cc].candi[j] == _iCPTmp->first)
{
arrDelPt[k].r = p2.r;
arrDelPt[k].c = cc;
++k;
break;
}
}
}
_colBegin = (p2.c/3) * 3;
_colEnd = _colBegin + 3;
for (cc = _colBegin; cc < _colEnd; ++cc)
{
for (j = 0; j < m_anCell[p1.r][cc].n; ++j)
{
if (m_anCell[p1.r][cc].candi[j] == _iCPTmp->first)
{
arrDelPt[k].r = p1.r;
arrDelPt[k].c = cc;
++k;
break;
}
}
}
if (k <= 0) continue;
printf("XY-Wing %d%d(%d,%d) Z:%d (%d,%d)(%d,%d) ",
arrCandi[0], arrCandi[1],
r, c,
_iCPTmp->first,
_iCPLoop->second.r, _iCPLoop->second.c,
_iCPTmp->second.r, _iCPTmp->second.c);
printf("删除候选数%d位置:", _iCPTmp->first);
for (i = 0; i < k; ++i)
{
printf("(%d,%d) ", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
} while (nLoop < 2);
// 2, XY -- XZ 同列, 不同Box; XY -- YZ 同Box, 不同列
nLoop = 0;
do
{
if (nLoop == 0)
{
pM1 = &mapCndiPt_col[0];
pM2 = &mapCndiPt_box[1];
}
else if (nLoop == 1)
{
pM1 = &mapCndiPt_col[1];
pM2 = &mapCndiPt_box[0];
}
++nLoop;
for (_iCPLoop = pM1->begin(); _iCPLoop != pM1->end(); ++_iCPLoop)
{
_iCPTmp = pM2->find(_iCPLoop->first);
if (_iCPTmp != pM2->end())
{
k = 0;
_Point & p1 = _iCPLoop->second;
_Point & p2 = _iCPTmp->second;
if (p2.c == p1.c) continue;
int _rowBegin = (p1.r/3) * 3;
int _rowEnd = _rowBegin + 3;
for (rr = _rowBegin; rr < _rowEnd; ++rr)
{
for (j = 0; j < m_anCell[rr][p2.c].n; ++j)
{
if (m_anCell[rr][p2.c].candi[j] == _iCPTmp->first)
{
arrDelPt[k].r = rr;
arrDelPt[k].c = p2.c;
++k;
break;
}
}
}
_rowBegin = (p1.r/3) * 3;
_rowEnd = _rowBegin + 3;
for (cc = _rowBegin; cc < _rowEnd; ++cc)
{
for (j = 0; j < m_anCell[rr][p1.c].n; ++j)
{
if (m_anCell[rr][p1.c].candi[j] == _iCPTmp->first)
{
arrDelPt[k].r = rr;
arrDelPt[k].c = p1.c;
++k;
break;
}
}
}
if (k <= 0) continue;
printf("XY-Wing %d%d(%d,%d) Z:%d (%d,%d)(%d,%d) ",
arrCandi[0], arrCandi[1],
r, c,
_iCPTmp->first,
_iCPLoop->second.r, _iCPLoop->second.c,
_iCPTmp->second.r, _iCPTmp->second.c);
printf("删除候选数%d位置:", _iCPTmp->first);
for (i = 0; i < k; ++i)
{
printf("(%d,%d) ", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
} while (nLoop < 2);
// 3, XY -- XZ 同行, 不同Box; XY -- YZ 同列, 不同Box
nLoop = 0;
do
{
if (nLoop == 0)
{
pM1 = &mapCndiPt_row[0];
pM2 = &mapCndiPt_col[1];
}
else if (nLoop == 1)
{
pM1 = &mapCndiPt_row[1];
pM2 = &mapCndiPt_col[0];
}
++nLoop;
for (_iCPLoop = pM1->begin(); _iCPLoop != pM1->end(); ++_iCPLoop)
{
_iCPTmp = pM2->find(_iCPLoop->first);
if (_iCPTmp != pM2->end())
{
k = 0;
_Point & p1 = _iCPLoop->second;
_Point & p2 = _iCPTmp->second;
// 这种类型, 只能消除一个位置的候选数
for (j = 0; j < m_anCell[p2.r][p1.c].n; ++j)
{
if (m_anCell[p2.r][p1.c].candi[j] == _iCPTmp->first)
{
arrDelPt[k].r = p2.r;
arrDelPt[k].c = p1.c;
++k;
break;
}
}
if (k <= 0) continue;
printf("XY-Wing %d%d(%d,%d) Z:%d (%d,%d)(%d,%d) ",
arrCandi[0], arrCandi[1],
r, c,
_iCPTmp->first,
_iCPLoop->second.r, _iCPLoop->second.c,
_iCPTmp->second.r, _iCPTmp->second.c);
printf("删除候选数%d位置:", _iCPTmp->first);
for (i = 0; i < k; ++i)
{
printf("(%d,%d) ", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
} while (nLoop < 2);
mapCndiPt_row[0].clear();
mapCndiPt_row[1].clear();
mapCndiPt_col[0].clear();
mapCndiPt_col[1].clear();
mapCndiPt_box[0].clear();
mapCndiPt_box[1].clear();
}
}
}
void Sudoku::XYZ_Wing()
{
bool bSubSet;
_Candi_Set setCandi;
_Candi_Set setCandiTmp;
_Candi_Set::const_iterator _iSet;
_CandiSetPt_Map candiSetPt_row;
_CandiSetPt_Map candiSetPt_box;
_CandiSetPt_Map candiSetPt_col;
_CandiSetPt_Map::iterator iSetPt;
_CandiSetPt_Map::iterator iSetPtTmp;
_Point pt;
_Point arrDelPt[2]; // 最多可删除候选数的位置个数
int r, c;
int i, j, k;
for (r = 0; r < 9; ++r)
{
for (c = 0; c < 9; ++c)
{
if (m_anCell[r][c].n != 3)
continue;
setCandi.clear();
for (i = 0; i < m_anCell[r][c].n; ++i)
{
setCandi.insert(m_anCell[r][c].candi[i]);
}
// 查找setCandi的子集(2数子集)
// 在(r,c)的同行,不同box中查找子集
for (i = 0; i < 9; ++i)
{
if (i/3 == c/3)
continue;
if (m_anCell[r][i].n != 2)
continue;
pt.r = r;
pt.c = i;
setCandiTmp.clear();
bSubSet = true;
for (j = 0; j < m_anCell[r][i].n; ++j)
{
_iSet = setCandi.find(m_anCell[r][i].candi[j]);
if (_iSet == setCandi.end())
{
bSubSet = false;
break;
}
setCandiTmp.insert(m_anCell[r][i].candi[j]);
}
if (!bSubSet)
continue;
// setCandiTmp 是 setCandi 的子集
candiSetPt_row.insert(_CandiSetPt_Pair(setCandiTmp, pt));
}
// 在(r,c)的同一个box中查找 子集
int m = (r/3)*3 + (c/3); // box的序号(0-8)
int _rowBegin = (m/3)*3;
int _rowEnd = _rowBegin + 3;
int rr, cc;
for (rr = _rowBegin; rr < _rowEnd; ++rr)
{
int _colBegin = (m%3) * 3;
int _colEnd = _colBegin + 3;
for (cc = _colBegin; cc < _colEnd; ++cc)
{
if (m_anCell[rr][cc].n != 2)
continue;
if (rr == r && cc == c)
continue;
pt.r = rr;
pt.c = cc;
setCandiTmp.clear();
bSubSet = true;
for (j = 0; j < m_anCell[rr][cc].n; ++j)
{
_iSet = setCandi.find(m_anCell[rr][cc].candi[j]);
if (_iSet == setCandi.end())
{
bSubSet = false;
break;
}
setCandiTmp.insert(m_anCell[rr][cc].candi[j]);
}
if (!bSubSet)
continue;
// setCandiTmp 是 setCandi 的子集
candiSetPt_box.insert(_CandiSetPt_Pair(setCandiTmp, pt));
}
}
// 找出 row 和 box 可以组成 XYZ的两个集合
for (iSetPt = candiSetPt_row.begin(); iSetPt != candiSetPt_row.end(); ++iSetPt)
{
iSetPtTmp = candiSetPt_box.begin();
for (; iSetPtTmp != candiSetPt_box.end(); ++iSetPtTmp)
{
k = 0;
_Point & p1 = iSetPt->second;
_Point & p2 = iSetPtTmp->second;
if (p1.r == p2.r) // 同行
continue;
_Candi_Set setI;
set_intersection(iSetPt->first.begin(), iSetPt->first.end(),
iSetPtTmp->first.begin(), iSetPtTmp->first.end(),
inserter(setI, setI.begin()) );
if (setI.size() != 1) // 交集不是一个元素
continue;
int _colBegin = (p2.c/3) * 3;
int _colEnd = _colBegin + 3;
for (cc = _colBegin; cc < _colEnd; ++cc)
{
int r1 = p1.r;
int c1 = cc;
if (r1 == r && c1 == c) // 过滤 XYZ 所在位置
continue;
for (j = 0; j < m_anCell[r1][c1].n; ++j)
{
if (m_anCell[r1][c1].candi[j] == *setI.begin())
{
arrDelPt[k].r = r1;
arrDelPt[k].c = c1;
++k;
break;
}
}
}
if (k <= 0) continue;
printf("XYZ-Wing (%d,%d) Z:%d (%d,%d)(%d,%d) ",
r, c,
*setI.begin(),
p1.r, p1.c,
p2.r, p2.c);
printf("删除候选数%d位置:", *setI.begin());
for (i = 0; i < k; ++i)
{
printf("(%d,%d) ", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
// 在(r,c)XYZ的同列,不同box中查找 子集
for (i = 0; i < 9; ++i)
{
if (i/3 == r/3)
continue;
if (m_anCell[i][c].n != 2)
continue;
pt.r = i;
pt.c = c;
setCandiTmp.clear();
bSubSet = true;
for (j = 0; j < m_anCell[i][c].n; ++j)
{
_iSet = setCandi.find(m_anCell[i][c].candi[j]);
if (_iSet == setCandi.end())
{
bSubSet = false;
break;
}
setCandiTmp.insert(m_anCell[i][c].candi[j]);
}
if (!bSubSet)
continue;
// setCandiTmp 是 setCandi 的子集
candiSetPt_col.insert(_CandiSetPt_Pair(setCandiTmp, pt));
}
// 找出 col 和 box 可以组成 XYZ的两个集合
for (iSetPt = candiSetPt_col.begin(); iSetPt != candiSetPt_col.end(); ++iSetPt)
{
iSetPtTmp = candiSetPt_box.begin();
for (; iSetPtTmp != candiSetPt_box.end(); ++iSetPtTmp)
{
k = 0;
_Point & p1 = iSetPt->second;
_Point & p2 = iSetPtTmp->second;
if (p1.c == p2.c) // 同列
continue;
_Candi_Set setI;
set_intersection(iSetPt->first.begin(), iSetPt->first.end(),
iSetPtTmp->first.begin(), iSetPtTmp->first.end(),
inserter(setI, setI.begin()) );
if (setI.size() != 1) // 交集不是一个元素
continue;
int _rowBegin = (p2.r/3) * 3;
int _rowEnd = _rowBegin + 3;
for (rr = _rowBegin; rr < _rowEnd; ++rr)
{
int r1 = rr;
int c1 = p1.c;
if (r1 == r && c1 == c) // 过滤 XYZ 所在位置
continue;
for (j = 0; j < m_anCell[r1][c1].n; ++j)
{
if (m_anCell[r1][c1].candi[j] == *setI.begin())
{
arrDelPt[k].r = r1;
arrDelPt[k].c = c1;
++k;
break;
}
}
}
if (k <= 0) continue;
printf("XYZ-Wing (%d,%d) Z:%d (%d,%d)(%d,%d) ",
r, c,
*setI.begin(),
p1.r, p1.c,
p2.r, p2.c);
printf("删除候选数%d位置:", *setI.begin());
for (i = 0; i < k; ++i)
{
printf("(%d,%d) ", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
candiSetPt_row.clear();
candiSetPt_col.clear();
candiSetPt_box.clear();
}
}
}
// Turbotfish 多宝鱼(比目鱼)
void Sudoku::Turbotfish()
{
int r, c, i, j, k;
_Point point;
list<_Point> lstPt;
list<_Point>::iterator _iLstPt;
_CandiCnt_Map mapCandi; // 这里用作 行或者列 与 位置信息对应表
_CandiCnt_Map::iterator _itCandi;
_CandiCnt_Map::iterator _itCandiSecond;
_Candi_rcP_Map rcp; // 候选数 - ( 行/列 - 位置列表) 对应表(二级map)
_Candi_rcP_Map::iterator _ircp;
_Point arrDelPt[5];
_Point arr1[2];
_Point arr2[2];
_Point ptOut[4];
bool bRet;
int n = 2;
if (n < 2 || n > 4)
return;
// 按照行, 整理候选数
for (r = 0; r < 9; ++r)
{
for (c = 0; c < 9; ++c)
{
point.r = 0; // row 用作他用
point.c = c;
ANSudokuCell &cell = m_anCell[r][c];
for (i = 0; i < cell.n; ++i)
{
point.r = cell.candi[i]; // row 用来记录候选数
_ircp = rcp.find(cell.candi[i]);
if (_ircp == rcp.end())
{
lstPt.push_back(point);
mapCandi.insert(_CandiCnt_Pair(r, lstPt));
rcp.insert(_Candi_rcP_Pair(cell.candi[i], mapCandi));
lstPt.clear();
mapCandi.clear();
}
else
{
_itCandi = _ircp->second.find(r);
if (_itCandi == _ircp->second.end())
{
lstPt.push_back(point);
_ircp->second.insert(_CandiCnt_Pair(r, lstPt));
lstPt.clear();
}
else // 相同候选数, 相同行, 增加位置坐标即可
{
_itCandi->second.push_back(point);
}
}
}
}
}
// 查看行方向的 Trubotfish
for (_ircp = rcp.begin(); _ircp != rcp.end(); ++ _ircp)
{
for (_itCandi=_ircp->second.begin(); _itCandi!=_ircp->second.end(); ++_itCandi)
{
// 只有一个位置时, 为 HiddenCandidates , 前面的查找函数找过了
if (_itCandi->second.size() == 1 || _itCandi->second.size() > n)
continue;
_iLstPt = _itCandi->second.begin();
arr1[0] = *_iLstPt++;
arr1[1] = *_iLstPt++;
arr1[0].r = _itCandi->first;
arr1[1].r = arr1[0].r;
_itCandiSecond = _itCandi;
++_itCandiSecond;
for (; _itCandiSecond != _ircp->second.end(); ++_itCandiSecond)
{
// 只有一个位置时, 为 HiddenCandidates , 前面的查找函数找过了
if (_itCandiSecond->second.size() == 1 || _itCandiSecond->second.size() > n)
continue;
_iLstPt = _itCandiSecond->second.begin();
arr2[0] = *_iLstPt++;
arr2[1] = *_iLstPt++;
arr2[0].r = _itCandiSecond->first;
arr2[1].r = arr2[0].r;
bRet = MergeTurbot(true, arr1, arr2, ptOut);
if (!bRet) continue;
// 看是否有可以消除的候选数
k = 0;
// 不在同一个box中
_Point &p3 = ptOut[2];
_Point &p4 = ptOut[3];
if (p3.r/3 != p4.r/3 && p3.c/3 == p4.c/3)
{
// --p1-----------p3---
//
// --p2------p4--------
//
// p3 行的范围, p4的列
int nBegin = p3.r/3 * 3;
int nEnd = nBegin + 3;
for (j = nBegin; j < nEnd; ++j)
{
int r1 = j;
int c1 = p4.c;
for (i = 0; i < m_anCell[r1][c1].n; ++i) // 看是否有可以消除的候选数
{
if (m_anCell[r1][c1].candi[i] == _ircp->first) // 候选数
{
arrDelPt[k].r = r1;
arrDelPt[k].c = c1;
++k;
break;
}
}
}
// p3 的列, p4的行范围
nBegin = p4.r/3 * 3;
nEnd = nBegin + 3;
for (j = nBegin; j < nEnd; ++j)
{
int r1 = j;
int c1 = p3.c;
for (i = 0; i < m_anCell[r1][c1].n; ++i) // 看是否有可以消除的候选数
{
if (m_anCell[r1][c1].candi[i] == _ircp->first) // 候选数
{
arrDelPt[k].r = r1;
arrDelPt[k].c = c1;
++k;
break;
}
}
}
}
if (k <= 0) continue;
printf("Turbotfish(%d)-R, bottom(%d,%d)(%d,%d), top(%d,%d)(%d,%d) ",
_ircp->first, // 候选数
ptOut[0].r, ptOut[0].c,
ptOut[1].r, ptOut[1].c,
ptOut[2].r, ptOut[2].c,
ptOut[3].r, ptOut[3].c
);
printf("删除候选数%d位置:", _ircp->first);
for (i = 0; i < k; ++i)
{
printf("(%d,%d)", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
}
rcp.clear();
//
// 按照列, 整理候选数
for (c = 0; c < 9; ++c)
{
for (r = 0; r < 9; ++r)
{
point.r = 0; // row 用作他用
point.c = r;
ANSudokuCell &cell = m_anCell[r][c];
for (i = 0; i < cell.n; ++i)
{
point.r = cell.candi[i]; // row 用来记录候选数
_ircp = rcp.find(cell.candi[i]);
if (_ircp == rcp.end())
{
lstPt.push_back(point);
mapCandi.insert(_CandiCnt_Pair(c, lstPt));
rcp.insert(_Candi_rcP_Pair(cell.candi[i], mapCandi));
lstPt.clear();
mapCandi.clear();
}
else
{
_itCandi = _ircp->second.find(c);
if (_itCandi == _ircp->second.end())
{
lstPt.push_back(point);
_ircp->second.insert(_CandiCnt_Pair(c, lstPt));
lstPt.clear();
}
else // 相同候选数, 相同行, 增加位置坐标即可
{
_itCandi->second.push_back(point);
}
}
}
}
}
// 查看列方向的 Turbotfish
for (_ircp = rcp.begin(); _ircp != rcp.end(); ++ _ircp)
{
for (_itCandi=_ircp->second.begin(); _itCandi!=_ircp->second.end(); ++_itCandi)
{
// 只有一个位置时, 为 HiddenCandidates , 前面的查找函数找过了
if (_itCandi->second.size() == 1 || _itCandi->second.size() > n)
continue;
list<_Point> &lst1 = _itCandi->second;
_iLstPt = _itCandi->second.begin();
arr1[0] = *_iLstPt++;
arr1[1] = *_iLstPt++;
arr1[0].r = arr1[0].c; // c 记录 row
arr1[1].r = arr1[1].c; // c 记录 row
arr1[0].c = _itCandi->first;
arr1[1].c = arr1[0].c;
_itCandiSecond = _itCandi;
++_itCandiSecond;
for (; _itCandiSecond != _ircp->second.end(); ++_itCandiSecond)
{
// 只有一个位置时, 为 HiddenCandidates , 前面的查找函数找过了
if (_itCandiSecond->second.size() == 1 || _itCandiSecond->second.size() > n)
continue;
list<_Point> &lst2 = _itCandiSecond->second;
_iLstPt = _itCandiSecond->second.begin();
arr2[0] = *_iLstPt++;
arr2[1] = *_iLstPt++;
arr2[0].r = arr2[0].c; // c 记录 row
arr2[1].r = arr2[1].c; // c 记录 row
arr2[0].c = _itCandiSecond->first;
arr2[1].c = arr2[0].c;
bRet = MergeTurbot(false, arr1, arr2, ptOut);
if (!bRet) continue;
// 看是否有可以消除的候选数
k = 0;
// 不在同一个box中
_Point &p3 = ptOut[2];
_Point &p4 = ptOut[3];
if (p3.r/3 == p4.r/3 && p3.c/3 != p4.c/3)
{
// p3 列的范围, p4的行
int nBegin = p3.c/3 * 3;
int nEnd = nBegin + 3;
for (j = nBegin; j < nEnd; ++j)
{
int r1 = p4.r;
int c1 = j;
for (i = 0; i < m_anCell[r1][c1].n; ++i) // 看是否有可以消除的候选数
{
if (m_anCell[r1][c1].candi[i] == _ircp->first) // 候选数
{
arrDelPt[k].r = r1;
arrDelPt[k].c = c1;
++k;
break;
}
}
}
// p3 的行, p4的列范围
nBegin = p4.c/3 * 3;
nEnd = nBegin + 3;
for (j = nBegin; j < nEnd; ++j)
{
int r1 = p3.r;
int c1 = j;
for (i = 0; i < m_anCell[r1][c1].n; ++i) // 看是否有可以消除的候选数
{
if (m_anCell[r1][c1].candi[i] == _ircp->first) // 候选数
{
arrDelPt[k].r = r1;
arrDelPt[k].c = c1;
++k;
break;
}
}
}
}
if (k <= 0) continue;
printf("Turbotfish(%d)-C, bottom(%d,%d)(%d,%d), top(%d,%d)(%d,%d) ",
_ircp->first, // 候选数
ptOut[0].r, ptOut[0].c,
ptOut[1].r, ptOut[1].c,
ptOut[2].r, ptOut[2].c,
ptOut[3].r, ptOut[3].c
);
printf("删除候选数%d位置:", _ircp->first);
for (i = 0; i < k; ++i)
{
printf("(%d,%d)", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
}
}
}
rcp.clear();
}
int Sudoku::TrySolve()
{
int r, c;
// 显式唯一候选数
SingleCandidature(r, c, false);
HiddenSingleCandi(r, c, false);
LockedCandidates();
NakedNumSet(2); // Naked Pair
NakedNumSet(3);
NakedNumSet(4);
HiddenNumSet(2); // Pair
HiddenNumSet(3); // Triple
HiddenNumSet(4); // Quad
X_Wing();
X_Wing(3); // Swordfish
X_Wing(4); // Jellyfish
UniqueRectangle();
XY_Wing();
XYZ_Wing();
Turbotfish();
Bug_1(); // BUG + 1
return 0;
}
/******************************************************************************
* 函 数 名: GetInterSection
* 功能描述: 获取p1和p2点可以共同看到的位置
* 输 入: p1 : 位置1
* p2 : 位置2
* n : arrOut数组可存放元素的个数
* 输 出: arrOut : p1和p2共同看到的位置
* 返 回 值: arrOut数组中元素的个数.
* 作 者: anning
* 日 期: 2010年7月5日
* 版 本: V2.0
* 修改记录:
* 日 期 版本 修改人 修改摘要
*****************************************************************************/
int Sudoku::GetInterSection(_Point &p1, _Point &p2, int n, _Point arrOut[])
{
int r11 = p1.r/3 * 3;
int r12 = r11 + 3;
int c11 = p1.c/3 * 3;
int c12 = c11 + 3;
int r21 = p2.r/3 * 3;
int r22 = r21 + 3;
int c21 = p2.c/3 * 3;
int c22 = c21 + 3;
int r, c;
int k = 0;
if (p1.r/3 == p2.r/3 && p1.c/3 == p2.c/3) // 在一个box中
{
for (r = r11; r < r12; ++r)
{
for (c = c11; c < c12; ++c)
{
if (r == p1.r && c == p1.c || r == p2.r && c == p2.c)
continue;
if(k < n)
{
arrOut[k].r = r;
arrOut[k].c = c;
}
++k;
}
}
}
else if (p1.r/3 == p2.r/3 && p1.c/3 != p2.c/3) // 两个box在同一行
{
r = p2.r;
for (c = c11; c < c12; ++c)
{
if (r == p1.r && c == p1.c)
continue;
if(k < n)
{
arrOut[k].r = r;
arrOut[k].c = c;
}
++k;
}
r = p1.r;
for (c = c21; c < c22; ++c)
{
if (r == p2.r && c == p2.c)
continue;
if(k < n)
{
arrOut[k].r = r;
arrOut[k].c = c;
}
++k;
}
}
else if (p1.r/3 != p2.r/3 && p1.c/3 == p2.c/3) // 两个box在同一列
{
c = p2.c;
for (r = r11; r < r12; ++r)
{
if(c == p1.c && r == p1.r)
continue;
if(k < n)
{
arrOut[k].r = r;
arrOut[k].c = c;
}
++k;
}
c = p1.c;
for (r = r21; r < r22; ++r)
{
if (c == p2.c && r == p2.r)
continue;
if(k < n)
{
arrOut[k].r = r;
arrOut[k].c = c;
}
++k;
}
}
else // 两个不同的box中, 仅有两个交点
{
if(k < n)
{
arrOut[k].r = p1.r;
arrOut[k].c = p2.c;
}
++k;
if(k < n)
{
arrOut[k].r = p2.r;
arrOut[k].c = p1.c;
}
++k;
}
if (p1.r == p2.r) // 同行
{
for (c = 0; c < 9; ++c)
{
if (c >= c11&&c < c12 || c >= c21&&c < c22)
continue;
if(k < n)
{
arrOut[k].r = p1.r;
arrOut[k].c = c;
}
++k;
}
}
else if (p1.c == p2.c) // 同列
{
for (r = 0; r < 9; ++r)
{
if (r >= r11 && r < r12 || r>=r21&&r<r22)
continue;
if(k < n)
{
arrOut[k].r = r;
arrOut[k].c = p1.c;
}
++k;
}
}
return k;
}
// Unique Rectangle (UR)
void Sudoku::UniqueRectangle()
{
ANSudokuCell anCell[9][9];
memcpy(anCell, m_anCell, sizeof(anCell));
int num;
int v[4]; // Unique SubSet 使用
int i, j, o;
int r, c;
for (r = 0; r < 9; ++r)
{
for (c = 0; c < 9; ++c)
{
ANSudokuCell & cell = anCell[r][c];
if (cell.n != 2) continue;
// 找到 A点 (r,c) 候选数x,y
// 在 r,c 所在box, 查找包含 x,y的数格
int m = (r/3)*3 + (c/3); // box的序号(0-8)
int rowBegin = r/3*3;
int rowEnd = rowBegin + 3;
for (int r1 = rowBegin; r1 < rowEnd; ++r1)
{
int colBegin = c/3*3;
int colEnd = colBegin + 3;
for (int c1 = colBegin; c1 < colEnd; ++c1)
{
ANSudokuCell &cellB = anCell[r1][c1];
if (r1 == r && c1 == c) continue; // 重复位置
// 如果 位置 B 不包含A, 则AB不会构造矩形的边
if (!IsInclude(cellB, cell)) continue;
// A B 两点同行或者同列才可以构成矩形的边
if (r1 != r && c1 != c) continue;
// 找出和A,B不在同一个box的点C,D
int r3, c3; // C
int r4, c4; // D
for (o = 0; o < 9; ++o)
{
/** A(r,c) B(r1,c1) r1 == r A,B same box
* ----------------
*
*
*
* C(r3,c3) D(r4,c4)
*/
if (r1 == r)
{
if (o/3 == r/3) continue;
r3 = o;
c3 = c;
r4 = o;
c4 = c1;
}
else
{
/** A(r,c) | C(r3,c3)
* |
* B(r1,c1)| D(r4,c4)
*
* c1 == c A,B same box
*/
if (o/3 == c/3) continue;
r3 = r;
c3 = o;
r4 = r1;
c4 = o;
}
ANSudokuCell &cellC = anCell[r3][c3];
ANSudokuCell &cellD = anCell[r4][c4];
if (!IsInclude(cellC, cell) || !IsInclude(cellD, cell))
continue;
// 找到了矩形A,B,C,D, 还需看是否满足矩形的消除条件
int rr, cc;
ANSudokuCell * pCell = NULL;
if (cellB.n == 2 && cellC.n == 2)
{
pCell = &cellD;
rr = r4;
cc = c4;
}
else if (cellB.n == 2 && cellD.n == 2)
{
pCell = &cellC;
rr = r3;
cc = c3;
}
else if (cellC.n == 2 && cellD.n == 2)
{
pCell = &cellB;
rr = r1;
cc = c1;
}
if (pCell != NULL) // UR1(Unique Corner)
{
printf("UR1(Unique Corner)[%d%d], r%d%d, c%d%d 删除[r%dc%d]位置的%d,%d/n"
,cell.candi[0], cell.candi[1]
,r, r4
,c, c4
,rr, cc
,cell.candi[0], cell.candi[1]
);
continue;
}
if (cellB.n == 2)
{
_Point arrOut[15];
_Point p1 = {r3, c3}; // C
_Point p2 = {r4, c4}; // D
// C, D是否仅含有一个相同的额外候选数
if (cellC.n == 3 && cellD.n == 3 && IsInclude(cellC, cellD))
{
int a;
Subtract(cellD, cell, &a);
num = GetInterSection(p1, p2, 15, arrOut);
_Point arrDelPt[15];
int k = 0;
for (i = 0; i < num; ++i)
{
ANSudokuCell &cTmp = m_anCell[arrOut[i].r][arrOut[i].c];
for (j = 0; j < cTmp.n; ++j)
{
if (cTmp.candi[j] == a)
{
arrDelPt[k] = arrOut[i];
++k;
break;
}
}
}
if (k <=0) continue;
printf("UR2(Unique Side)[%d%d], r%d%d, c%d%d "
,cell.candi[0], cell.candi[1]
,r, r4 // A, D 对角
,c, c4
);
printf("删除候选数%d位置:", a);
for (i = 0; i < k; ++i)
{
printf("[r%dc%d]", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
continue;
}
v[0] = cell.candi[0];
v[1] = cell.candi[1];
num = UniqueSubset(p1, p2, arrOut, v);
if (num > 0)
{
printf("UR3(Unique Subset)[%d%d], r%d%d, c%d%d 数集"
,cell.candi[0], cell.candi[1]
,r, r4 // A, D 对角
,c, c4
);
for (i = 0; i < 4; ++i)
{
if (v[i] == 0)
{
break;
}
printf("%d", v[i]);
}
printf(" 删除候选数的位置:");
for (i = 0; i < num; ++i)
{
printf("[r%dc%d]", arrOut[i].r, arrOut[i].c);
}
printf("/n");
continue;
}
// UR4(Unique Pair) 查找
int nCandi = UniquePair(cell, p1, p2);
if (nCandi !=0)
{
printf("UR4(Unique Pair)[%d%d], r%d%d, c%d%d "
,cell.candi[0], cell.candi[1]
,r, r4 // A, D 对角
,c, c4
);
printf("删除候选数%d位置:[r%dc%d][r%dc%d]/n"
, nCandi, p1.r, p1.c, p2.r, p2.c);
continue;
}
}
else if (cellC.n == 2)
{
_Point arrOut[15];
_Point p1 = {r1, c1}; // B
_Point p2 = {r4, c4}; // D
// B, D是否仅含有一个相同的额外候选数
if (cellB.n == 3 && cellD.n == 3 && IsInclude(cellB, cellD))
{
int a;
Subtract(cellD, cell, &a);
num = GetInterSection(p1, p2, 15, arrOut);
_Point arrDelPt[15];
int k = 0;
for (i = 0; i < num; ++i)
{
ANSudokuCell &cTmp = m_anCell[arrOut[i].r][arrOut[i].c];
for (j = 0; j < cTmp.n; ++j)
{
if (cTmp.candi[j] == a)
{
arrDelPt[k] = arrOut[i];
++k;
break;
}
}
}
if (k <=0) continue;
printf("UR2(Unique Side)[%d%d], r%d%d, c%d%d "
,cell.candi[0], cell.candi[1]
,r, r4
,c, c4
);
printf("删除候选数%d位置:", a);
for (i = 0; i < k; ++i)
{
printf("[r%dc%d]", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
continue;
}
v[0] = cell.candi[0];
v[1] = cell.candi[1];
num = UniqueSubset(p1, p2, arrOut, v);
if (num > 0)
{
printf("UR3(Unique Subset)[%d%d], r%d%d, c%d%d 数集"
,cell.candi[0], cell.candi[1]
,r, r4 // A, D 对角
,c, c4
);
for (i = 0; i < 4; ++i)
{
if (v[i] == 0)
{
break;
}
printf("%d", v[i]);
}
printf(" 删除候选数的位置:");
for (i = 0; i < num; ++i)
{
printf("[r%dc%d]", arrOut[i].r, arrOut[i].c);
}
printf("/n");
continue;
}
// UR4(Unique Pair) 查找
int nCandi = UniquePair(cell, p1, p2);
if (nCandi !=0)
{
printf("UR4(Unique Pair)[%d%d], r%d%d, c%d%d "
,cell.candi[0], cell.candi[1]
,r, r4 // A, D 对角
,c, c4
);
printf("删除候选数%d位置:[r%dc%d][r%dc%d]/n"
, nCandi, p1.r, p1.c, p2.r, p2.c);
continue;
}
}
_Point ptA = {r, c};
_Point ptB = {r1, c1};
_Point ptC = {r3, c3};
_Point ptD = {r4, c4};
// UR5 B,C,D都只有一个额外候选数, 或者仅B,C有相同的一个额外候选数
// 则消除他们共同看到的数格上的该候选数
int nRslt = UR5(ptA, ptB, ptC, ptD);
if (1 == nRslt) // 找到UR5
{
printf("UR5(%d%d), r%d%d, c%d%d/n"
,cell.candi[0], cell.candi[1]
,r, r4
,c, c4
);
continue;
}
nRslt = UR6(ptA, ptB, ptC, ptD);
if (1 == nRslt) // 找到UR6
{
continue;
}
nRslt = UR7(ptA, ptB, ptC, ptD);
if (1 == nRslt) // 找到UR7
{
continue;
}
}
}
}
cell.n = 0; // 以后不需要利用该点A(r,c)构成矩形
}
}
}
// c1 的候选数列表 是否包含c2的候选数列表
bool Sudoku::IsInclude(ANSudokuCell & c1, ANSudokuCell & c2)
{
int i,j;
for (i = 0; i < c2.n; ++i)
{
bool bHas = false;
for (j = 0; j < c1.n; ++j)
{
if (c1.candi[j] == c2.candi[i])
{
bHas = true;
break;
}
}
if (!bHas)
{
return false;
}
}
return true;
}
// 取 c1 - c2 剩余的候选数
int Sudoku::Subtract(ANSudokuCell & c1, ANSudokuCell & c2, int arrOut[])
{
int i,j,k = 0;
for (i = 0; i < c1.n; ++i)
{
bool bHas = false;
for (j = 0; j < c2.n; ++j)
{
if (c1.candi[i] == c2.candi[j])
{
bHas = true;
break;
}
}
if (!bHas)
{
arrOut[k++] = c1.candi[i];
}
}
return k;
}
// 判断 p3, p4所在行或者列 是否仅包含cellA中某一个候选数, 如是, 通过返回值返回
// 该候选数; 否则返回0
int Sudoku::UniquePair(ANSudokuCell &cellA, _Point &p3, _Point &p4)
{
bool bFirst = false; // 第一个候选数是否在其他(除了p3,p4点)位置存在
bool bSecond = false;
_Point arrOut[15];
int num = GetInterSection(p3, p4, 15, arrOut);
for (int i = 0; i < num; ++i)
{
// 以下判断理论上可以不加
if (p3.r == p4.r)
{
if (arrOut[i].r != p3.r)
{
continue;
}
}
else
{
if (arrOut[i].c != p3.c)
{
continue;
}
}
ANSudokuCell &cTmp = m_anCell[arrOut[i].r][arrOut[i].c];
for (int j = 0; j < cTmp.n; ++j)
{
if (!bFirst && cTmp.candi[j] == cellA.candi[0])
{
bFirst = true;
break;
}
else if (!bSecond && cTmp.candi[j] == cellA.candi[1])
{
bSecond = true;
break;
}
}
if (bFirst && bSecond)
{
break;
}
}
if (!bFirst) // 第一个位置只有两个
{
return cellA.candi[1];
}
else if (!bSecond)
{
return cellA.candi[0];
}
return 0;
}
// Unique Rectangle 3
// p1和p2点作为一个虚拟单元格, 利用显式数集进行候选数消除,
// 能消除候选数的位置通过 arrDelPt 输出, 通过v输出这个数集,
// 返回值为可以消除位置的个数, 如果构不成数集或者无候选数消除,返回0
// ---p1和p2需要排除的两个数字, 通过v传入------------
// 显式 数对(Naked Pair), 3数集(Naked Triple), 4数集(Naked Quad)
int Sudoku::UniqueSubset(_Point &p1, _Point &p2, _Point arrDelPt[13], int v[4])
{
int i, j, k, m;
bool bSamePt, bValEqual;
ANSetPoint anPt;
_Point point;
_Point arrOut[13]; // box 和 行或者列的总单元格 减去 2(Pair), 待删除位置
_Point pt[4];
list<_Point> lstPt;
list<_Point>::iterator _iLstPt;
_Candi_Set::const_iterator _iSet;
_Candi_List::iterator _itCandi;
_Candi_List lstCandi;
_Candi_List l2;
_Candi_Set setCandi;
_Candi_Set setCD; // p1, p2合成的集合
int num;
int x = v[0];
int y = v[1];
v[2] = 0;
v[3] = 0;
// 将p1和p2点除了x,y值以为的候选数,构成一个数集
ANSudokuCell &c1 = m_anCell[p1.r][p1.c];
for (i = 0; i < c1.n; ++i) {
if (c1.candi[i] == x || c1.candi[i] == y) continue;
setCD.insert(c1.candi[i]);
}
ANSudokuCell &c2 = m_anCell[p2.r][p2.c];
for (i = 0; i < c2.n; ++i) {
if (c2.candi[i] == x || c2.candi[i] == y) continue;
setCD.insert(c2.candi[i]);
}
int n = 4;
_Point arrHouse[9];
int nLoop = 0;
do
{
anPt.s = setCD;
anPt.p.push_back(p1);
anPt.p.push_back(p2);
lstCandi.push_back(anPt);
anPt.p.clear();
if (nLoop == 0)
num = GetHouse(p1, p2, arrHouse, HOUSE_BOX);
else if (nLoop == 1)
num = GetHouse(p1, p2, arrHouse, HOUSE_ROW);
else if (nLoop == 2)
num = GetHouse(p1, p2, arrHouse, HOUSE_COL);
++nLoop;
for (i = 0; i < num; ++i)
{
ANSudokuCell &cTmp = m_anCell[arrHouse[i].r][arrHouse[i].c];
if (cTmp.n < 2 || cTmp.n > n) continue;
for (k = 0; k < cTmp.n; ++k)
{
setCandi.insert(cTmp.candi[k]);
}
point = arrHouse[i];
for (_itCandi = lstCandi.begin(); _itCandi != lstCandi.end(); ++_itCandi)
{
_Candi_Set setU2;
set_union(_itCandi->s.begin(), _itCandi->s.end(), setCandi.begin(),
setCandi.end(), inserter(setU2, setU2.begin()) );
if (setU2.size() > n) continue;
anPt.s = setU2;
_iLstPt = _itCandi->p.begin();
for (; _iLstPt != _itCandi->p.end(); ++_iLstPt)
{
anPt.p.push_back(*_iLstPt);
}
anPt.p.push_back(point);
l2.push_back(anPt);
anPt.p.clear();
}
// 添加集合
anPt.s = setCandi;
anPt.p.push_back(point);
lstCandi.push_back(anPt);
lstCandi.splice(lstCandi.begin(), l2);
anPt.p.clear();
setCandi.clear();
}
int setNum, ptNum;
_Candi_List::iterator itCandiN2 = lstCandi.end();
_Candi_List::iterator itCandiN3 = lstCandi.end();
_Candi_List::iterator itCandiN4 = lstCandi.end();
// 查找数对Pair, 三数集, 四数集 的位置
for (_itCandi = lstCandi.begin(); _itCandi != lstCandi.end(); /*++_itCandi*/)
{
setNum = _itCandi->s.size();
ptNum = _itCandi->p.size();
if (_itCandi->s.size() > n || ptNum - setNum != 1)
_itCandi = lstCandi.erase(_itCandi);
else
{
if (setNum == 2 && itCandiN2 == lstCandi.end())
{
itCandiN2 = _itCandi;
break;
}
else if (setNum == 3 && itCandiN3 == lstCandi.end())
itCandiN3 = _itCandi;
else if (setNum == 4 && itCandiN4 == lstCandi.end())
itCandiN4 = _itCandi;
++_itCandi;
}
}
if (itCandiN2 != lstCandi.end())
_itCandi = itCandiN2;
else if (itCandiN3 != lstCandi.end())
_itCandi = itCandiN3;
else if (itCandiN4 != lstCandi.end())
_itCandi = itCandiN4;
else {
lstCandi.clear();
continue;
}
setNum = _itCandi->s.size();
ptNum = _itCandi->p.size();
// 位置 比 数集中候选数的个数 多1(因为使用了伪数格)
_iSet = _itCandi->s.begin();
_iLstPt = _itCandi->p.begin();
for (i = 0; i < setNum; ++i)
{
v[i] = *_iSet++;
pt[i] = *_iLstPt++;
}
pt[i] = *_iLstPt++;
k = 0;
int wH = JudgeHouse(pt, ptNum);
if (wH == (HOUSE_ROW|HOUSE_BOX) || wH == (HOUSE_COL|HOUSE_BOX) )
num = GetInterSection(p1, p2, 13, arrOut); // 是否有可以消除的候选数
else
memcpy(arrOut, arrHouse, num*sizeof(_Point));
for (m = 0; m < num; ++m)
{
ANSudokuCell &cTmp = m_anCell[arrOut[m].r][arrOut[m].c];
bSamePt = false;
for (j = 0; j < ptNum; ++j)
{
if (pt[j].r == arrOut[m].r && pt[j].c == arrOut[m].c)
{
bSamePt = true;
break;
}
}
if (bSamePt) continue;
for (i = 0; i < cTmp.n; ++i)
{
bValEqual = false;
for (j = 0; j < n; ++j) // 看是否有可以消除的候选数
{
if (cTmp.candi[i] == v[j])
{
bValEqual = true;
break;
}
}
if (bValEqual) {
arrDelPt[k] = arrOut[m];
++k;
break;
}
}
}
if (k > 0) return k;
lstCandi.clear();
} while (nLoop < 3);
return 0;
}
// 取p1和p2, 共同的house, type指明 box, row or col
int Sudoku::GetHouse(_Point &p1, _Point &p2, _Point arrHouse[9], int type)
{
int k = 0;
int r, c;
if (type == HOUSE_BOX && p1.r/3==p2.r/3 && p1.c/3==p2.c/3)
{
int _rowBegin = p1.r/3 * 3;
int _rowEnd = _rowBegin + 3;
for (r = _rowBegin; r< _rowEnd; ++r)
{
int _colBegin = p1.c/3 * 3;
int _colEnd = _colBegin + 3;
for (c = _colBegin; c < _colEnd; ++c)
{
if (r == p1.r && c == p1.c || r == p2.r && c == p2.c) continue;
arrHouse[k].r = r;
arrHouse[k].c = c;
++k;
}
}
}
else if (type == HOUSE_ROW && p1.r == p2.r)
{
r = p1.r;
for (c = 0; c < 9; ++c)
{
if (r == p1.r && c == p1.c || r == p2.r && c == p2.c) continue;
arrHouse[k].r = r;
arrHouse[k].c = c;
++k;
}
}
else if (type == HOUSE_COL && p1.c == p2.c)
{
c = p1.c;
for (r = 0; r < 9; ++r)
{
if (r == p1.r && c == p1.c || r == p2.r && c == p2.c) continue;
arrHouse[k].r = r;
arrHouse[k].c = c;
++k;
}
}
return k;
}
// 判断点arrPt(个数为n),是否属于同一个house (box, row, col)
int Sudoku::JudgeHouse(_Point arrPt[], int n)
{
int nRet = 0;
int i;
bool bRow = true;
bool bCol = true;
bool bBox = true;
for (i = 1; i < n; ++i)
{
if (bRow && arrPt[i].r != arrPt[0].r)
{
bRow = false;
}
if (bCol && arrPt[i].c != arrPt[0].c)
{
bCol = false;
}
if (bBox && arrPt[i].r/3 != arrPt[0].r/3 || arrPt[i].c/3 != arrPt[0].c/3)
{
bBox = false;
}
}
if (bRow) nRet |= HOUSE_ROW;
if (bCol) nRet |= HOUSE_COL;
if (bBox) nRet |= HOUSE_BOX;
return nRet;
}
// 判断A,B,C,D是否可以组成 UR5, 返回0,不能; 非0可以组成UR5
// 要求:A必须是只有两个候选数的位置
int Sudoku::UR5(_Point &ptA, _Point &ptB, _Point &ptC, _Point &ptD)
{
ANSudokuCell &A = m_anCell[ptA.r][ptA.c];
ANSudokuCell &B = m_anCell[ptB.r][ptB.c];
ANSudokuCell &C = m_anCell[ptC.r][ptC.c];
ANSudokuCell &D = m_anCell[ptD.r][ptD.c];
// B,C,D任何一个的候选数不为3, 或者它们任何一个的候选数不包含A,则不能组成UR5
if (A.n!=2 || B.n!=3 || C.n!=3 || (D.n!=3&&D.n!=2) || !IsInclude(B,C)
|| !IsInclude(D,B))
return 0;
int i;
_Point arrOut[15];
int num = GetInterSection(ptB, ptC, 15, arrOut);
int nCandi;
Subtract(B, A, &nCandi); // 只有一个候选数
if (D.n == 3) // D点有额外的候选数
{
for (i=0; i<num; ++i)
{
// 如果是D点或者 和 D不在同一个box,则需要删除该点
if (arrOut[i].r==ptD.r && arrOut[i].c==ptD.c
|| arrOut[i].r/3!=ptD.r/3 || arrOut[i].c/3!=ptD.c/3)
{
swap(arrOut[i--], arrOut[--num]);
}
}
}
_Point arrDelPt[15];
int k = 0, j;
for (i = 0; i < num; ++i)
{
ANSudokuCell &cTmp = m_anCell[arrOut[i].r][arrOut[i].c];
for (j = 0; j < cTmp.n; ++j)
{
if (cTmp.candi[j] == nCandi)
{
arrDelPt[k] = arrOut[i];
++k;
break;
}
}
}
if (k <= 0) return 0;
printf("UR5[%d%d], r%d%d, c%d%d "
,A.candi[0], A.candi[1]
,ptA.r, ptD.r
,ptA.c, ptD.c
);
printf("删除候选数%d位置:", nCandi);
for (i = 0; i < k; ++i)
{
printf("[r%dc%d]", arrDelPt[i].r, arrDelPt[i].c);
}
printf("/n");
return 1;
}
// 判断A,B,C,D是否可以组成 UR6, 返回0,不能; 非0可以组成UR6
// 要求:A必须是只有两个候选数的位置
int Sudoku::UR6(_Point &ptA, _Point &ptB, _Point &ptC, _Point &ptD)
{
ANSudokuCell &A = m_anCell[ptA.r][ptA.c];
ANSudokuCell &B = m_anCell[ptB.r][ptB.c];
ANSudokuCell &C = m_anCell[ptC.r][ptC.c];
ANSudokuCell &D = m_anCell[ptD.r][ptD.c];
if (A.n!=2 || D.n!=2 || B.n<=2 || C.n<=2)
return 0;
// 隐含条件 A, B same box
int i, c, r;
bool bHasCandi1 = false;
bool bHasCandi2 = false;
// 按照行查找
for (c = 0; c < 9; ++c)
{
if (c == ptA.c || c == ptD.c) continue;
ANSudokuCell &cTmp = m_anCell[ptA.r][c];
for (i = 0; i < cTmp.n; ++i)
{
if (cTmp.candi[i] == A.candi[0])
{
bHasCandi1 = true;
}
if (cTmp.candi[i] == A.candi[1])
{
bHasCandi2 = true;
}
}
ANSudokuCell &cTmp2 = m_anCell[ptD.r][c];
for (i = 0; i < cTmp2.n; ++i)
{
if (cTmp2.candi[i] == A.candi[0])
{
bHasCandi1 = true;
}
if (cTmp2.candi[i] == A.candi[1])
{
bHasCandi2 = true;
}
}
}
int nDelCandi = 0;
if (!bHasCandi1)
nDelCandi = A.candi[0];
else if (!bHasCandi2)
nDelCandi = A.candi[1];
if (nDelCandi != 0) // 符合UR6
{
printf("UR6-Row(%d%d), r%d%d, c%d%d "
,A.candi[0], A.candi[1]
,ptA.r, ptD.r
,ptA.c, ptD.c
);
printf("删除候选数:%d的位置:[r%dc%d][r%dc%d]/n"
, nDelCandi, ptB.r, ptB.c, ptC.r, ptC.c);
return 1;
}
// 按照列查找
for (r = 0; r < 9; ++r)
{
if (r == ptA.r || r == ptD.r) continue;
ANSudokuCell &cTmp = m_anCell[r][ptA.c];
for (i = 0; i < cTmp.n; ++i)
{
if (cTmp.candi[i] == A.candi[0])
{
bHasCandi1 = true;
}
if (cTmp.candi[i] == A.candi[1])
{
bHasCandi2 = true;
}
}
ANSudokuCell &cTmp2 = m_anCell[r][ptD.c];
for (i = 0; i < cTmp2.n; ++i)
{
if (cTmp2.candi[i] == A.candi[0])
{
bHasCandi1 = true;
}
if (cTmp2.candi[i] == A.candi[1])
{
bHasCandi2 = true;
}
}
}
if (!bHasCandi1)
nDelCandi = A.candi[0];
else if (!bHasCandi2)
nDelCandi = A.candi[1];
if (nDelCandi != 0) // 符合UR6
{
printf("UR6-Col(%d%d), r%d%d, c%d%d "
,A.candi[0], A.candi[1]
,ptA.r, ptD.r
,ptA.c, ptD.c
);
printf("删除候选数:%d的位置:[r%dc%d][r%dc%d]/n"
, nDelCandi, ptB.r, ptB.c, ptC.r, ptC.c);
return 1;
}
return 0;
}
// 判断A,B,C,D是否可以组成 UR7, 返回0,不能; 非0可以组成
// 要求:A必须是只有两个候选数的位置, 隐含条件 A, B same box
int Sudoku::UR7(_Point &ptA, _Point &ptB, _Point &ptC, _Point &ptD)
{
ANSudokuCell &A = m_anCell[ptA.r][ptA.c];
ANSudokuCell &B = m_anCell[ptB.r][ptB.c];
ANSudokuCell &C = m_anCell[ptC.r][ptC.c];
ANSudokuCell &D = m_anCell[ptD.r][ptD.c];
if (A.n!=2 || B.n<=2 || C.n<=2)
return 0;
int i, r, c;
bool bHasCandi1 = false;
bool bHasCandi2 = false;
// D点所在行
for (c=0; c<9; ++c)
{
if (c == ptA.c || c == ptD.c) continue;
ANSudokuCell &cTmp = m_anCell[ptD.r][c];
for (i = 0; i < cTmp.n; ++i)
{
if (cTmp.candi[i] == A.candi[0])
{
bHasCandi1 = true;
}
if (cTmp.candi[i] == A.candi[1])
{
bHasCandi2 = true;
}
}
}
// D点所在列
for (r=0; r<9; ++r)
{
if (r == ptA.r || r == ptD.r) continue;
ANSudokuCell &cTmp = m_anCell[r][ptD.c];
for (i = 0; i < cTmp.n; ++i)
{
if (cTmp.candi[i] == A.candi[0])
{
bHasCandi1 = true;
}
if (cTmp.candi[i] == A.candi[1])
{
bHasCandi2 = true;
}
}
}
int nDelCandi = 0;
if (!bHasCandi1)
nDelCandi = A.candi[1];
else if (!bHasCandi2)
nDelCandi = A.candi[0];
if (nDelCandi != 0) // 符合UR7
{
printf("UR7(%d%d), r%d%d, c%d%d "
,A.candi[0], A.candi[1]
,ptA.r, ptD.r
,ptA.c, ptD.c
);
printf("删除候选数:%d的位置:[r%dc%d] D点/n"
, nDelCandi, ptD.r, ptD.c);
return 1;
}
return 0;
}