Last Digit


Last Digit

「Last Digit」は、数独の数字配置のルールそのものです。
0~26のハウスについて、「未確定のセルが1つのハウスでは、そのセルは残りの数字に確定」します。

左の例では X=4 Y=6 Z=8 に確定します。


問題例

背景が濃いセルは問題数字、 淡いセルは解読したセル、
小さい数字は候補数字

R3C3のセルは数字6と確定した。

.93...7..5.4.7..1.27.3..5.8..2..78...4..5.6.....4...914.853.9...3...4.85..5..9.3.

素朴にプログラムを作れば、次のコードになるでしょう。
[09]GetCell_Houseでハウス(行・列・ブロック)内のnx番目のセルを取得し、ハウス内の未確定セルが1セルのとき解読成功となります。
[16]SolCodeは解析繰返しの始めに”-1”に設定し、セルの数字が確定したとき"1"とする制御変数です。 値"2"は、候補数字から除外できる数字が見つかったことを表します。
[18] MltSolOnは、複数のセルが同時に確定する場合に”同時/"個別に確定”の制御変数です。
[25] SolInfoDspは”解読のみ行う/ユーザーへの情報提供を行う”の制御変数です。

Last Digit(素朴版)

partial class GNPZ_Analyzer{
    public bool gSDK_LastDigitOld( ){
        int rc=0;
        UCell P0=null;
        
        for( int tfx=0; tfx<27; tfx++ ){
            int cc=0;
            for( int nx=0; nx<9; nx++ ){
                UCell P = GetCell_House( pBDL, tfx, nx, ref rc );
                if( P.No==0 ){
                    if( ++cc>=2 ) goto nextTry;
                    P0 = P;
                }
            }
            if( cc==1 ){
                SolCode=1;
                P0.FixedNo = P0.FreeB.BitToNum()+1;
                if( !MltSolOn )  goto LFond;
            }
        nextTry:
            continue;
        }

    LFond:
        if( SolCode==0 )  return  false;
        if( SolInfoDsp ) ResultLong = "Last Digit";
        Result = "Last Digit";
        return true;
    }
    private UCell GetCell_House( List<UCell> pBDL, int tfx, int nx, ref int rc ){ //nx=0...8
        int r=0, c=0, fx=tfx%9;
        switch(tfx/9){
            case 0: r=fx; c=nx; break;//行
            case 1: r=nx; c=fx; break;//列
            case 2: r=(fx/3)*3+nx/3; c=(fx%3)*3+nx%3; break;//ブロック
        }
        return pBDL[r*9+c];
    }
}

次はGNPXの目指す方針で作ったコードです。解析アルゴリズムの本質的部分は[05]の1行です。
if( pBDL.IEGetCellInHouse(tfx,0x1FF).Count()==1 ){
「盤面pBDLからtfx番目のHouseのセルを列挙し、その数(Count関数)が1の場合に…」と定義そのままを表すコードです。
IEGetCellInHouseの2番目の引数は列挙方法を修飾するもので、セルの持つ候補数字の条件を指定します。たとえば、pBDL.IEGetCellInHouse(tfx,(1<<2))とすれば、候補数字に3を含むセルのみが列挙されます。
0x1FFは、「いずれかの候補数字がある」すなわち「確定セルを除くすべてのセル」を意味します。
IEGetCellInHouseはList<UCell>型の拡張関数として、他の同様な関数とともに静的クラスで定義します。
素朴版のようなプログラミングは、高度なアルゴリズムになるとすぐに混乱し破綻します。早めにGNPX版に慣れてください。


解析プログラム Last Digit(GNPX版)

public class SimpleSingleGen: AnalyzerBaseV2{
    public bool LastDigit( ){
        bool  SolFond=false;
        for( int tfx=0; tfx<27; tfx++ ){
            if( pBDL.IEGetCellInHouse(tfx,0x1FF).Count() == 1 ){
                SolFond=true;
                ar P=pBDL.IEGetCellInHouse(tfx,0x1FF).First();
                P.FixedNo=P.FreeB.BitToNum()+1;
                if( !MltSolOn )  goto LFond;
            }
        }

        LFond:
        if(SolFond){
            SolCode=1;
            Result="Last Digit";
            if( SolInfoDsp ) ResultLong="Last Digit";
            AnMan.SnapSaveGP();
            return true;
        }
        return false;
    }
}

IEGet関数

static public class StaticSA{ 
    static public IEnumerable<UCell> IEGetCellInHouse( this List<UCell> pBDL, int tfx, int FreeB=0x1FF ){
        int r=0, c=0, tp=tfx/9, fx=tfx%9;
        for( int nx=0; nx<9; nx++ ){
            switch(tp){
                case 0: r=fx; c=nx; break;//行
                case 1: r=nx; c=fx; break;//列
                case 2: r=(fx/3)*3+nx/3; c=(fx%3)*3+nx%3; break;//ブロック
            }
            UCell P=pBDL[r*9+c];
            P.nx=nx;
            if( (P.FreeB&FreeB)>0 ) yield return P;
        }
    }
}