第30講 数独(ナンバープレイス)問題解決ソフトVer.1の制作
(数独(ナンバープレイス)問題作成ソフトに挑戦する人は☆☆)


第6話 問題データ以外の欄に数字をランダムに入れるコードの解説その2
コード再掲
#pragma endregion
  private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
           int i,j;
           array<String^>^ x=gcnew array<String^>(15);
           for(i=1;i<14;i++){
             if(i%4==1)for(j=0;j<13;j++)x[j]=L"*";
             if(i%4!=1)for(j=1;j<13;j++){
               if(j%4==1)x[j]=L"*";
               if(j%4!=1)x[j-1]=L"";
             }
             dataGridView1->Rows->Add(x);
           }
        }

        private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
           int i,j;
           for(i=0;i<15;i++)dataGridView1->Rows->Add();
           array<String^,2>^ w=gcnew array<String^,2>(15,100);
           for(i=0;i<14;i++){
             for(j=0;j<14;j++){
               w[i,j]=static_cast<String^>(dataGridView1[j,i]->Value);
             }
           }
           for(i=0;i<14;i++){
             for(j=0;j<14;j++){
               dataGridView1[j,15+i]->Value=w[i,j];
             }
           }
           char k1=0,k2,a[9][9];
           for(i=0;i<13;i++){
             if(i%4==0)k1++;
             if(i%4!=0){
               k2=0;
               for(j=0;j<13;j++){
                 if(j%4==0)k2++;
                   if(j%4!=0){
                   if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);
                   if(w[i,j]==L"")a[i-k1][j-k2]=0;
                 }
               } 
             }
           }
           k1=0;
           for(i=0;i<13;i++){
             if(i%4==0)k1++;
               if(i%4!=0){
                 k2=0;
                 for(j=0;j<13;j++){
                   if(j%4==0)k2++;
                   if(j%4!=0){
                     if(a[i-k1][j-k2]==0)dataGridView1[j,15+i]->Value=rand()%9+1;
                   }
                 }          
               }
             }
           }
        }
  };
}
解説の続き
           for(i=0;i<14;i++){
             for(j=0;j<14;j++){
               w[i,j]=static_cast<String^>(dataGridView1[j,i]->Value);
             }
           }
については解説はいりませんね。
入門
のデータを丸ごと読み取っています。

次の5行では、読み取ったデータをその2行下にコピー(ペーストといった方がよいでしょうか)しています。
           for(i=0;i<14;i++){
             for(j=0;j<14;j++){
               dataGridView1[j,15+i]->Value=w[i,j];
             }
           }
初心者

さて、難解なのが最後の部分
           char k1=0,k2,a[9][9];
           for(i=0;i<13;i++){
             if(i%4==0)k1++;
               if(i%4!=0){
                 k2=0;
                 for(j=0;j<13;j++){
                   if(j%4==0)k2++;
                     if(j%4!=0){
                       if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);
                       if(w[i,j]==L"")a[i-k1][j-k2]=0;
                     }
                   }
                 }
               }
              k1=0;
              for(i=0;i<13;i++){
                if(i%4==0)k1++;
                if(i%4!=0){
                  k2=0;
                  for(j=0;j<13;j++){
                    if(j%4==0)k2++;
                    if(j%4!=0){
                      if(a[i-k1][j-k2]==0)dataGridView1[j,15+i]->Value=rand()%9+1;
                    }
                  }          
                }
              }
            }
です。
k1やk2の役割、if(i%4==0)k1++;とif(j%4==0)k2++;の意味、さらにif(i%4!=0){とif(j%4!=0){の意味が把握できないとこのコードは理解することが出来ません。
その前にa[9][9]はなぜ9行9列の2次元配列にされているのでしょうか。
数独は、本来9×9の2次元データです。
           for(i=0;i<14;i++){
             for(j=0;j<14;j++){
               w[i,j]=static_cast<String^>(dataGridView1[j,i]->Value);
             }
           }
において、14行14列の2次元データとして扱われているのは、外との境界やブロックの境界を示す『*』が入っているからです。
その境界線を除くと、9行9列のデータになります。
14行14列と9行9列の違いから、k1とk2が必要となってきます。
つまり、k1とk2は行数と列数の違いを埋める役割を持っています。
では、if(i%4==0)k1++;とif(j%4==0)k2++;の意味はなんでしょうか。
初歩
をご覧になればお分かりのように境界線は、4行ごとそして4列ごとに引かれています。
そして、dataGridViewの場合は、行も列も0から始まります。つまり、0行目、0列目から始まります。
そうすると、境界線は0行目・4行目・8行目・12行目、0列目・4列目・8列目・12列目に引かれています。
0・4・8・12は、すべて4で割ったとき余りは0です。
ですから、if(i%4==0)k1++;とif(j%4==0)k2++;は余計な行数と列数をカウントしています。
すると、if(i%4!=0){とif(j%4!=0){の役割は明らかですね。
0・4・8・12の数字のとき以外、つまり1・2・3・5・6・7・9・10・11のときに実行しなさいという意味です。
1・2・3までのときは、余計な行数や列数は1ですよね。
したがって、if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);とif(w[i,j]==L"")a[i-k1][j-k2]=0;はそれぞれ、
if(w[i,j]!=L"")a[i-1][j-1]=int::Parse(w[i,j]);とif(w[i,j]==L"")a[i-1][j-1]=0;です。
5・6・7のときには、余計な行数および列数は2です。このときは、
if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);とif(w[i,j]==L"")a[i-k1][j-k2]=0;はそれぞれ、
if(w[i,j]!=L"")a[i-2][j-2]=int::Parse(w[i,j]);とif(w[i,j]==L"")a[i-2][j-2]=0;です。
最後9・10・11のときは、余計な行数と列数は3で、
if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);とif(w[i,j]==L"")a[i-k1][j-k2]=0;はそれぞれ、
if(w[i,j]!=L"")a[i-3][j-3]=int::Parse(w[i,j]);とif(w[i,j]==L"")a[i-3][j-3]=0;です。
以上から
           for(i=0;i<13;i++){
             if(i%4==0)k1++;
               if(i%4!=0){
                 k2=0;
                 for(j=0;j<13;j++){
                   if(j%4==0)k2++;
                     if(j%4!=0){
                       if(w[i,j]!=L"")a[i-k1][j-k2]=int::Parse(w[i,j]);
                       if(w[i,j]==L"")a[i-k1][j-k2]=0;
                     }
                   }
                 }
               }
によって、14行14列から必要な9行9列のデータだけを拾っていることがわかります。
              k1=0;
              for(i=0;i<13;i++){
                if(i%4==0)k1++;
                if(i%4!=0){
                  k2=0;
                  for(j=0;j<13;j++){
                    if(j%4==0)k2++;
                    if(j%4!=0){
                      if(a[i-k1][j-k2]==0)dataGridView1[j,15+i]->Value=rand()%9+1;
                    }
                  }          
                }
              }
の部分はご自分で考えてください。

では次の課題です。今回は空いてるセルにランダムに数字を入れたので、まったく数独(ナンバープレイス)になっていませんでした。
そこで、次回では行の条件(横の条件)、行に同じ数字が入ってはいけないを加えましょう。


第5話へ 第7話へ

戻る

VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)