VC++を使ってパズル(数独)(sudoku)を解くプログラムを作ってみる
2018/01/07


昨年の5月頃までは、電子工作とともにプログラムの学習をしていたのだが、若干の問題発生と母親の介護が重なって少し中断してしまった。
すると、頭の中はたちまちクリアされてしまい、何をしていたかさえさだかではなくなってしまい、「ここは何処?・・・私は誰?・・・」という感じで、やる気も失せてしまっていた。

10月と12月には、母親が急に意識を失ってしまい、救急搬送して入院という事態になったり、どうも若干よろしくなかったのである。

しかし、そうしながらも「検査結果を見ると、脳血管や心臓、肺、膝にはそう大きな問題は無く、痙攣を抑える薬を飲んでいれば、命にかかわるものではないようです」と医者は言うのである。
すると、なんとなくホッとしたら不思議なことに、急にやる気が戻ってきたのである。

私は自分自身、「介護自体にプレッシャーを感じている」と思ったことは、あまりないはずだったのである。

自分では自分なりにノンビリとマイペースでやっていると思っていたのである。
親も自分も永遠に生きるわけではないし、ずっと健康でいられるとも思っていないので、「何時かは解らないが近いうちに何か悪いことがが起こる」と、覚悟を決めたうえでノンビリとやっていたつもりなのである。

しかしどうやら、介護というものは自分自身で感じている以上に、実際には精神的にプレッシャーが自分でも知らないうちにかかっているようなのである。



さて、今回のテーマは「数独」というもので、「数学は独身に限る」の省略形らしい。
「ナンバープレイス」というのも一般的な名称らしい。

test_pgm029
私は以前から帰宅の電車の中で、携帯電話に入っているこのゲームをしながら時間をつぶしていたのである。
おそらく7〜8年くらいはやっているので、一週間に2回の解答を出すとして、7×2×50で700回くらいは解答を出していることになる。

このゲームでは初級、中級、上級というレベルがあり、初級では最初に出ている数字が沢山あるのが、級が上がっていくと最初に出てくる数字がだんだん少なくなり、当然ながら解答を出すのも難しくなっていく。


このゲームは9×9のマス目が3×3のマス目に区切ってある。

1〜9の数字を縦の列と、横の列(行)と3×3の内部で重ならないように埋め尽くすというゲームなのである。
問題としては、最初からいくつかの数字が既に配置されており、その状態から上記のルールに従って空欄をどう埋めるか推理していくゲームなのである。

私は普段は上級でやってるのだが、今まででおそらく数回ではあるが、全部数字を埋めたのに、携帯電話のソフトウェアが「完了」とは認めなかったことがあるのである。

何回やっても、どう調べても正しいはずなのに、ソフトウェアは完了とは認めなかったのである。

また、推理ではどうしても全部を埋めることが出来なかったことはもっと多いのである。
たいがいは同じ行(列)で二ヵ所以上の空きが数行出来てしまい、どう推理しても入れられなかったのである。
もちろん、一か八かで入る可能性の数字を入れてみて、何か矛盾が出来たら交換するというのも手なのかもしれないのだが、どうもあまり良い気分ではない。


ということで、「visual studio 2017」があるので、解答を出すプログラムを作っておいて、こんどそういう状況になったら、以下のことを調べてみようと思うのである。

1、本当に解答が1つだったのか?

ソフトウェアが完了と認めなかったということは、ソフトウェアは正解が別にあると認識していたと考えられるのである。

2、本当に解答があるのか?

まぁ、要するに、この携帯電話に入っている数独のプログラムにバグが有るのか無いのか?を調べたいのである。
もしくは私の勘違いで、どこかにミスがあって解答にならなかったり、ちゃんと解答出来たりするのか調べたいのである。



まず仕様としては、プログラムがスタートすると以下のような画面が現れる。
※表示されている数字は最初は入っておらず全て空欄である。

test_pgm030
実際は正方形の一辺が270ピクセルほどある正方形である。

このゲームは左の絵のように数字文字が現れるので、フォントの大きさから正方形の大きさを逆算している。




test_pgm031
マウスを盤面の中に入れると画面にはカーソルの十字線が現れる。
また、左下にはステータスバーに盤面のマウスの位置が表示される。

こういう動作は、CADやお絵かきソフトなどでよく使用される技法で、なかなかカッコ良いのである。

今回はこういうものを作ってみようと思うのである。




test_pgm032
設定したいマスにカーソルを合わせて右クリックをすると、設定するダイアログ・ボックスが現れる。

test_pgm033
設定のダイアログ・ボックスは、このようなもので、そのマスに設定したい値を選んでOKを押す。

間違えた場合は、間違えたマスで空欄を選んでOKを押すとクリアされる。

また、入れられない数字を選んだ場合はOKを押しても何もおきない。


test_pgm035
初期値の設定を行うと、その数字は黒で表示される。

test_pgm034
全ての数字を設定したら、メニューの「編集」−>「実行」を選ぶと結果が表示される。

メニューの「編集」−>「画面クリア」を選ぶと画面はクリアされる。


test_pgm036
実行を行い、解答が出ると赤字で解答が表示され一旦停止する。そのときに解答数も出る。
解答が一つしかない場合は1で終了になるが、複数解答がある場合は次の解答でまた停止する。

だいたいこんなシナリオで良いだろうということで、次はプログラムのロジックを考える。

※ここに出ている画像は、プログラムが完成したのでその映像を出しているが、順番としては最初は手書きのメモ程度のものである。



さて、オペレーションの仕様はだいたい決めたのだが、「実行」を押したらいったい何をすれば良いのか?
というか、どうすれば解答へたどり着けるのか?

まず、考えると、この空間を埋めるのが数字でなくてはならないという意味は無いことに気が付く。
1〜9ではなくともA〜Iでも良いのである。
同じ記号が重ならなければよいということである。

どうやら、以前のプラパズルのように、全ての順列組み合わせを入れてみるという方法になりそうである。
その条件に縦の列と横の列、3×3のマス目の中に同一記号が無い、という条件がつくというもののようである。

そう考えると、このパズルの正解を求めるのは、原理的には以前のペントミノよりは簡単なのである。

test_pgm037
要するに、この1行の順列組み合わせを全て実行し、入るものが縦横と3×3の中に無ければ置けるということを9行分実行すれば良いのである。

メンドクサイのが、最初の固定値を設定する作業を行うマン・マシン・インターフェイスの処理である。


ということで、やるべきことは分かったのだが、実際にプログラムを作ろうとすると、またしても私の頭は完全にクリアーされており、何から始めて良いのかな〜んにも出てこないのであった。

しかたがないので、またまたウィンドウの中に「Hello World」が出るプログラムを作ってみようという、超初心者向けの例題から再スタートである。
これは、英語の勉強でいえば「I have a pen」のレベルであり、我ながらやっていてウンザリなのである。


再度勉強をしなおす基本は以下のようなものである。

1、好きな太さの線を書く
2、あるエリアの内部を好きな色で塗る。
3、フォントを変える。
4、メニューからイベントを取得して何かやる。
5、ダイアログボックスを表示し、データをやり取りする。



まず、データとしては以下の5つが必要になるので、ドキュメントクラスのヘッダーファイルで定義する。
高速化をするために1次元データにしようかと思ったのだが、以前のペントミノの経験から、高速化よりも考えやすさとか、理解しやすさを優先するために9×9の配列とした。

方向としては、横方向にX軸、縦方向にY軸として、[X]{Y}とする。
このへんの決定に関しては理由はとくに無く、ただの好みとか趣味の問題である。

	int data_k[9][9];		// 設定されたデータ
	int data_l[9][9];		// 設定されたフラグ 1:初期設定 2:自動設定
	int data_m[9][9];		// 設定する元のデータ X方向に 1〜9
	int data_n[9];			// 元のデータの数・・・9-初期設定された数
	int counter_all;		// 解答の数
data_k[9][9]が見えている盤面で、当初は全て0、初期のデータを設定するとその値となる。
解答が計算された場合もdata_kに結果が入り、それがクライアント領域に表示される。

data_l[9][9]は、data_kに設定された数値が初期設定(手動で定義されたもの)のものか、計算結果のものかのフラグになる。
1は初期設定されたもの、2が計算結果で出されたもの、このフラグで表示する数字の色を変化させる。

data_m[9][9]は、順列組み合わせで使用するもので、初期値は1〜9が9列分入っている。
画面からある値を初期値でdata_kに入れると、その数は計算不要としてdata_mから取り除かれる。

data_n[9]はdata_mに入っている計算が必要な個数で、当初は9行分全てに9が入っている。
ある行にある数字が初期値で与えられると、その数字はdata_mから除かれて、data_nのある列が1減る。

test_pgm038


例として、上の図の上の行が2行目とすると、その行には初期値が入っていないので、設定されたデータは以下のようになっている。

data_k[0〜8][1]には全て0が設定されている。
data_l[0〜8][1]には全て0が設定されている。
data_m[0〜8][1]には1〜9全てが順番に設定されている。
data_n[1]には9が設定されている。

下の3行目には、7、5、3が既に設定されているので、データは以下のようになっている。

data_k[0〜8][2]には、0,7,0,0,5,0,3,0,0が設定されている。
data_l[0〜8][2]には、0,1,0,0,1,0,1,0,0が設定されている。
data_m[0〜5][2]には、1,2,4,6,8,9が設定され、data_m[6〜8][2]には、10がダミーで設定されている。
data_n[2]には6が設定されている。

data_mに入っているdata_n[n]個の値の全ての順列組み合わせを空いた場所に入れてみれば良いわけである。

data_m[6〜8][2]には、10がダミーで設定されているというのは、この値は本来の計算には使用しないのだが、デバックのときとか、初期設定のやり直しのときに昇順で並ばせる場合に便利なので最大値+1を設定している。



これらの初期設定が以下のルーチンである。

ドキュメント・クラスのヘッダーファイルに追加する部分

// 属性
public:
	int data_k[9][9];		// 設定されたデータ
	int data_l[9][9];		// 設定されたフラグ 1:初期設定 2:自動設定
	int data_m[9][9];		// 設定する元のデータ X方向に 1〜9
	int data_n[9];			// 元のデータの数・・・9-初期設定された数
	int counter_all;		// 解答の数

// 操作
public:
	virtual void Cleare_data();		// データの完全クリア
	virtual void Cleare_data2();	// 計算結果のクリア(手動で設定したデータは残す)



ドキュメントクラスに追加するクリアルーチンが二つ。

// Clear data
void CSuudoku01Doc::Cleare_data()
{
	int i1;
	int i2;

	counter_all = 0;
	for (i1 = 0; i1 < 9; i1++) {
		for (i2 = 0; i2 < 9; i2++) {
			data_k[i1][i2] = 0;
			data_l[i1][i2] = 0;
			data_m[i1][i2] = i1 + 1;
		}
		data_n[i1] = 9;
	}
	UpdateAllViews(NULL);
}


void CSuudoku01Doc::Cleare_data2()
{
	int i1;
	int i2;
	int temp;
	BOOL flg;

	counter_all = 0;
	for (i1 = 0; i1 < 9; i1++) {
		for (i2 = 0; i2 < 9; i2++) {

			if (data_l[i1][i2] != 1) {
				data_k[i1][i2] = 0;
				data_l[i1][i2] = 0;
			}
		}
	}

	do {

		flg = FALSE;
		for (i1 = 0; i1 < 9; i1++) {
			for (i2 = 0; i2 < 8; i2++) {
				if (data_m[i2][i1] > data_m[i2 + 1][i1]) {
					flg = TRUE;
					temp = data_m[i2][i1];
					data_m[i2][i1] = data_m[i2 + 1][i1];
					data_m[i2 + 1][i1] = temp;
				}
			}
		}

	} while (flg);


	UpdateAllViews(NULL);
}
あと、初期設定をドキュメントクラスのコンストラクタから一回呼んでおく。

// CSuudoku01Doc コンストラクション/デストラクション

CSuudoku01Doc::CSuudoku01Doc()
{
	// TODO: この位置に 1 度だけ呼ばれる構築用のコードを追加してください。

	Cleare_data();

}


ビュークラスのヘッダーファイルへの追加。

// 操作
public:
	virtual void sub_Draw2(CSuudoku01Doc* pDoc, CDC* pDC);				// 再描画
	virtual void sub_EXEC_iy0(CSuudoku01Doc* pDoc, CDC* pDC);			// 1行目の計算処理
	virtual void sub_EXEC_iy1(CSuudoku01Doc* pDoc, CDC* pDC);			// 2行目の計算処理
	virtual void sub_EXEC_iy2(CSuudoku01Doc* pDoc, CDC* pDC);			// 3行目の計算処理
	virtual void sub_EXEC_iy3(CSuudoku01Doc* pDoc, CDC* pDC);			// 4行目の計算処理
	virtual void sub_EXEC_iy4(CSuudoku01Doc* pDoc, CDC* pDC);			// 5行目の計算処理
	virtual void sub_EXEC_iy5(CSuudoku01Doc* pDoc, CDC* pDC);			// 6行目の計算処理
	virtual void sub_EXEC_iy6(CSuudoku01Doc* pDoc, CDC* pDC);			// 7行目の計算処理
	virtual void sub_EXEC_iy7(CSuudoku01Doc* pDoc, CDC* pDC);			// 8行目の計算処理
	virtual void sub_EXEC_iy8(CSuudoku01Doc* pDoc, CDC* pDC);			// 9行目の計算処理
	virtual BOOL check_ix_iy(CSuudoku01Doc* pDoc,int chkix, int chkiy,int tempdata);	// そこに置けるかのチェック
	int old_point_x;		// 十字カーソル表示用
	int old_point_y;		// 十字カーソル表示用
	int width_cursor = 1;	// 十字カーソル表示用
	int offset_x = 100;		// 表示のオフセット
	int offset_y = 100;		// 表示のオフセット
	int font_height = 30;	// フォントの大きさの設定
	int font_width;
	int exec_flg;			// 実行を続行するかのフラグ(キャンセルにも使用)
書いていて、sub_EXEC_iy0〜iy8という、ほとんど同じ処理があるのが若干恥ずかしくなってくるのである。

もうちょっとでも、なんとか工夫出来なかったものじゃないだろうか?



ビュークラスの変更と追加

コンストラクタで、フラグをリセットしておく。

// CSuudoku01View コンストラクション/デストラクション

CSuudoku01View::CSuudoku01View()
{
	// TODO: 構築コードをここに追加します。
	exec_flg = 0;
	old_point_x = -width_cursor;
	old_point_y = -width_cursor;
}
old_point_xに-width_cursorを設定するのは、後述する十文字のカーソルの表示のためである。



OnDraw関数での描画ルーチンの変更


// CSuudoku01View 描画

void CSuudoku01View::OnDraw(CDC* pDC)
{
	CSuudoku01Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: この場所にネイティブ データ用の描画コードを追加します。

	old_point_x = -width_cursor;
	old_point_y = -width_cursor;

	pDC->TextOutW(100, 80, _T("123456789"));
	if (exec_flg == 1) {
		sub_EXEC_iy0(pDoc, pDC);

		if (exec_flg == 0) {		// cansel
			pDoc->Cleare_data2();
		}
		else {
			sub_Draw2(pDoc, pDC);
		}
		exec_flg = 0;
	}
	else {
		sub_Draw2(pDoc, pDC);
	}

}

old_point_xとyへ-width_cursorを設定しているのは、後述する十文字のカーソルの表示のためである。
pDC->TextOutW(100, 80, _T("123456789"))は、フォントの決定の為に、テストで表示したシステムのフォントである。
本番ではコメントにする。

sub_EXEC_iy0(pDoc, pDC)が1行目の実行ルーチン
exec_flgは実行中が1でキャンセルされると0になる。
キャンセルで戻ってきた場合は、pDoc->Cleare_data2で、初期値に戻される。

sub_Draw2が本来の再描画ルーチンである。



test_pgm039
メニューで「画面クリア」を選んだときの処理

単純にドキュメントクラスの初期処理を呼び出すだけであるが、念のために「メッセージボックス」を表示して、クリアして良いかどうか確認をしている。

test_pgm040
「OK」のボタンを押さなければ何もしない。



void CSuudoku01View::OnClearData()
{
	// TODO: ここにコマンド ハンドラー コードを追加します。

	CSuudoku01Doc* pDoc = GetDocument();

	if (MessageBox(_T("画面をクリアします。"), _T("初期化"), MB_OKCANCEL) == IDOK) {
		pDoc->Cleare_data();
	}

}


test_pgm041
マウスを動かすと、画面の左下のステータスバーの中に、X座標とY座標がリアルタイムで表示される。

また、マウスが盤面上に来ると、画面いっぱいに十字線が表示されてマウスと一緒に動く。



マウスの処理は、OnMouseMove(UINT nFlags, CPoint point)関数が呼び出されたときに、CPoint構造体に現在の位置情報が入っているので、その値をステータスバーに表示しているだけである。

若干メンドクサイのが、ステータスバーそのものをAfxGetApp()からズルズルと持ってくる方法である。
ただ、このステータスバーは、リアルに表示出来るので、デバッグのときなどは意外と重宝するのである。

void CSuudoku01View::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。
	CView::OnMouseMove(nFlags, point);

	// デバイスコンテキスト取得
	CDC* pDC = GetDC();
	CRect rect01;
	CRect win_rect;

	// ステータスバーにX座標とY座標を表示する。
	CFrameWnd* pFrame = (CFrameWnd*)AfxGetApp()->m_pMainWnd;
	CStatusBar* pStatusBar = (CStatusBar*)pFrame->GetDescendantWindow(AFX_IDW_STATUS_BAR);

	int ix;
	int iy;
	CString str_point;
	
	//===========================================================

	GetClientRect(win_rect);

	str_point.Format(_T("X = %d  Y = %d"), point.x, point.y);
	pStatusBar->SetPaneText(0, str_point);

	//===========================================================

	rect01.left = old_point_x;
	rect01.top = 0;
	rect01.right = old_point_x + width_cursor;
	rect01.bottom = win_rect.bottom;

	pDC->InvertRect(rect01);

	rect01.left = 0;
	rect01.top = old_point_y;
	rect01.right = win_rect.right;
	rect01.bottom = old_point_y + width_cursor;

	pDC->InvertRect(rect01);

	ix = point.x - offset_x;
	iy = point.y - offset_y;

	if (ix < 0 || iy < 0) {

		old_point_x = -width_cursor;
		old_point_y = -width_cursor;

	}
	else {

		ix = ix / font_height;
		iy = iy / font_height;

		if (ix < 9 && iy < 9) {

			rect01.left = point.x;
			rect01.top = 0;
			rect01.right = point.x + width_cursor;
			rect01.bottom = win_rect.bottom;

			pDC->InvertRect(rect01);

			rect01.left = 0;
			rect01.top = point.y;
			rect01.right = win_rect.right;
			rect01.bottom = point.y + width_cursor;

			pDC->InvertRect(rect01);

			old_point_x = point.x;
			old_point_y = point.y;

		}
		else {

			old_point_x = -width_cursor;
			old_point_y = -width_cursor;

		}
	}

}
test_pgm042
動的に動く十字線は、実際には線を描いているのではなく、細い四角形であり、その四角形の内部の色を反転させることで表示している。
ただ、その四角形の一辺を1ポイントの広さ(?)にしているだけで、あくまでも四角形なのである。
描画しているのはpDC->InvertRect(xxxx);という関数である。

色の反転は、もう一度反転させると元の色に戻るという性質があり、元の色を別の方法で覚えておく必要が無いのである。
覚えておく必要があるのが、色を反転させた四角形の座標だけなのである。

試しにwidth_cursorを5にして実行したのが左のもので、白は黒、黒は白、緑が紫になっている。


このロジックで問題になるのが、十字線の表示エリアを抜けた場合と、何らかのイベント(たとえばウインドウの大きさが変わったとか、別のウインドウが重なったとか)で画面が再描画された場合である。
そのときに最後の色の反転が残ってしまい、不要な十字線が残ってしまうのである。

それを防ぐ一番簡単な方法が、OnDraw関数が呼ばれたり、十字線の表示エリアを外れた場合には、十字線を再反転させるエリアの座標を、十字線の太さ分クライアント領域から外してしまうのである。
それが-width_cursorという記述である。





test_pgm043
盤面の適当な場所にカーソルを持っていき、右クリックすると左のようなダイアログボックスが表示される。

このダイアログボックスの中の適当な数値を選んでOKのボタンを押すと、その値が盤面に表示される。
test_pgm044
再度その場所でダイアログボックスを開いて、空欄を選んでOKを押すとクリアされる。


ダイアログボックスのヘッダーファイルには、ラジオボタンの値と表示される位置の変数を定義しておく。

public:
	int m_rad01;
	virtual BOOL OnInitDialog();

	int m_dlgpos_x;
	int m_dlgpos_y;
ダイアログボックスの初期処理には、SetWindowPosを使って表示される位置を変更する。
これを入れておかないと、マウスのボタンを押した場所に関係なく中央にダイアログボックスが表示されてしまい、なんだか違和感を感じるのである。

BOOL CNumsetDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO: ここに初期化を追加してください
	CWnd* pCWnd = this;

	SetWindowPos(pCWnd, m_dlgpos_x, m_dlgpos_y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);

	return TRUE;  // return TRUE unless you set the focus to a control
				  // 例外 : OCX プロパティ ページは必ず FALSE を返します。
}


上記のダイアログボックスを使って、右ボタンを押したときの実行が下記のものである。

右ボタンを押したときの座標がCPointで来るので、それを使用して盤面の位置を算出する。

void CSuudoku01View::OnRButtonUp(UINT nFlags, CPoint point)
{
	// TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。

	CView::OnRButtonUp(nFlags, point);

	CSuudoku01Doc* pDoc = GetDocument();

	CNumsetDlg dlg;
	int ix;
	int iy;
	CString str0;
	int temp_ix;
	int temp_ix2;
	int hit_flg;
	int temp_data;
	BOOL bad_flg;

	CWnd* pCWnd = this;
	HWND hWnd = pCWnd->m_hWnd;
	CRect lp_Rect;

	GetWindowRect(lp_Rect);		// メインページの座標

	ix = point.x - offset_x;
	iy = point.y - offset_y;
	if (ix >= 0 && iy >= 0) {
		ix = ix / font_height;
		iy = iy / font_height;
		if (ix < 9 && iy < 9) {
			dlg.m_dlgpos_x = point.x + lp_Rect.left;
			dlg.m_dlgpos_y = point.y + lp_Rect.top;
			if (dlg.DoModal() == IDOK) {
				//str0.Format(_T("ix = %d  iy = %d  インデックス = %d "),ix,iy,dlg.m_rad01);
				//MessageBox(str0);
				if (dlg.m_rad01 == 9) {
					temp_data = pDoc->data_k[ix][iy];
					if (temp_data == 0) {
						return;
					}
					hit_flg = 0;

					if (pDoc->data_n[iy] == 0) {
						pDoc->data_m[0][iy] = temp_data;
					}
					else {
						temp_ix2 = pDoc->data_n[iy];
						for (temp_ix = 8; temp_ix > 0; temp_ix--) {
							pDoc->data_m[temp_ix][iy] = pDoc->data_m[temp_ix - 1][iy];
							if (pDoc->data_m[temp_ix][iy] < temp_data) {
								//str0.Format(_T("ヒット   x = %d  y = %d data = %d "), temp_ix, iy, temp_data);
								//MessageBox(str0);
								hit_flg = 1;
								pDoc->data_m[temp_ix][iy] = temp_data;
								break;
							}
						}
						if (!hit_flg) {
							pDoc->data_m[0][iy] = temp_data;
						}
					}
					//MessageBox(_T("インデックスを1加算"));
					pDoc->data_n[iy]++;

					pDoc->data_k[ix][iy] = 0;
					pDoc->data_l[ix][iy] = 0;
				}
				else {

					temp_data = dlg.m_rad01 + 1;
					bad_flg = check_ix_iy(pDoc, ix, iy, temp_data);
					if (!bad_flg) {

						for (temp_ix = 0; temp_ix < pDoc->data_n[iy]; temp_ix++) {

							if (pDoc->data_m[temp_ix][iy] == temp_data) {
								//str0.Format(_T("ヒット   x = %d  y = %d data = %d "), temp_ix, iy, temp_data);
								//MessageBox(str0);
								for (temp_ix2 = temp_ix; temp_ix2 < pDoc->data_n[iy]; temp_ix2++) {
									if (temp_ix2 == 8) {
										pDoc->data_m[temp_ix2][iy] = 10;
									}
									else {
										pDoc->data_m[temp_ix2][iy] = pDoc->data_m[temp_ix2 + 1][iy];
									}
								}
								break;

							}
						}
						pDoc->data_n[iy]--;

						pDoc->data_k[ix][iy] = temp_data;
						pDoc->data_l[ix][iy] = 1;
					}
				}
				pDoc->UpdateAllViews(NULL);
			}
		}
	}
	else {
	}

}


下記のプログラムが、盤面のその場所(x,y)にtemp_dataに入っているものが置けるかどうかのチェックルーチンである。
見ていて、あまりにもダサいのとアホらしいコーディングを書いてしまったので、見直すのも嫌になってしまったので、途中までで以下省略とする。

これだけ見ても、ダサさは十分に伝わると思う。

BOOL CSuudoku01View::check_ix_iy(CSuudoku01Doc* pDoc,int chkix, int chkiy,int temp_data)
{
	int temp_ix;
	int temp_iy;
	BOOL bad_flg;

	bad_flg = FALSE;

	if (pDoc->data_k[chkix][chkiy] != 0) {
		return TRUE;
	}
	for (temp_ix = 0; temp_ix < 9; temp_ix++) {
		if (pDoc->data_k[temp_ix][chkiy] == temp_data) {
			return TRUE;
		}
	}
	if (chkiy == 0) {
		for (temp_iy = 1; temp_iy < 3; temp_iy++) {
			if (chkix < 3) {
				for (temp_ix = 0; temp_ix < 3; temp_ix++) {
					if (pDoc->data_k[temp_ix][temp_iy] == temp_data) {
						return TRUE;
					}
				}
			}
			else if (chkix < 6) {
				for (temp_ix = 3; temp_ix < 6; temp_ix++) {
					if (pDoc->data_k[temp_ix][temp_iy] == temp_data) {
						return TRUE;
					}
				}
			}



メニューから実行を選ぶと、この処理で実行フラグを1にするのと、解答数を0にリセットするだけである。

実際の計算処理はOnDrawから呼び出されるsub_EXEC_iy0で行っているのだが、たぶん本来のウインドウズの処理としは、いけないことをやっているのである。

本当の設計としては、ここから別スレッドを起こして実行処理を行い、実行の結果を何らかのパイプなり共有メモリ(なんと懐かしい響き)で画面の処理に送るべきなのであろう。

しかし、今回はメンドクサイので(こればっかり)処理をしては表示されるというのを一連のプログラムで書いてしまったのである。


void CSuudoku01View::On32773()
{
	// TODO: ここにコマンド ハンドラー コードを追加します。
	CSuudoku01Doc* pDoc = GetDocument();

	if (MessageBox(_T("実行します。"), _T("実行"), MB_OKCANCEL) == IDOK) {
		exec_flg = 1;
		pDoc->counter_all = 0;

		pDoc->UpdateAllViews(NULL);
	}

}


これが再描画のルーチンである。

見慣れない処理が最初の頃に出てくる。

pDC->SetBkMode(TRANSPARENT);というのは、こちらで指定したフォントで文字を書いた場合に、背景色とフォントのバックカラーがずれる場合があるので記述している。
色々な背景色の場所に、同じフォントで書くとずれが発生する。

hfontDC = pDC->operator HDC();というのは、これもフォント関係で、フォントそのものの色を変える場合、デバイスコンテキストのハンドルが必要になるので、その取得である。

void CSuudoku01View::sub_Draw2(CSuudoku01Doc* pDoc,CDC* pDC)
{

	CFont font1;
	TEXTMETRIC tm1;
	CStringW str0;

	HDC hfontDC;
	COLORREF font_col;

	int font_offset_x = 8;
	int font_offset_y = 1;

	CPen pen1(PS_SOLID, 3, RGB(0, 0, 0));
	CPen* old_pen;

	int temp0;
	int temp1;
	int temp2;
	int temp3;

	//===========================================================

	pDC->SetBkMode(TRANSPARENT);
	hfontDC = pDC->operator HDC();

	//===========================================================

	font1.CreateFontW(font_height, 0, 0, 0, FW_HEAVY, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Arial"));
	pDC->SelectObject(&font1);
	pDC->GetTextMetrics(&tm1);
	font_height = tm1.tmHeight;
	font_width = tm1.tmAveCharWidth;

	//str0.Format(_T("フォントの幅 = %d ピクセル  高さ = %d ピクセル"),font_width,font_height);
	//MessageBox(str0);

	//===========================================================

	pDC->FillSolidRect(offset_x, offset_y, font_height * 9, font_height * 9, RGB(255, 255, 255));
	pDC->FillSolidRect(offset_x, offset_y, font_height * 3, font_height * 3, RGB(64, 255, 0));
	pDC->FillSolidRect(offset_x + font_height * 6, offset_y, font_height * 3, font_height * 3, RGB(64, 255, 0));
	pDC->FillSolidRect(offset_x + font_height * 3, offset_y + font_height * 3, font_height * 3, font_height * 3, RGB(64, 255, 0));
	pDC->FillSolidRect(offset_x, offset_y + font_height * 6, font_height * 3, font_height * 3, RGB(64, 255, 0));
	pDC->FillSolidRect(offset_x + font_height * 6, offset_y + font_height * 6, font_height * 3, font_height * 3, RGB(64, 255, 0));

	pDC->MoveTo(offset_x, offset_y);
	temp1 = offset_y;
	pDC->LineTo(offset_x + font_height * 9, temp1);
	for (temp0 = 0; temp0 < 9; temp0++) {
		temp1 = temp1 + font_height;
		pDC->MoveTo(offset_x, temp1);
		pDC->LineTo(offset_x + font_height * 9, temp1);
	}

	pDC->MoveTo(offset_x, offset_y);
	temp1 = offset_x;
	pDC->LineTo(temp1, offset_y + font_height * 9);
	for (temp0 = 0; temp0 < 9; temp0++) {
		temp1 = temp1 + font_height;
		pDC->MoveTo(temp1, offset_y);
		pDC->LineTo(temp1, offset_y + font_height * 9);
	}

	old_pen = pDC->SelectObject(&pen1);

	pDC->MoveTo(offset_x, offset_y);
	temp1 = offset_y;
	pDC->LineTo(offset_x + font_height * 9, temp1);
	for (temp0 = 0; temp0 < 3; temp0++) {
		temp1 = temp1 + font_height * 3;
		pDC->MoveTo(offset_x, temp1);
		pDC->LineTo(offset_x + font_height * 9, temp1);
	}

	pDC->MoveTo(offset_x, offset_y);
	temp1 = offset_x;
	pDC->LineTo(temp1, offset_y + font_height * 9);
	for (temp0 = 0; temp0 < 3; temp0++) {
		temp1 = temp1 + font_height * 3;
		pDC->MoveTo(temp1, offset_y);
		pDC->LineTo(temp1, offset_y + font_height * 9);
	}

	pDC->SelectObject(old_pen);

	//===========================================================

	temp2 = offset_x + font_offset_x;
	temp3 = offset_x + font_offset_y;
	for (temp0 = 0; temp0 < 9; temp0++) {
		for (temp1 = 0; temp1 < 9; temp1++) {
			if (pDoc->data_l[temp1][temp0] != 0) {
				str0.Format(_T("%d"), pDoc->data_k[temp1][temp0]);
				//MessageBox(str0);
				if (pDoc->data_l[temp1][temp0] == 1) {
					font_col = SetTextColor(hfontDC, RGB(0, 0, 0));
				}
				else {
					font_col = SetTextColor(hfontDC, RGB(255, 0, 255));
				}
				pDC->TextOutW(temp2, temp3, str0);
				font_col = SetTextColor(hfontDC, font_col);

			}
			temp2 = temp2 + font_height;
		}
		temp2 = offset_x + font_offset_x;
		temp3 = temp3 + font_height;
	}

}
test_pgm045
今回はちょっと大きめの図柄で描いているいるのでフォントも大きくする。
その方が色の変化が見やすいのである。

30ピクセルのフォントを使用してみたが、枠の外に描いてあるシステムのフォントと比べてみると、だいぶ大きい。



これが1行目の順列組み合わせの処理である。

なんだかダサい作りで我ながら嫌になってしまうのだが、まぁ、これが自分の実力なのだろう。

新人研修とかで私が何時も言っていたのは、「良いプログラムは、解りやすく、素直に作るのが良い」などと言っていたのである。
そして「モットーとしては、小さく、早く、美しく作るべし!!」などと言っていたのであるが、どうも「自分でも出来ないことを教えていた」という気分になるのである。


void CSuudoku01View::sub_EXEC_iy0(CSuudoku01Doc* pDoc, CDC* pDC)
{
	//MessageBox(_T("実行処理スタートiy0"), _T("実行"), MB_OKCANCEL);
	int ix0;
	int ix1;
	int ix2;
	int ix3;
	int ix4;
	int ix5;
	int ix6;
	int ix7;
	int ix8;

	int ix_com0;
	int ix_com1;
	int ix_com2;
	int ix_com3;
	int ix_com4;
	int ix_com5;
	int ix_com6;
	int ix_com7;
	int ix_com8;

	int iy;
	int ix_loop;
	int temp_m;
	CStringW str0;
	int count_iy0;

	count_iy0 = 0;
	iy = 0;
	ix_loop = pDoc->data_n[iy];


	if (ix_loop == 0) {
		//sub_Draw2(pDoc, pDC);
		sub_EXEC_iy1(pDoc, pDC);
		//MessageBox(_T("ステップ2"));
		//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
		if (exec_flg == 0) {
			return;
		}
	}
	else {

		for (ix0 = 0; ix0 < ix_loop; ix0++) {

			temp_m = pDoc->data_m[0][iy];
			pDoc->data_m[0][iy] = pDoc->data_m[ix0][iy];
			pDoc->data_m[ix0][iy] = temp_m;

			for (ix_com0 = 0; ix_com0 < 9; ix_com0++) {
				if (pDoc->data_k[ix_com0][iy] == 0) {
					break;
				}
			}

			temp_m = pDoc->data_m[0][iy];
			if (!check_ix_iy(pDoc, ix_com0, iy, temp_m)) {
				//MessageBox(_T("次行けます0"), _T("実行中"), MB_OKCANCEL);
				pDoc->data_k[ix_com0][iy] = pDoc->data_m[0][iy];
				pDoc->data_l[ix_com0][iy] = 2;

				if (ix_loop == 1) {

					//sub_Draw2(pDoc, pDC);
					count_iy0++;
					sub_EXEC_iy1(pDoc, pDC);
					//MessageBox(_T("ステップ2"));
					//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
					if (exec_flg == 0) {
						return;
					}

				}
				else {

					for (ix1 = 1; ix1 < ix_loop; ix1++) {

						temp_m = pDoc->data_m[1][iy];
						pDoc->data_m[1][iy] = pDoc->data_m[ix1][iy];
						pDoc->data_m[ix1][iy] = temp_m;

						for (ix_com1 = ix_com0; ix_com1 < 9; ix_com1++) {
							if (pDoc->data_k[ix_com1][iy] == 0) {
								break;
							}
						}

						temp_m = pDoc->data_m[1][iy];
						if (!check_ix_iy(pDoc, ix_com1, iy, temp_m)) {
							//MessageBox(_T("次行けます1"), _T("実行中"), MB_OKCANCEL);
							pDoc->data_k[ix_com1][iy] = pDoc->data_m[1][iy];
							pDoc->data_l[ix_com1][iy] = 2;

							if (ix_loop == 2) {

								//sub_Draw2(pDoc, pDC);
								count_iy0++;
								sub_EXEC_iy1(pDoc, pDC);
								//MessageBox(_T("ステップ2"));
								//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
								if (exec_flg == 0) {
									return;
								}

							}
							else {

								for (ix2 = 2; ix2 < ix_loop; ix2++) {

									temp_m = pDoc->data_m[2][iy];
									pDoc->data_m[2][iy] = pDoc->data_m[ix2][iy];
									pDoc->data_m[ix2][iy] = temp_m;

									for (ix_com2 = ix_com1; ix_com2 < 9; ix_com2++) {
										if (pDoc->data_k[ix_com2][iy] == 0) {
											break;
										}
									}

									temp_m = pDoc->data_m[2][iy];
									if (!check_ix_iy(pDoc, ix_com2, iy, temp_m)) {
										//MessageBox(_T("次行けます2"), _T("実行中"), MB_OKCANCEL);
										pDoc->data_k[ix_com2][iy] = pDoc->data_m[2][iy];
										pDoc->data_l[ix_com2][iy] = 2;

										if (ix_loop == 3) {
											//sub_Draw2(pDoc, pDC);
											count_iy0++;
											sub_EXEC_iy1(pDoc, pDC);
											//MessageBox(_T("ステップ2"));
											//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
											if (exec_flg == 0) {
												return;
											}
										}
										else {

											for (ix3 = 3; ix3 < ix_loop; ix3++) {

												temp_m = pDoc->data_m[3][iy];
												pDoc->data_m[3][iy] = pDoc->data_m[ix3][iy];
												pDoc->data_m[ix3][iy] = temp_m;

												for (ix_com3 = ix_com2; ix_com3 < 9; ix_com3++) {
													if (pDoc->data_k[ix_com3][iy] == 0) {
														break;
													}
												}

												temp_m = pDoc->data_m[3][iy];
												if (!check_ix_iy(pDoc, ix_com3, iy, temp_m)) {
													//MessageBox(_T("次行けます3"), _T("実行中"), MB_OKCANCEL);
													pDoc->data_k[ix_com3][iy] = pDoc->data_m[3][iy];
													pDoc->data_l[ix_com3][iy] = 2;

													if (ix_loop == 4) {
														//sub_Draw2(pDoc, pDC);
														count_iy0++;
														sub_EXEC_iy1(pDoc, pDC);
														//MessageBox(_T("ステップ2"));
														//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
														if (exec_flg == 0) {
															return;
														}
													}
													else {

														for (ix4 = 4; ix4 < ix_loop; ix4++) {

															temp_m = pDoc->data_m[4][iy];
															pDoc->data_m[4][iy] = pDoc->data_m[ix4][iy];
															pDoc->data_m[ix4][iy] = temp_m;

															for (ix_com4 = ix_com3; ix_com4 < 9; ix_com4++) {
																if (pDoc->data_k[ix_com4][iy] == 0) {
																	break;
																}
															}

															temp_m = pDoc->data_m[4][iy];
															if (!check_ix_iy(pDoc, ix_com4, iy, temp_m)) {
																//MessageBox(_T("次行けます3"), _T("実行中"), MB_OKCANCEL);
																pDoc->data_k[ix_com4][iy] = pDoc->data_m[4][iy];
																pDoc->data_l[ix_com4][iy] = 2;

																if (ix_loop == 5) {
																	//sub_Draw2(pDoc, pDC);
																	count_iy0++;
																	sub_EXEC_iy1(pDoc, pDC);
																	//MessageBox(_T("ステップ2"));
																	//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
																	if (exec_flg == 0) {
																		return;
																	}
																}
																else {

																	for (ix5 = 5; ix5 < ix_loop; ix5++) {

																		temp_m = pDoc->data_m[5][iy];
																		pDoc->data_m[5][iy] = pDoc->data_m[ix5][iy];
																		pDoc->data_m[ix5][iy] = temp_m;

																		for (ix_com5 = ix_com4; ix_com5 < 9; ix_com5++) {
																			if (pDoc->data_k[ix_com5][iy] == 0) {
																				break;
																			}
																		}

																		temp_m = pDoc->data_m[5][iy];
																		if (!check_ix_iy(pDoc, ix_com5, iy, temp_m)) {
																			//MessageBox(_T("次行けます3"), _T("実行中"), MB_OKCANCEL);
																			pDoc->data_k[ix_com5][iy] = pDoc->data_m[5][iy];
																			pDoc->data_l[ix_com5][iy] = 2;

																			if (ix_loop == 6) {
																				//sub_Draw2(pDoc, pDC);
																				count_iy0++;
																				sub_EXEC_iy1(pDoc, pDC);
																				//MessageBox(_T("ステップ2"));
																				//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
																				if (exec_flg == 0) {
																					return;
																				}
																			}
																			else {

																				for (ix6 = 6; ix6 < ix_loop; ix6++) {

																					temp_m = pDoc->data_m[6][iy];
																					pDoc->data_m[6][iy] = pDoc->data_m[ix6][iy];
																					pDoc->data_m[ix6][iy] = temp_m;

																					for (ix_com6 = ix_com5; ix_com6 < 9; ix_com6++) {
																						if (pDoc->data_k[ix_com6][iy] == 0) {
																							break;
																						}
																					}

																					temp_m = pDoc->data_m[6][iy];
																					if (!check_ix_iy(pDoc, ix_com6, iy, temp_m)) {
																						//MessageBox(_T("次行けます3"), _T("実行中"), MB_OKCANCEL);
																						pDoc->data_k[ix_com6][iy] = pDoc->data_m[6][iy];
																						pDoc->data_l[ix_com6][iy] = 2;

																						if (ix_loop == 7) {
																							//sub_Draw2(pDoc, pDC);
																							count_iy0++;
																							sub_EXEC_iy1(pDoc, pDC);
																							//MessageBox(_T("ステップ2"));
																							//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
																							if (exec_flg == 0) {
																								return;
																							}
																						}
																						else {

																							for (ix7 = 7; ix7 < ix_loop; ix7++) {

																								temp_m = pDoc->data_m[7][iy];
																								pDoc->data_m[7][iy] = pDoc->data_m[ix7][iy];
																								pDoc->data_m[ix7][iy] = temp_m;

																								for (ix_com7 = ix_com6; ix_com7 < 9; ix_com7++) {
																									if (pDoc->data_k[ix_com7][iy] == 0) {
																										break;
																									}
																								}

																								temp_m = pDoc->data_m[7][iy];
																								if (!check_ix_iy(pDoc, ix_com7, iy, temp_m)) {
																									//MessageBox(_T("次行けます3"), _T("実行中"), MB_OKCANCEL);
																									pDoc->data_k[ix_com7][iy] = pDoc->data_m[7][iy];
																									pDoc->data_l[ix_com7][iy] = 2;

																									if (ix_loop == 8) {
																										//sub_Draw2(pDoc, pDC);
																										count_iy0++;
																										sub_EXEC_iy1(pDoc, pDC);
																										//MessageBox(_T("ステップ2"));
																										//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
																										if (exec_flg == 0) {
																											return;
																										}
																									}
																									else {

																										for (ix8 = 8; ix8 < ix_loop; ix8++) {

																											temp_m = pDoc->data_m[8][iy];
																											pDoc->data_m[8][iy] = pDoc->data_m[ix8][iy];
																											pDoc->data_m[ix8][iy] = temp_m;

																											for (ix_com8 = ix_com7; ix_com8 < 9; ix_com8++) {
																												if (pDoc->data_k[ix_com8][iy] == 0) {
																													break;
																												}
																											}

																											temp_m = pDoc->data_m[8][iy];
																											if (!check_ix_iy(pDoc, ix_com8, iy, temp_m)) {
																												//MessageBox(_T("次行けます3"), _T("実行中"), MB_OKCANCEL);
																												pDoc->data_k[ix_com8][iy] = pDoc->data_m[8][iy];
																												pDoc->data_l[ix_com8][iy] = 2;

																												//sub_Draw2(pDoc, pDC);
																												count_iy0++;
																												sub_EXEC_iy1(pDoc, pDC);
																												//MessageBox(_T("ステップ2"));
																												//MessageBox(_T("終了0"), _T("実行中"), MB_OKCANCEL);
																												if (exec_flg == 0) {
																													return;
																												}

																												pDoc->data_k[ix_com8][iy] = 0;
																												pDoc->data_l[ix_com8][iy] = 0;
																											}

																											temp_m = pDoc->data_m[8][iy];
																											pDoc->data_m[8][iy] = pDoc->data_m[ix8][iy];
																											pDoc->data_m[ix8][iy] = temp_m;
																										}	// for ix8 loop end

																									}	// ix_loop == 8  else end

																									pDoc->data_k[ix_com7][iy] = 0;
																									pDoc->data_l[ix_com7][iy] = 0;
																								}

																								temp_m = pDoc->data_m[7][iy];
																								pDoc->data_m[7][iy] = pDoc->data_m[ix7][iy];
																								pDoc->data_m[ix7][iy] = temp_m;
																							}	// for ix7 loop end

																						}	// ix_loop == 7 else end

																						pDoc->data_k[ix_com6][iy] = 0;
																						pDoc->data_l[ix_com6][iy] = 0;
																					}

																					temp_m = pDoc->data_m[6][iy];
																					pDoc->data_m[6][iy] = pDoc->data_m[ix6][iy];
																					pDoc->data_m[ix6][iy] = temp_m;
																				}	// for ix6 loop end

																			}	// ix_loop == 6  else end

																			pDoc->data_k[ix_com5][iy] = 0;
																			pDoc->data_l[ix_com5][iy] = 0;
																		}

																		temp_m = pDoc->data_m[5][iy];
																		pDoc->data_m[5][iy] = pDoc->data_m[ix5][iy];
																		pDoc->data_m[ix5][iy] = temp_m;
																	}	// for ix5 loop end 

																}	// ix_loop == 5 else end

																pDoc->data_k[ix_com4][iy] = 0;
																pDoc->data_l[ix_com4][iy] = 0;
															}

															temp_m = pDoc->data_m[4][iy];
															pDoc->data_m[4][iy] = pDoc->data_m[ix4][iy];
															pDoc->data_m[ix4][iy] = temp_m;
														}	// for ix4 loop end

													}	// if ix_loop == 4  else end

													pDoc->data_k[ix_com3][iy] = 0;
													pDoc->data_l[ix_com3][iy] = 0;
												}

												temp_m = pDoc->data_m[3][iy];
												pDoc->data_m[3][iy] = pDoc->data_m[ix3][iy];
												pDoc->data_m[ix3][iy] = temp_m;
											}	// for ix3 end

										}	// if ix_loop == 3  else end

										pDoc->data_k[ix_com2][iy] = 0;
										pDoc->data_l[ix_com2][iy] = 0;
									}

									temp_m = pDoc->data_m[2][iy];
									pDoc->data_m[2][iy] = pDoc->data_m[ix2][iy];
									pDoc->data_m[ix2][iy] = temp_m;
								}	// for ix2 end

							}	// ix_loop == 2 else end

							pDoc->data_k[ix_com1][iy] = 0;
							pDoc->data_l[ix_com1][iy] = 0;
						}

						temp_m = pDoc->data_m[1][iy];
						pDoc->data_m[1][iy] = pDoc->data_m[ix1][iy];
						pDoc->data_m[ix1][iy] = temp_m;
					}	// for ix1 end

				}	// ix_loop == 1 else end

				pDoc->data_k[ix_com0][iy] = 0;
				pDoc->data_l[ix_com0][iy] = 0;
			}

			temp_m = pDoc->data_m[0][iy];
			pDoc->data_m[0][iy] = pDoc->data_m[ix0][iy];
			pDoc->data_m[ix0][iy] = temp_m;
		}		// for ix0 end

	}	// ix_loop == 0 else end
	
//	str0.Format(_T("実行件数iy0 = %d"), count_iy0);
//	MessageBox(str0);
	str0.Format(_T("全実行件数 = %d"), pDoc->counter_all);
	MessageBox(str0);

}
sub_EXEC_iy1の呼び出しが2行目の計算になる。

2行目のプログラムは、このプログラムをコピーしてアチコチの名称を0->1に変え、プログラムの初めにiy = 0;としているところをiy = 1;にするだけである。
以下同文という感じでほとんど同じプログラムを回しているだけである。


ロジックでメンドクサイのが、手動でいくつもの初期値が設定されていると、その数のループの時点で2行目の処理に行くことである。
極端な場合、全ての欄に数値が入っていた場合はix_loop(pDoc->data_n[iy]の値)が0で、何もせずに2行目に行く。

test_pgm046
テストとして、プログラムの初めの方にあるif (ix_loop == 0)の中のsub_EXEC_iy1(pDoc, pDC);をコメントにする。
また、プログラムの最後の方にあるstr0.Format(_T("実行件数iy0 = %d"), count_iy0);と次の表示している行のコメントを外し、データを設定して実行してみる。

test_pgm047
実行結果は0件と出る。

test_pgm048
2件のときは実行結果は2件と出る。

test_pgm049
3件のときは実行結果は6件と出る。


要するに順列組み合わせなので、空いたエリアの数の階乗の計算結果となるのである。
当然ながら何も設定せずに実行すると、9の階乗の362880件の実行結果となる。


test_pgm050
1行目を元に戻し、2行目の処理だけを全件行うと、12096件の解答となる。
つまり、362880×12096の解答数ということらしい。


test_pgm051
1行目、2行目を計算し、3行目の処理だけを全件行うと、216件の解答となる。
つまり、362880×12096×216の解答数ということらしい。


test_pgm052 test_pgm053
1〜3行目までを計算し、4行目を全件処理すると、どうやら12096件の解答がある場合と12072件の解答があるパターンがあるらしい。

解答の数が増えるたのは、3×3のマス目の制限から外れたからである。


test_pgm054 test_pgm055
1〜4行目までを計算し、5行目を全件処理すると、どうやら500件の解答がある場合と392件の解答があるパターンがあるらしい。


test_pgm056 test_pgm057
1〜5行目までを計算し、6行目を全件処理すると、どうやら8件の解答がある場合と0件の解答があるパターンがあるらしい。


test_pgm058 test_pgm059 test_pgm060
1〜6行目までを計算し、7行目を全件処理すると、どうやら52〜120件の多数の解答のパターンがあるらしい。

けっこう多いので、全てのパターンを出すのは断念した。


test_pgm061 test_pgm062
1〜7行目までを計算し、8行目を全件処理すると、どうやら4件の解答がある場合と8件の解答があるパターンがあるらしい。


8行目まで計算してしまうと、当然ながら9行目は無条件で決まってしまうので、調査はここまでである。

もっとも、これまで出した数字が何か意味があるかと言えば、せいぜい1行目の解答数が9の階乗で計算どおりということくらいかもしれない。




test_pgm063
プログラムが完成したので、早速、なかなか解答にたどり着けなかった問題を入れてみる。

test_pgm064
アッというまに「全実行件数が1」と出て、ちゃんと解答は1件のみということが解る。

「めでたし、めでたし・・・・・」


当初目的の「本当は複数の解答があるやもしれン」という初期値は、出てきたらその都度ここに乗せようと思っているのである。



HOME