Sudoku邏輯

------------------------------------------
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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章