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