静岡理工科大学 菅沼ホーム JavaScript 目次 索引

ナンプレ

  1. ステップ1: ゲーム枠の作成

      ナンプレ( Number Place )は,下の図に示すように,3 × 3 のマス目からなるブロックが,3 × 3 の状態に並べられており,空白部分に数字を埋めていくゲームです.通常,下の図の左に示すように,初期状態ではいくつかの数字が既に設定されており(これが,問題になります),残りの空白に数字を埋めていくことになります(下の図の右が解答結果になります).数字を埋めるルールは非常に簡単です,「各ブロック,各行( 9 マスからなる),及び,各列( 9 マスからなる)に 1 ~ 9 までの数字が 1 回だけ出現しなければならない」ということだけです.

      

      以下に示すプログラムは,問題を自分で入力し(コンピュータによって,問題を設定することも可能),その問題を自分で解くように作成されています.プログラムを作成する順序としてステップ1からステップ5までに分けて記述されていますが,各ステップで作成したプログラムはそのまま実行可能です.上位のステップになるほど,以下に示すように,問題を解くためのコンピュータによる支援が付加されていきます.

    • ルール違反のチェック(ステップ2)
    • 使用した数字の数を表示(ステップ3)
    • 指定された数字を設定することが可能な場所の表示(ステップ4)
    • 現在の状態を記憶し,後ほど,その状態に再び戻す(ステップ5)

      「ゲーム枠の作成」で説明した方法と同じような考え方で作成しますが,このゲームは CANVAS 要素を使用しないで作成しています.従って,「ゲーム枠の作成」とはかなり異なったプログラムになります.また,ゲームクリア及びゲームオーバーの画面は存在しません.以下,各クラスに対して,「ゲーム枠の作成」との違いも考慮しながら,順に説明していきます.

    1. HTML ファイル

        CANVAS 要素を使用しないため,「ゲーム枠の作成」における HTML ファイルとはかなり異なっています.この HTML ファイルと mp_start 関数の実行( 32 行目)により,ゲームの実行に必要な要素をすべて配置し,StartPanel に相当する sp_start 関数,及び,GamePanel に相当する gp_start 関数では,各要素の属性や値を変更することによってゲームを実行していきます.従って,39 行目に示すように,何らかの操作によって StartPanel に移動するといった処理を行わず,要素の配置が終了すると即座に StartPanel の状態に移動します.

        なお,DIV 要素で表された 13 行目~ 28 行目は,問題を作成するための画面ですが,当初は表示されていません.StartPanel において,「問題」ボタンをクリックすると表示されます.その内容に関する説明は,StartPanel の説明の際に行います.

      01	<!DOCTYPE HTML>
      02	<HTML>
      03	<HEAD>
      04		<TITLE>ナンプレ:ステップ1</TITLE>
      05		<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
      06		<LINK REL="stylesheet" TYPE="text/css" HREF="../../../master.css">
      07		<SCRIPT TYPE="text/javascript" SRC="main/MainPanel.js"></SCRIPT>
      08		<SCRIPT TYPE="text/javascript" SRC="start/StartPanel.js"></SCRIPT>
      09		<SCRIPT TYPE="text/javascript" SRC="game/GamePanel.js"></SCRIPT>
      10		<SCRIPT TYPE="text/javascript" SRC="problem/Problem.js"></SCRIPT>
      11	</HEAD>
      12	<BODY CLASS="eeffee">
      13		<DIV ID="problem_set_sheet" STYLE="display: none">
      14			<BUTTON CLASS="std" onClick="problem_set(1)">解</BUTTON> 
      15			空白<INPUT ID="sp" TYPE="text" SIZE="2" STYLE="font-size: 90%">
      16			<BUTTON CLASS="std" onClick="problem_set(2)">Go</BUTTON> 
      17			<BUTTON CLASS="std" onClick="problem_set(3)">設定</BUTTON>
      18			<P CLASS="center"><SCRIPT TYPE="text/javascript">
      19				for (var i1 = 0; i1 < 9; i1++) {
      20					for (var i2 = 0; i2 < 9; i2++) {
      21						var str = '<INPUT ID="tx' + i1 + i2 + '" TYPE="text" SIZE="2" STYLE="width: 30px; height: 30px; font-size: 30px; text-align: center;">'
      22						document.write(str + "\n");
      23					}
      24					document.write("<BR>\n");
      25				}
      26			</SCRIPT></P>
      27			<INPUT ID="msg" TYPE="text" SIZE="5" STYLE="font-size: 90%">
      28		</DIV>
      29	
      30		<H1>ナンプレ:ステップ1(基本)</H1>
      31		<SCRIPT TYPE="text/javascript">
      32			mp_start();
      33		</SCRIPT>
      34		<A HREF="method.htm" TARGET="method"><BUTTON ID="method" CLASS="std">遊び方</BUTTON></A>
      35		<BUTTON ID="start" CLASS="std">実行</BUTTON>
      36		<BUTTON ID="finish" CLASS="std" onClick="mp.finish()">ゲーム終了</BUTTON>
      37		<BUTTON ID="problem" CLASS="std" onClick="problem_set(0)">問題</BUTTON>
      38		<SCRIPT TYPE="text/javascript">
      39			sp_start();
      40		</SCRIPT>
      41	</BODY>
      42	</HTML>
      				

    2. MainPanel

        このプログラムでは,主として,問題の状態を保存する配列を確保し,ゲームを実行するために必要なボードをテキストフィールドの配置によって実現しています.

      01	mp = null;   // MainPanel オブジェクト
      02	
      03				//
      04				// MainPanel の開始
      05				//
      06	function mp_start()
      07	{
      08						// MainPanel オブジェクト
      09		mp = new MainPanel();
      10	}
      11				//
      12				// MainPanel オブジェクト(プロパティ)
      13				//
      14	function MainPanel()
      15	{
      16						// 問題領域と作業域の確保 : pr[i][j][k], wk[i][j][k]
      17						//    [i] : ブロック番号.左から右,上から下へ番号付け(i=0~9)
      18						//    [j][k] : 各ブロックのj行k列(j,k=0~2)
      19		this.pr = new Array();
      20		this.wk = new Array();
      21		for (var i1 = 0; i1 < 9; i1++) {
      22			this.pr[i1] = new Array();
      23			this.wk[i1] = new Array();
      24			for (var i2 = 0; i2 < 3; i2++) {
      25				this.pr[i1][i2] = new Array(0, 0, 0);
      26				this.wk[i1][i2] = new Array(0, 0, 0);
      27			}
      28		}
      29						// ゲームボード
      30		document.write('<DIV ID="bord" STYLE="width: 505px; height: 535px; margin-left: auto; margin-right: auto;">\n');
      31		for (var i1 = 0; i1 < 9; i1++) {
      32			document.write('<DIV STYLE="float: left; border-width: 3px; border-style: solid; border-color: #00ff00;">\n');
      33			this.bord(i1);
      34			if (i1%3 == 2)
      35				document.write('</DIV><BR>\n');
      36			else
      37				document.write('</DIV>\n');
      38		}
      39		document.write('</DIV>\n');
      40	
      41		return this;
      42	}
      43				//
      44				// MainPanel オブジェクト(メソッド)
      45				//
      46						// ボード(ブロック)
      47	MainPanel.prototype.bord = function(k)
      48	{
      49		for (var i1 = 0; i1 < 3; i1++) {
      50			var id1 = "bd" + k + i1;
      51			for (var i2 = 0; i2 < 3; i2++) {
      52				var id2 = id1 + i2;
      53				document.write('<INPUT ID="' + id2 + '" TYPE="text" STYLE="width: 50px; height: 50px; font-size: 50px; text-align: center;"></INPUT>');
      54			}
      55			document.write('<BR>');
      56		}
      57	}
      58						// 終了
      59	MainPanel.prototype.finish = function()
      60	{
      61		document.getElementById('method').style.display = "none";
      62		document.getElementById('start').style.display = "none";
      63		document.getElementById('finish').style.display = "none";
      64		document.getElementById('bord').style.display = "none";
      65		document.getElementById('problem').style.display = "none";
      66	}
      				
      09 行目( mp_start 関数)

        MainPanel オブジェクトを生成し,その結果をグローバル変数 mp( 01 行目)に代入しています.具体的には,14 行目~ 42 行目に記述された MainPanel 関数が実行されます.

      19 行目~ 28 行目( MainPanel 関数)

        問題( pr[9][3][3] )とその作業領域( wk[9][3][3] )のため,3 次元配列を定義し,初期設定を行っています.最初の添え字がブロック番号を表し,一番上の左のブロックが 0,その右が 1,・・・,一番下の右のブロックが 8 となります.ここで示すように,3 次元配列は,まず配列を定義し( 19,20 行目),その配列の各要素を再び配列として定義し( 22,23 行目),さらにそれらの各要素を配列として定義( 25,26 行目)することによって可能です.ここでは,すべての要素が 0 で初期設定されます.

        一般に,配列の各要素を配列として定義することによって,多次元配列を定義することが可能です.以下に示すのは 2 行 3 列の 2 次元配列の例です.
        var a = new Array(2);   // var a = new Array(); でも可
        for (var i1 = 0; i1 < 2; i1++)
        	a[i1] = new Array(3);
        					
      初期設定も同時に行いたい場合は,例えば,以下のようにして行います.
        var a = new Array(2);   // var a = new Array(); でも可
        a[0] = new Array(1, 2, 3);
        a[1] = new Array(4, 5, 6);
        					
      30 行目~ 39 行目( MainPanel 関数)

        幅 505px,高さ 535px の DIV 要素の中に,次に述べる bord メソッドを利用して,3 × 3 のテキストフィールドからなるブロックを 9 個配置しています.document.write("文字列") は,document オブジェクトのメソッドであり,ブラウザに「文字列」を書き出します.文字列内に,HTML 要素が含まれていれば,その要素は適切に解釈されブラウザに表示されます.

      47 行目~ 57 行目( bord メソッド)

        INPUT 要素を使用して,幅 50px,高さ 50px のテキストフィールドを 9 個設置しています.

      59 行目~ 66 行目( finish メソッド)

        GamePanel において,「終了」ボタンがクリックされた時の処理です.

    3. StartPanel

        StartPanel の状態は,ゲームを開始した時点,及び,GamePanel の状態において「次の問題」ボタンをクリックした時点に表示されます.そのため,ボタンやテキストフィールドの再設定等が必要になります.

      01	sp = null;   // StartPanel オブジェクト
      02				//
      03				// StartPanel の開始
      04				//
      05	function sp_start()
      06	{
      07						// StartPanel オブジェクト
      08		sp = new StartPanel();
      09	}
      10				//
      11				// StartPanel オブジェクト(プロパティ)
      12				//
      13	function StartPanel()
      14	{
      15						// 初期設定
      16		for (var i1 = 0; i1 < 9; i1++) {
      17			for (var i2 = 0; i2 < 3; i2++) {
      18				for (var i3 = 0; i3 < 3; i3++) {
      19					mp.pr[i1][i2][i3] = 0;
      20					mp.wk[i1][i2][i3] = 0;
      21					id = "bd" + i1 + i2 + i3;
      22					eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
      23					eval("document.getElementById('" + id + "').value = ''");
      24				}
      25			}
      26		}
      27						// ボタンの表示制御
      28		document.getElementById('method').style.display = "";
      29		document.getElementById('start').style.display = "";
      30		document.getElementById('start').addEventListener("click", this.onPlay);
      31		document.getElementById('finish').style.display = "none";
      32		document.getElementById('problem').style.display = "";
      33		document.getElementById('start').innerHTML = "実行";
      34	}
      35				//
      36				// StartPanel オブジェクト(メソッド)
      37				//
      38						// 「実行」ボタンがクリックされたときの処理
      39	StartPanel.prototype.onPlay = function()
      40	{
      41		for (var i1 = 0; i1 < 9; i1++) {
      42			for (var i2 = 0; i2 < 3; i2++) {
      43				for (var i3 = 0; i3 < 3; i3++) {
      44					var id = "bd" + i1 + i2 + i3;
      45					var str = eval("document.getElementById('" + id + "').value");
      46					if (str.length > 0) {   // 文字列の長さ > 0 ?
      47						if (isNaN(str))   // 非数字か ?
      48							var k = -1;
      49						else {
      50							var k = parseInt(str);   // 整数への変換
      51							if (k <= 0 || k >= 10)
      52								k = -1;
      53						}
      54						mp.pr[i1][i2][i3] = k;
      55						if (k > 0)
      56							mp.wk[i1][i2][i3] = 1;
      57					}
      58					else {
      59						mp.pr[i1][i2][i3] = 0;
      60						mp.wk[i1][i2][i3] = 0;
      61					}
      62				}
      63			}
      64		}
      65		gp_start();
      66	}
      				
      08 行目( sp_start 関数)

        StartPanel オブジェクトを生成し,その結果をグローバル変数 sp( 01 行目)に代入しています.具体的には,13 行目~ 34 行目に記述された StartPanel 関数が実行されます.

      16 行目~ 26 行目( StartPanel 関数)

        3 次元配列 pr と wk の初期設定を行うと共に,各ブロックに配置されたテキストフィールドの初期設定を行っています(背景色を白,空白の状態).eval は,JavaScript のトップレベルの関数であり,引数として渡された文字列を JavaScript の文として評価します.

      28 行目~ 33 行目( StartPanel 関数)

        ボタンの再設定を行っています.ID 属性が start であるボタンの表示内容を変え( 33 行目),さらに,マウスクリックに対するイベントリスナを付加( 30 行目)しています.この結果,このボタン(「実行」ボタン)をクリックすると onPlay メソッドが実行されます.

      39 行目~ 66 行目( onPlay メソッド)

        「実行」ボタンがクリックされた時の処理です.各ブロックのテキストフィールドに入力された数字を配列変数 pr の対応する場所に設定し,かつ,その場所の wk の値を 1 (問題として設定された部分であり,ゲーム実行中にこの値を編集不可能にするため)に設定しています.ただし,数字以外や 1 ~ 9 以外が入力されていた場合は pr に -1 を設定しています(編集不可能にはならない).何も入力されていない場合は,pr 及び wk の値を 0 に設定しています.この設定が終了した後,GamePanel に移動します( 65 行目).なお,length は,String オブジェクトのプロパティであり,isNaNparseInt は,トップレベルの関数です.

        基本的に,スタート画面において,キーボードから問題を入力し,「実行」ボタンをクリックすればナンプレを実行可能です.面倒なようであれば,コンピュータに問題を作成させることも可能です.「問題」ボタンをクリックすると,ゲーム画面の上に,問題作成用の画面が表示されます.ただし,以下に説明するように,この問題作成プログラムは完璧なものではありません.解の形を決定した後,適当に空白を作り,問題としています.そのため,解は必ず存在しますが,唯一解であるか否かのチェックは行っていません.解の形は,基本的に,以下に示すような方法で求めています.

      • 最初の 1 を,9 × 9 個のマスのいずれかにランダムに配置する.
      • 2 番目の 1 を,配置可能な位置からランダムに選択して配置する.
      • 3 番目の 1 を,配置可能な位置からランダムに選択して配置する.
      •   ・・・・・
      • 最初の 2 を,配置可能な位置からランダムに選択して配置する.
      • 2 番目の 2 を,配置可能な位置からランダムに選択して配置する.
      •   ・・・・・

        上の処理を続けると,いずれかの段階で配置可能な場所がなくなり,次に進むことができなくなるようなことがしばしば起こります.その場合は,最後に配置した数字を除き,その数字を配置可能な他の場所に配置し直します.このような候補がなくなった場合は,さらに前に戻り,同様な処理を繰り返します.つまり,バックトラック処理を行っています.しかし,

      • 組み合わせの問題から,バックトラック回数が膨大になる可能性がある.
      • ループに陥ることなく,バックトラックを実行するためには,過去の状態をすべて保存しておく必要がある.その結果,プログラムが複雑になると共に,膨大なメモリを必要とする可能性がある.

      などの理由から,バックトラックの回数が 1000 回に達すると,そこまでの状態をクリアし,1 の配置から再度実行するということを繰り返しています.問題生成画面の下のテキストフィールドに数字が表示されますが,この数字が問題生成に要した回数を表しています.例えば,15432 という数字は,1 から配置する方法を 16 回繰り返し,16 回目において,432 回のバックトラックによって解を得たことを表しています.以下に示すのが,問題作成画面で使用される関数 problem_set,及び,そこで使用される Pos オブジェクトの内容です.

      /**********************************************/
      /* 問題の作成                                 */
      /*      ssw :  = 0 : 目標とする解の決定       */
      /*             = 1 : 再度,解を求める         */
      /*             = 2 : 指定された数の空白を決定 */
      /*             = 3 : 問題をメイン画面に設定   */
      /**********************************************/
      001	function problem_set(ssw)
      002	{
      003		var pr = new Array();
      004		var p  = new Array();
      005		for (var i1 = 0; i1 < 9; i1++) {
      006			pr[i1] = new Array();
      007			p[i1]  = new Array();
      008		}
      009	
      010		if (ssw == 0 || ssw == 1) {
      011				// ページの表示
      012			if (ssw == 0) {
      013				document.getElementById('problem_set_sheet').style.display = "";
      014				window.scroll(0, 0);
      015			}
      016				// 問題の作成
      017						// 初期設定
      018			var ct1 = 0;   // バックトラック回数
      019			var ct2 = 0;   // バックトラック回数の合計
      020			var n1 = 1;   // 配置する数字
      021			var n2 = 1;   // その数字の何個目かを示す
      022			var ft = 1;   // 一番最初の数字( 1 )か否か
      023			var mp = new Array();
      024			for (var i1 = 0; i1 < 9; i1++) {
      025				for (var i2 = 0; i2 < 9; i2++)
      026					pr[i1][i2] = 0;
      027			}
      028						// 設定
      029			while (n1 <= 9) {
      030				while (n2 <= 9) {
      031								// 最初の数字
      032					if (ft > 0) {
      033						ft    = 0;
      034						var k = Math.floor(Math.random() * 81);
      035						if (k >= 81)
      036							k = 80;
      037						var k1     = Math.floor(k / 9);
      038						var k2     = k % 9;
      039						pr[k1][k2] = 1;
      040						var row1   = new Array();
      041						var col1   = new Array();
      042						for (var i1 = 0; i1 < 9; i1++) {
      043							for (var i2 = 0; i2 < 9; i2++) {
      044								if (i1 != k1 || i2 != k2) {
      045									row1.push(i1);
      046									col1.push(i2);
      047								}
      048							}
      049						}
      050						var ps = new Pos(k1, k2, 1, 1, row1, col1);
      051						mp.push(ps);
      052						n2++;
      053					}
      054								// 2番目以降の数字
      055					else {
      056						select(n1, pr, p);
      057						var row1 = new Array();
      058						var col1 = new Array();
      059						for (var i1 = 0; i1 < 9; i1++) {
      060							for (var i2 = 0; i2 < 9; i2++) {
      061								if (p[i1][i2] > 0) {
      062									row1.push(i1);
      063									col1.push(i2);
      064								}
      065							}
      066						}
      067										// 設定する場所がある場合
      068						if (row1.length > 0) {
      069							var k = Math.floor(Math.random() * row1.length);
      070							if (k >= row1.length)
      071								k = row1.length - 1;
      072							var k1     = row1[k];
      073							var k2     = col1[k];
      074							pr[k1][k2] = n1;
      075							row1.splice(k, 1);
      076							col1.splice(k, 1);
      077							var ps = new Pos(k1, k2, n1, n2, row1, col1);
      078							mp.push(ps);
      079							n2++;
      080						}
      081										// 設定する場所がない場合
      082						else {
      083							var sw = 0;
      084							while (sw == 0 && ct1 < 1000) {
      085								ct1++;
      086								var n = mp.length - 1;
      087								if (n < 0)
      088									break;
      089								else {
      090									var ps     = mp[n];
      091									var k1     = ps.r;
      092									var k2     = ps.c;
      093									pr[k1][k2] = 0;
      094									mp.pop();
      095									if (ps.row.length > 0) {
      096										sw    = 1;
      097										n1    = ps.n1;
      098										n2    = ps.n2;
      099										row1  = ps.row;
      100										col1  = ps.col;
      101										var k = Math.floor(Math.random() * row1.length);
      102										if (k >= row1.length)
      103											k = row1.length - 1;
      104										k1         = row1[k];
      105										k2         = col1[k];
      106										pr[k1][k2] = n1;
      107										row1.splice(k, 1);
      108										col1.splice(k, 1);
      109										ps = new Pos(k1, k2, n1, n2, row1, col1);
      110										mp.push(ps);
      111										n2++;
      112									}
      113								}
      114							}
      115							if (sw == 0) {
      116								ct2 += ct1;
      117								ct1  = 0;
      118								n1   = 0;
      119								n2   = 10;
      120								ft   = 1;
      121								mp.splice(0, mp.length);
      122								for (var i1 = 0; i1 < 9; i1++) {
      123									for (var i2 = 0; i2 < 9; i2++)
      124										pr[i1][i2] = 0;
      125								}
      126							}
      127						}
      128					}
      129				}
      130				n1++;
      131				n2 = 1;
      132			}
      133						// 出力
      134			ct2 += ct1;
      135			document.getElementById("msg").value = ct2;
      136			for (var i1 = 0; i1 < 9; i1++) {
      137				for (var i2 = 0; i2 < 9; i2++) {
      138					var id = "tx" + i1 + i2;
      139					document.getElementById(id).value = pr[i1][i2];
      140				}
      141			}
      142		}
      143				// 空白の生成
      144		else if (ssw == 2) {
      145			for (var i1 = 0; i1 < 9; i1++) {
      146				for (var i2 = 0; i2 < 9; i2++)
      147					pr[i1][i2] = 1;
      148			}
      149			var n = parseInt(document.getElementById("sp").value);
      150			if (n > 0) {
      151				var k = 0;
      152				while (k < n) {
      153					var k1 = Math.floor(9 * Math.random());
      154					if (k1 > 8)
      155						k1 = 8;
      156					var k2 = Math.floor(9 * Math.random());
      157					if (k2 > 8)
      158						k2 = 8;
      159					if (pr[k1][k2] != 0) {
      160						var id = "tx" + k1 + k2;
      161						document.getElementById(id).value = "";
      162						pr[k1][k2] = 0;
      163						k++;
      164					}
      165				}
      166			}
      167		}
      168				// 問題の設定
      169		else {
      170			for (var i1 = 0; i1 < 9; i1++) {
      171				for (var i2 = 0; i2 < 3; i2++) {
      172					for (var i3 = 0; i3 < 3; i3++) {
      173						var k1 = Math.floor(i1 / 3) * 3 + i2;
      174						var k2 = (i1 % 3) * 3 + i3;
      175						var bd = "bd" + i1 + i2 + i3;
      176						var tx = "tx" + k1 + k2;
      177						document.getElementById(bd).value = document.getElementById(tx).value;
      178					}
      179				}
      180			}
      181			document.getElementById('sp').value = "";
      182			document.getElementById('problem_set_sheet').style.display = "none";
      183		}
      184	}
      185				// 数字を配置できる場所の特定
      186				//   n : 数字
      187				//   pr[i][j] : 現在の状態
      188				//   p[i][j] : =0 : 配置不可能
      189				//             =1 : 配置可能
      190	function select(n, pr, p)
      191	{
      192					// 初期設定
      193		for (var i1 = 0; i1 < 9; i1++) {
      194			for (var i2 = 0; i2 < 9; i2++) {
      195				if (pr[i1][i2] == 0)
      196					p[i1][i2] = 1;
      197				else
      198					p[i1][i2] = 0;
      199			}
      200		}
      201					// ブロック内
      202		for (var i1 = 0; i1 < 9; i1++) {
      203			var k1 = Math.floor(i1 / 3) * 3;
      204			var k2 = (i1 % 3) * 3;
      205			var sw = 0;
      206			for (var i2 = k1; i2 < k1+3 && sw == 0; i2++) {
      207				for (var i3 = k2; i3 < k2+3 && sw == 0; i3++) {
      208					if (pr[i2][i3] == n)
      209						sw = 1;
      210				}
      211			}
      212			if (sw > 0) {
      213				for (var i2 = k1; i2 < k1+3; i2++) {
      214					for (var i3 = k2; i3 < k2+3; i3++)
      215						p[i2][i3] = 0;
      216				}
      217			}
      218		}
      219					// 行内
      220		for (var i1 = 0; i1 < 9; i1++) {
      221			var sw = 0;
      222			for (var i2 = 0; i2 < 9 && sw == 0; i2++) {
      223				if (pr[i1][i2] == n)
      224					sw = 1;
      225			}
      226			if (sw > 0) {
      227				for (var i2 = 0; i2 < 9; i2++)
      228					p[i1][i2] = 0;
      229			}
      230		}
      231					// 列内
      232		for (var i1 = 0; i1 < 9; i1++) {
      233			var sw = 0;
      234			for (var i2 = 0; i2 < 9 && sw == 0; i2++) {
      235				if (pr[i2][i1] == n)
      236					sw = 1;
      237			}
      238			if (sw > 0) {
      239				for (var i2 = 0; i2 < 9; i2++)
      240					p[i2][i1] = 0;
      241			}
      242		}
      243	}
      244				// オブジェクト Pos
      245	function Pos(r1, c1, m1, m2, row1, col1)
      246	{
      247		this.r   = r1;
      248		this.c   = c1;
      249		this.n1  = m1;
      250		this.n2  = m2;
      251		this.row = row1;
      252		this.col = col1;
      253	}
      				
      003 行目~ 008 行目

        2 次元配列 pr,p を定義しています.pr には設定された 1 ~ 9 ( 0 は未設定)の数字が入ります.p は作業域であり,その場所に数字を設定することが可能か否か( =1:可能,=0:不可能)を表す配列です.

      12 行目~ 15 行目

        「問題」ボタンをクリックしたときの処理であり,問題作成画面を表示すると共に,画面を一番上までスクロール( scroll )しています.

      033 行目~ 052 行目

        最初の 1 を設定している部分です.( 9 × 9 )個の場所からランダムに選択し,k1 行 k2 列に 1 を設定しています( 034 行目~ 039 行目).次に,残りの( 9 × 9 - 1 )個の場所を,配置可能な候補として,配列 row1 と col1 に保存しています( 040 行目~ 049 行目,pushArray オブジェクトのメソッド).これらの情報に基づき,Pos オブジェクトを生成し( 050 行目),そのオブジェクトを配列 mp に記憶しています( 051 行目).Pos オブジェクトは,245 行目~ 253 行目に示してあるように,数字 n1 の n2 個目を,r 行 c 列に設定したとき,r 行 c 列以外の配置可能場所を,配列 row と col に記憶しています.

      056 行目~ 066 行目

        数字 n1 の配置可能な場所を調べ( 056 行目),その結果を,配列 row1 と col1 に保存しています.

      069 行目~ 079 行目

        配置可能な場所が存在した場合の処理です.配置可能な場所から配置場所をランダムに選択し( 069 行目~ 073 行目),その場所に数字 n1 を設定しています( 074 行目).配置した場所を配置可能な場所の候補から削除した後( 075.076 行目,spliceArray オブジェクトのメソッド),Pos オブジェクトを生成し,そのオブジェクトを配列 mp に記憶しています.なお,配列.length における length は,Array オブジェクトのプロパティであり,配列の要素数を表します.

      083 行目~ 126 行目

        配置可能な場所が存在しなかった場合の処理です.083 行目~ 114 行目が,バックトラックの実行部分です.配列 mp から,一つ前の情報を抜き出し,配置可能な場所が存在した場合は配置を実行し,存在しなければ,さらに前に戻るという処理を実行しています.また,115 行目~ 126 行目は,バックトラック回数が 1000 回に達した場合の処理であり,最初の 1 の配置に戻すための設定です.なお,088 行目の break 文は,繰り返しの終了などに使用される文であり,この例の場合,break 文が実行されると,繰り返しを終了し,while 文( 084 行目~ 114 行目)の外に出ます( while 文の次の文である 115 行目が実行される).

      134 行目~ 141 行目

        バックトラック回数を問題作成画面の下のテキストフィールドに設定( 135 行目)すると共に,生成された問題を問題作成画面に設定しています.ct2 に対する制限が付けてありませんので,029,030 行目の while 文によって無限ループに入る可能性もありますので,ご注意ください.

      145 行目~ 166 行目

        問題作成画面の「Go」ボタンがクリックされたときの処理です.空白表示の横のテキストフィールドに空白の数を入力し,このボタンをクリックすると,入力された空白の数 n だけランダムに数字が空白に置き換わります.また,0 以下の値を設定しても,何も起こりません.なお,このボタンを使用せず,各テキストエリアを直接編集することも可能です.

      170 行目~ 182 行目

        問題作成画面の「設定」ボタンがクリックされたときの処理です.このボタンをクリックすると,問題がゲーム本体のボードに設定されます.その後,問題作成用の画面を非表示にしています( 182 行目).

      190 行目~ 243 行目

        数字 n1 を配置可能な場所を調べ,その結果を配列 p に設定しています.

      245 行目~ 253 行目

        Pos オブジェクトの定義です.

    4. GamePanel

        GamePanel では,ゲームを実行するため,ボタンやテキストフィールドの再設定を行っています.

      01	gp = null;   // GamePanel オブジェクト
      02	
      03				//
      04				// GamePanel の開始
      05				//
      06	function gp_start()
      07	{
      08						// GamePanel オブジェクト
      09		gp = new GamePanel();
      10	}
      11				//
      12				// GamePanel オブジェクト(プロパティ)
      13				//
      14	function GamePanel()
      15	{
      16						// ボードの制御
      17		for (var i1 = 0; i1 < 9; i1++) {
      18			for (var i2 = 0; i2 < 3; i2++) {
      19				for (var i3 = 0; i3 < 3; i3++) {
      20					var id = "bd" + i1 + i2 + i3;
      21					if (mp.wk[i1][i2][i3] == 1) {
      22						eval("document.getElementById('" + id + "').style.backgroundColor = '#c8ffc8'");
      23						eval("document.getElementById('" + id + "').readOnly = 'true'");
      24					}
      25					else {
      26						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
      27						eval("document.getElementById('" + id + "').readOnly = ''");
      28					}
      29				}
      30			}
      31		}
      32						// ボタンの表示制御
      33		document.getElementById('start').removeEventListener("click", sp.onPlay);
      34		document.getElementById('method').style.display = "none";
      35		document.getElementById('start').style.display = "";
      36		document.getElementById('finish').style.display = "";
      37		document.getElementById('problem').style.display = "none";
      38		document.getElementById('start').innerHTML = "次の問題";
      39		document.getElementById('start').addEventListener("click", this.onNext);
      40	
      41		return this;
      42	}
      43				//
      44				// GamePanel オブジェクト(メソッド)
      45				//
      46						// 「次の問題」ボタンがクリックされたときの処理
      47	GamePanel.prototype.onNext = function()
      48	{
      49		for (var i1 = 0; i1 < 9; i1++) {
      50			for (var i2 = 0; i2 < 3; i2++) {
      51				for (var i3 = 0; i3 < 3; i3++) {
      52					var id = "bd" + i1 + i2 + i3;
      53					if (mp.wk[i1][i2][i3] == 1)
      54						eval("document.getElementById('" + id + "').readOnly = ''");
      55				}
      56			}
      57		}
      58		document.getElementById('start').removeEventListener("click", gp.onNext);
      59		sp_start();
      60	}
      				
      09 行目( gp_start 関数)

        GamePanel オブジェクトを生成し,その結果をグローバル変数 gp( 01 行目)に代入しています.具体的には,14 行目~ 42 行目に記述された GamePanel 関数が実行されます.

      17 行目~ 31 行目( GamePanel 関数)

        変数 wk の値が 1 である場所に対応するテキストフィールドの背景色を薄い緑(RGB : C8FFC8 ),かつ,編集不可能に設定しています( 22,23 行目).また,それ以外のテキストフィールドに対しては,背景色を白,かつ,編集可能にしています( 26,27 行目).

      33 行目~ 39 行目( GamePanel 関数)

        ボタンの再設定を行っています.ID 属性が start であるボタンからイベントリスナを取り除き( 33 行目),ボタンの表示内容を変え( 38 行目),さらに,マウスクリックに対する新たなイベントリスナを付加( 39 行目)しています.この結果,このボタン(「次の問題」ボタン)をクリックすると onNext メソッドが実行されます.

      47 行目~ 60 行目( onNext メソッド)

        「次の問題」ボタンがクリックされた時の処理です.変数 wk の値が 1 である場所に対応するテキストフィールドを編集可能にし( 53,54 行目),「次の問題」ボタンからイベントリスナを取り除き( 58 行目),その後,StartPanel に移動します( 59 行目).

  2. ステップ2: ルール違反のチェック

      ステップ1の段階で,ゲームを実行することは可能ですが,ルール違反を起こしていないか否かをチェックすることはかなり面倒な作業になります.ここでは,ルール違反が起こった時それを警告する機能を付加します.修正するプログラムは,StartPanel と GamePanel です.まず,StartPanel では,「実行」ボタンがクリックされた時の処理を修正します.

    001	sp = null;   // StartPanel オブジェクト
    002				//
    003				// StartPanel の開始
    004				//
    005	function sp_start()
    006	{
    007						// StartPanel オブジェクト
    008		sp = new StartPanel();
    009	}
    010				//
    011				// StartPanel オブジェクト(プロパティ)
    012				//
    013	function StartPanel()
    014	{
    015						// 初期設定
    016		for (var i1 = 0; i1 < 9; i1++) {
    017			for (var i2 = 0; i2 < 3; i2++) {
    018				for (var i3 = 0; i3 < 3; i3++) {
    019					mp.pr[i1][i2][i3] = 0;
    020					mp.wk[i1][i2][i3] = 0;
    021					id = "bd" + i1 + i2 + i3;
    022					eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    023					eval("document.getElementById('" + id + "').value = ''");
    024				}
    025			}
    026		}
    027						// ボタンの表示制御
    028		document.getElementById('method').style.display = "";
    029		document.getElementById('start').style.display = "";
    030		document.getElementById('start').addEventListener("click", this.onPlay);
    031		document.getElementById('finish').style.display = "none";
    032		document.getElementById('problem').style.display = "";
    033		document.getElementById('start').innerHTML = "実行";
    034	}
    035				//
    036				// StartPanel オブジェクト(メソッド)
    037				//
    038						// 「実行」ボタンがクリックされたときの処理
    039	StartPanel.prototype.onPlay = function()
    040	{
    041		for (var i1 = 0; i1 < 9; i1++) {
    042			for (var i2 = 0; i2 < 3; i2++) {
    043				for (var i3 = 0; i3 < 3; i3++) {
    044					var id = "bd" + i1 + i2 + i3;
    045					var str = eval("document.getElementById('" + id + "').value");
    046					if (str.length > 0) {
    047						if (isNaN(str))
    048							var k = -1;
    049						else {
    050							var k = parseInt(str);
    051							if (k == 0)
    052								k = -1;
    053						}
    054						mp.pr[i1][i2][i3] = k;
    055						mp.wk[i1][i2][i3] = 1;
    056					}
    057					else {
    058						mp.pr[i1][i2][i3] = 0;
    059						mp.wk[i1][i2][i3] = 0;
    060					}
    061				}
    062			}
    063		}
    064		var sw = sp.check();
    065		if (sw > 0)
    066			gp_start();
    067	}
    068						// データは適切か?
    069	StartPanel.prototype.check = function()
    070	{
    071		var sw = 1;
    072								// 数字の適切性
    073		for (var i1 = 0; i1 < 9; i1++) {
    074			for (var i2 = 0; i2 < 3; i2++) {
    075				for (var i3 = 0; i3 < 3; i3++) {
    076					var id = "bd" + i1 + i2 + i3;
    077					eval("document.getElementById('" + id + "').style.color = '#000000'");
    078					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    079						sw = 0;
    080						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    081					}
    082				}
    083			}
    084		}
    085								// ブロック内
    086		if (sw > 0) {
    087			for (var i1 = 0; i1 < 9; i1++) {
    088				for (var i2 = 0; i2 < 8; i2++) {
    089					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    090					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    091					if (k1 > 0) {
    092						for (var i3 = i2+1; i3 < 9; i3++) {
    093							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    094							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    095							if (k1 == k2) {
    096								sw = 0;
    097								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    098								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    099							}
    100						}
    101					}
    102				}
    103			}
    104		}
    105								// 行内
    106		if (sw > 0) {
    107			for (var i1 = 0; i1 < 9; i1++) {
    108				for (var i2 = 0; i2 < 8; i2++) {
    109					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    110					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    111					if (k1 > 0) {
    112						for (var i3 = i2+1; i3 < 9; i3++) {
    113							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    114							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    115							if (k1 == k2) {
    116								sw = 0;
    117								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    118								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    119							}
    120						}
    121					}
    122				}
    123			}
    124		}
    125								// 列内
    126		if (sw > 0) {
    127			for (var i1 = 0; i1 < 9; i1++) {
    128				for (var i2 = 0; i2 < 8; i2++) {
    129					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    130					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    131					if (k1 > 0) {
    132						for (var i3 = i2+1; i3 < 9; i3++) {
    133							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    134							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    135							if (k1 == k2) {
    136								sw = 0;
    137								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    138								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    139							}
    140						}
    141					}
    142				}
    143			}
    144		}
    145	
    146		return sw;
    147	}
    			
    064 行目~ 066 行目( onPlay メソッド)

      ルール違反を起こしていないか否かのチェックをメソッド check で行い,問題が無ければ GamePanel に移動します.

    069 行目~ 147 行目( check メソッド)

      ルール違反を起こしていないか否かのチェックを行うためのメソッドです.073 行目~ 084 行目においては,1 ~ 9 以外の数字または文字が使用されているか否かを調べています.もし,それらの数字や文字が使用されていた場合は文字色を赤に設定します.以下,086 行目~ 104 行目では同じブロック内,106 行目~ 124 行目では同じ行内,及び,126 行目~ 144 行目では同じ列内に,同じ数字が複数回使用されていないか否かをチェックし,同様の処理を行っています.

      GamePanel においては,テキストフィールドにイベントリスナを付加し,テキストフィールドの内容が変化した時ルール違反のチェックを行っています.

    001	gp = null;   // GamePanel オブジェクト
    002	
    003				//
    004				// GamePanel の開始
    005				//
    006	function gp_start()
    007	{
    008						// GamePanel オブジェクト
    009		gp = new GamePanel();
    010	}
    011				//
    012				// GamePanel オブジェクト(プロパティ)
    013				//
    014	function GamePanel()
    015	{
    016						// ボードの制御
    017		for (var i1 = 0; i1 < 9; i1++) {
    018			for (var i2 = 0; i2 < 3; i2++) {
    019				for (var i3 = 0; i3 < 3; i3++) {
    020					var id = "bd" + i1 + i2 + i3;
    021					if (mp.wk[i1][i2][i3] == 1) {
    022						eval("document.getElementById('" + id + "').style.backgroundColor = '#c8ffc8'");
    023						eval("document.getElementById('" + id + "').readOnly = 'true'");
    024					}
    025					else {
    026						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    027						eval("document.getElementById('" + id + "').readOnly = ''");
    028						eval("document.getElementById('" + id + "').addEventListener('input', this.onInput)");
    029					}
    030				}
    031			}
    032		}
    033						// ボタンの表示制御
    034		document.getElementById('start').removeEventListener("click", sp.onPlay);
    035		document.getElementById('method').style.display = "none";
    036		document.getElementById('start').style.display = "";
    037		document.getElementById('finish').style.display = "";
    038		document.getElementById('problem').style.display = "none";
    039		document.getElementById('start').innerHTML = "次の問題";
    040		document.getElementById('start').addEventListener("click", this.onNext);
    041	
    042		return this;
    043	}
    044				//
    045				// GamePanel オブジェクト(メソッド)
    046				//
    047						// 「次の問題」ボタンがクリックされたときの処理
    048	GamePanel.prototype.onNext = function()
    049	{
    050		for (var i1 = 0; i1 < 9; i1++) {
    051			for (var i2 = 0; i2 < 3; i2++) {
    052				for (var i3 = 0; i3 < 3; i3++) {
    053					var id = "bd" + i1 + i2 + i3;
    054					if (mp.wk[i1][i2][i3] != 1)
    055						eval("document.getElementById('" + id + "').removeEventListener('input', gp.onInput)");
    056					else
    057						eval("document.getElementById('" + id + "').readOnly = ''");
    058				}
    059			}
    060		}
    061		document.getElementById('start').removeEventListener("click", gp.onNext);
    062		sp_start();
    063	}
    064						// ゲーム版のテキストフィールドに入力された時の処理
    065	GamePanel.prototype.onInput = function()
    066	{
    067		for (var i1 = 0; i1 < 9; i1++) {
    068			for (var i2 = 0; i2 < 3; i2++) {
    069				for (var i3 = 0; i3 < 3; i3++) {
    070					var id = "bd" + i1 + i2 + i3;
    071					var str = eval("document.getElementById('" + id + "').value");
    072					if (str.length <= 0)
    073						k = 0;
    074					else {
    075						if (isNaN(str))
    076							var k = -1;
    077						else {
    078							var k = parseInt(str);
    079							if (k == 0)
    080								k = -1;
    081						}
    082					}
    083					mp.pr[i1][i2][i3] = k;
    084				}
    085			}
    086		}
    087		var sw = gp.check();
    088	}
    089						// データは適切か?
    090	GamePanel.prototype.check = function()
    091	{
    092		var sw = 1;
    093								// 数字の適切性
    094		for (var i1 = 0; i1 < 9; i1++) {
    095			for (var i2 = 0; i2 < 3; i2++) {
    096				for (var i3 = 0; i3 < 3; i3++) {
    097					var id = "bd" + i1 + i2 + i3;
    098					eval("document.getElementById('" + id + "').style.color = '#000000'");
    099					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    100						sw = 0;
    101						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    102					}
    103				}
    104			}
    105		}
    106								// ブロック内
    107		if (sw > 0) {
    108			for (var i1 = 0; i1 < 9; i1++) {
    109				for (var i2 = 0; i2 < 8; i2++) {
    110					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    111					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    112					if (k1 > 0) {
    113						for (var i3 = i2+1; i3 < 9; i3++) {
    114							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    115							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    116							if (k1 == k2) {
    117								sw = 0;
    118								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    119								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    120							}
    121						}
    122					}
    123				}
    124			}
    125		}
    126								// 行内
    127		if (sw > 0) {
    128			for (var i1 = 0; i1 < 9; i1++) {
    129				for (var i2 = 0; i2 < 8; i2++) {
    130					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    131					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    132					if (k1 > 0) {
    133						for (var i3 = i2+1; i3 < 9; i3++) {
    134							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    135							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    136							if (k1 == k2) {
    137								sw = 0;
    138								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    139								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    140							}
    141						}
    142					}
    143				}
    144			}
    145		}
    146								// 列内
    147		if (sw > 0) {
    148			for (var i1 = 0; i1 < 9; i1++) {
    149				for (var i2 = 0; i2 < 8; i2++) {
    150					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    151					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    152					if (k1 > 0) {
    153						for (var i3 = i2+1; i3 < 9; i3++) {
    154							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    155							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    156							if (k1 == k2) {
    157								sw = 0;
    158								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    159								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    160							}
    161						}
    162					}
    163				}
    164			}
    165		}
    166	
    167		return sw;
    168	}
    			
    028 行目( GamePanel 関数)

      各ブロックのテキストフィールドにイベントリスナを付加しています.この結果,テキストフィールドの内容が変化すると onInput メソッドが実行されます.

    065 行目~ 088 行目( onInput メソッド)

      テキストフィールドの内容が変化した時に呼ばれるメソッドです.テキストフィールドの内容が 0 以外の数字である場合はその数字が,また,0 または文字である場合は -1 が配列変数 pr に設定され,ルール違反か否かのチェックを行うメソッド check が呼ばれます.なお,この段階においては,メソッド check の内容は,StartPanel のメソッド check と同じです.

  3. ステップ3: 数字の出現回数の表示

      ここでは,1 から 9 までの数字に対して,各数字が盤面全体に現れる回数をカウントし,その値を表示する機能を付加しています.ゲーム終了時点では,すべての数字の出現回数が 9 になるはずです.修正するプログラムは,MainPanel,StartPanel,及び,GamePanel です.まず,MainPanel では,1 から 9 までの数字を表示するボタン(以後,「数字」ボタンと呼ぶ)と,各数字の出現回数を表示するためのテキストフィールド(以後,カウンタフィールドと呼ぶ)を設置します.

    01	mp = null;   // MainPanel オブジェクト
    02	
    03				//
    04				// MainPanel の開始
    05				//
    06	function mp_start()
    07	{
    08						// MainPanel オブジェクト
    09		mp = new MainPanel();
    10	}
    11				//
    12				// MainPanel オブジェクト(プロパティ)
    13				//
    14	function MainPanel()
    15	{
    16						// 問題領域と作業域の確保 : pr[i][j][k], wk[i][j][k]
    17						//    [i] : ブロック番号.左から右,上から下へ番号付け(i=0~9)
    18						//    [j][k] : 各ブロックのj行k列(j,k=0~2)
    19		this.pr = new Array();
    20		this.wk = new Array();
    21		for (var i1 = 0; i1 < 9; i1++) {
    22			this.pr[i1] = new Array();
    23			this.wk[i1] = new Array();
    24			for (var i2 = 0; i2 < 3; i2++) {
    25				this.pr[i1][i2] = new Array(0, 0, 0);
    26				this.wk[i1][i2] = new Array(0, 0, 0);
    27			}
    28		}
    29						// ゲームボード
    30		document.write('<DIV ID="bord" STYLE="width: 505px; height: 535px; margin-left: auto; margin-right: auto;">\n');
    31		for (var i1 = 0; i1 < 9; i1++) {
    32			document.write('<DIV STYLE="float: left; border-width: 3px; border-style: solid; border-color: #00ff00;">\n');
    33			this.bord(i1);
    34			if (i1%3 == 2)
    35				document.write('</DIV><BR>\n');
    36			else
    37				document.write('</DIV>\n');
    38		}
    39		document.write('</DIV>\n');
    40						// 「数字」ボタン
    41		document.write('<DIV ID="num" STYLE="width: 550px; height: 30px; margin-left: auto; margin-right: auto;">\n');
    42		for (var i1 = 0; i1 < 9; i1++) {
    43			var id1 = "num_b" + i1;
    44			document.write('<BUTTON ID="' + id1 + '" STYLE="width: 49px; height: 25px; font-size: 20px; text-align: center;">' + (i1+1) + '</BUTTON>\n');
    45		}
    46		document.write('</DIV>\n');
    47						// カウンタ
    48		document.write('<DIV ID="count" STYLE="width: 550px; height: 30px; margin-left: auto; margin-right: auto;">\n');
    49		for (var i1 = 0; i1 < 9; i1++) {
    50			var id1 = "ct" + i1;
    51			document.write('<INPUT ID="' + id1 + '" TYPE="text" STYLE="width: 45px; height: 25px; font-size: 20px; text-align: center;">\n');
    52		}
    53		document.write('</DIV>\n');
    54	
    55		return this;
    56	}
    57				//
    58				// MainPanel オブジェクト(メソッド)
    59				//
    60						// ボード(ブロック)
    61	MainPanel.prototype.bord = function(k)
    62	{
    63		for (var i1 = 0; i1 < 3; i1++) {
    64			var id1 = "bd" + k + i1;
    65			for (var i2 = 0; i2 < 3; i2++) {
    66				var id2 = id1 + i2;
    67				document.write('<INPUT ID="' + id2 + '" TYPE="text" STYLE="width: 50px; height: 50px; font-size: 50px; text-align: center;"></INPUT>');
    68			}
    69			document.write('<BR>');
    70		}
    71	}
    72						// 終了
    73	MainPanel.prototype.finish = function()
    74	{
    75		document.getElementById('method').style.display = "none";
    76		document.getElementById('start').style.display = "none";
    77		document.getElementById('finish').style.display = "none";
    78		document.getElementById('bord').style.display = "none";
    79		document.getElementById('num').style.display = "none";
    80		document.getElementById('count').style.display = "none";
    81		document.getElementById('problem').style.display = "none";
    82	}
    			
    41 行目~ 46 行目( MainPanel 関数)

      「数字」ボタンを設置しています.

    48 行目~ 53 行目( MainPanel 関数)

      カウンターフィールドを設置しています.

    79 行目~ 80 行目( MainPanel 関数)

      「数字」ボタンとカウンタフィールドを表示しないようにしています.

      StartPanel における修正は簡単です.032,033 行目において,「数字」ボタンとカウンタフィールドを表示しないようにしているだけです.

    001	sp = null;   // StartPanel オブジェクト
    002				//
    003				// StartPanel の開始
    004				//
    005	function sp_start()
    006	{
    007						// StartPanel オブジェクト
    008		sp = new StartPanel();
    009	}
    010				//
    011				// StartPanel オブジェクト(プロパティ)
    012				//
    013	function StartPanel()
    014	{
    015						// 初期設定
    016		for (var i1 = 0; i1 < 9; i1++) {
    017			for (var i2 = 0; i2 < 3; i2++) {
    018				for (var i3 = 0; i3 < 3; i3++) {
    019					mp.pr[i1][i2][i3] = 0;
    020					mp.wk[i1][i2][i3] = 0;
    021					id = "bd" + i1 + i2 + i3;
    022					eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    023					eval("document.getElementById('" + id + "').value = ''");
    024				}
    025			}
    026		}
    027						// ボタンの表示制御
    028		document.getElementById('method').style.display = "";
    029		document.getElementById('start').style.display = "";
    030		document.getElementById('start').addEventListener("click", this.onPlay);
    031		document.getElementById('finish').style.display = "none";
    032		document.getElementById('num').style.display = "none";
    033		document.getElementById('count').style.display = "none";
    034		document.getElementById('problem').style.display = "";
    035		document.getElementById('start').innerHTML = "実行";
    036	}
    037				//
    038				// StartPanel オブジェクト(メソッド)
    039				//
    040						// 「実行」ボタンがクリックされたときの処理
    041	StartPanel.prototype.onPlay = function()
    042	{
    043		for (var i1 = 0; i1 < 9; i1++) {
    044			for (var i2 = 0; i2 < 3; i2++) {
    045				for (var i3 = 0; i3 < 3; i3++) {
    046					var id = "bd" + i1 + i2 + i3;
    047					var str = eval("document.getElementById('" + id + "').value");
    048					if (str.length > 0) {
    049						if (isNaN(str))
    050							var k = -1;
    051						else {
    052							var k = parseInt(str);
    053							if (k == 0)
    054								k = -1;
    055						}
    056						mp.pr[i1][i2][i3] = k;
    057						mp.wk[i1][i2][i3] = 1;
    058					}
    059					else {
    060						mp.pr[i1][i2][i3] = 0;
    061						mp.wk[i1][i2][i3] = 0;
    062					}
    063				}
    064			}
    065		}
    066		var sw = sp.check();
    067		if (sw > 0)
    068			gp_start();
    069	}
    070						// データは適切か?
    071	StartPanel.prototype.check = function()
    072	{
    073		var sw = 1;
    074								// 数字の適切性
    075		for (var i1 = 0; i1 < 9; i1++) {
    076			for (var i2 = 0; i2 < 3; i2++) {
    077				for (var i3 = 0; i3 < 3; i3++) {
    078					var id = "bd" + i1 + i2 + i3;
    079					eval("document.getElementById('" + id + "').style.color = '#000000'");
    080					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    081						sw = 0;
    082						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    083					}
    084				}
    085			}
    086		}
    087								// ブロック内
    088		if (sw > 0) {
    089			for (var i1 = 0; i1 < 9; i1++) {
    090				for (var i2 = 0; i2 < 8; i2++) {
    091					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    092					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    093					if (k1 > 0) {
    094						for (var i3 = i2+1; i3 < 9; i3++) {
    095							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    096							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    097							if (k1 == k2) {
    098								sw = 0;
    099								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    100								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    101							}
    102						}
    103					}
    104				}
    105			}
    106		}
    107								// 行内
    108		if (sw > 0) {
    109			for (var i1 = 0; i1 < 9; i1++) {
    110				for (var i2 = 0; i2 < 8; i2++) {
    111					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    112					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    113					if (k1 > 0) {
    114						for (var i3 = i2+1; i3 < 9; i3++) {
    115							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    116							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    117							if (k1 == k2) {
    118								sw = 0;
    119								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    120								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    121							}
    122						}
    123					}
    124				}
    125			}
    126		}
    127								// 列内
    128		if (sw > 0) {
    129			for (var i1 = 0; i1 < 9; i1++) {
    130				for (var i2 = 0; i2 < 8; i2++) {
    131					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    132					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    133					if (k1 > 0) {
    134						for (var i3 = i2+1; i3 < 9; i3++) {
    135							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    136							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    137							if (k1 == k2) {
    138								sw = 0;
    139								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    140								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    141							}
    142						}
    143					}
    144				}
    145			}
    146		}
    147	
    148		return sw;
    149	}
    			

      最後は,GamePanel における修正です.

    001	gp = null;   // GamePanel オブジェクト
    002	
    003				//
    004				// GamePanel の開始
    005				//
    006	function gp_start()
    007	{
    008						// GamePanel オブジェクト
    009		gp = new GamePanel();
    010	}
    011				//
    012				// GamePanel オブジェクト(プロパティ)
    013				//
    014	function GamePanel()
    015	{
    016						// ボードの制御
    017		for (var i1 = 0; i1 < 9; i1++) {
    018			for (var i2 = 0; i2 < 3; i2++) {
    019				for (var i3 = 0; i3 < 3; i3++) {
    020					var id = "bd" + i1 + i2 + i3;
    021					if (mp.wk[i1][i2][i3] == 1) {
    022						eval("document.getElementById('" + id + "').style.backgroundColor = '#c8ffc8'");
    023						eval("document.getElementById('" + id + "').readOnly = 'true'");
    024					}
    025					else {
    026						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    027						eval("document.getElementById('" + id + "').readOnly = ''");
    028						eval("document.getElementById('" + id + "').addEventListener('input', this.onInput)");
    029					}
    030				}
    031			}
    032		}
    033						// ボタンの表示制御
    034		document.getElementById('start').removeEventListener("click", sp.onPlay);
    035		document.getElementById('num').style.display = "";
    036		document.getElementById('count').style.display = "";
    037		document.getElementById('method').style.display = "none";
    038		document.getElementById('start').style.display = "";
    039		document.getElementById('finish').style.display = "";
    040		document.getElementById('problem').style.display = "none";
    041		document.getElementById('start').innerHTML = "次の問題";
    042		document.getElementById('start').addEventListener("click", this.onNext);
    043						// 数字の数を数える
    044		this.count();
    045	
    046		return this;
    047	}
    048				//
    049				// GamePanel オブジェクト(メソッド)
    050				//
    051						// 「次の問題」ボタンがクリックされたときの処理
    052	GamePanel.prototype.onNext = function()
    053	{
    054		for (var i1 = 0; i1 < 9; i1++) {
    055			for (var i2 = 0; i2 < 3; i2++) {
    056				for (var i3 = 0; i3 < 3; i3++) {
    057					var id = "bd" + i1 + i2 + i3;
    058					if (mp.wk[i1][i2][i3] != 1)
    059						eval("document.getElementById('" + id + "').removeEventListener('input', gp.onInput)");
    060					else
    061						eval("document.getElementById('" + id + "').readOnly = ''");
    062				}
    063			}
    064		}
    065		document.getElementById('start').removeEventListener("click", gp.onNext);
    066		sp_start();
    067	}
    068						// ゲーム版のテキストフィールドに入力された時の処理
    069	GamePanel.prototype.onInput = function()
    070	{
    071		for (var i1 = 0; i1 < 9; i1++) {
    072			for (var i2 = 0; i2 < 3; i2++) {
    073				for (var i3 = 0; i3 < 3; i3++) {
    074					var id = "bd" + i1 + i2 + i3;
    075					var str = eval("document.getElementById('" + id + "').value");
    076					if (str.length <= 0)
    077						k = 0;
    078					else {
    079						if (isNaN(str))
    080							var k = -1;
    081						else {
    082							var k = parseInt(str);
    083							if (k == 0)
    084								k = -1;
    085						}
    086					}
    087					mp.pr[i1][i2][i3] = k;
    088				}
    089			}
    090		}
    091		var sw = gp.check();
    092		if (sw > 0)
    093			gp.count();
    094	}
    095						// データは適切か?
    096	GamePanel.prototype.check = function()
    097	{
    098		var sw = 1;
    099								// 数字の適切性
    100		for (var i1 = 0; i1 < 9; i1++) {
    101			for (var i2 = 0; i2 < 3; i2++) {
    102				for (var i3 = 0; i3 < 3; i3++) {
    103					var id = "bd" + i1 + i2 + i3;
    104					eval("document.getElementById('" + id + "').style.color = '#000000'");
    105					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    106						sw = 0;
    107						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    108					}
    109				}
    110			}
    111		}
    112								// ブロック内
    113		if (sw > 0) {
    114			for (var i1 = 0; i1 < 9; i1++) {
    115				for (var i2 = 0; i2 < 8; i2++) {
    116					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    117					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    118					if (k1 > 0) {
    119						for (var i3 = i2+1; i3 < 9; i3++) {
    120							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    121							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    122							if (k1 == k2) {
    123								sw = 0;
    124								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    125								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    126							}
    127						}
    128					}
    129				}
    130			}
    131		}
    132								// 行内
    133		if (sw > 0) {
    134			for (var i1 = 0; i1 < 9; i1++) {
    135				for (var i2 = 0; i2 < 8; i2++) {
    136					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    137					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    138					if (k1 > 0) {
    139						for (var i3 = i2+1; i3 < 9; i3++) {
    140							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    141							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    142							if (k1 == k2) {
    143								sw = 0;
    144								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    145								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    146							}
    147						}
    148					}
    149				}
    150			}
    151		}
    152								// 列内
    153		if (sw > 0) {
    154			for (var i1 = 0; i1 < 9; i1++) {
    155				for (var i2 = 0; i2 < 8; i2++) {
    156					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    157					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    158					if (k1 > 0) {
    159						for (var i3 = i2+1; i3 < 9; i3++) {
    160							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    161							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    162							if (k1 == k2) {
    163								sw = 0;
    164								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    165								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    166							}
    167						}
    168					}
    169				}
    170			}
    171		}
    172	
    173		return sw;
    174	}
    175						// 数字の数を数える
    176	GamePanel.prototype.count = function()
    177	{
    178		for (var i1 = 0; i1 < 9; i1++) {
    179			var k1 = i1 + 1;
    180			var n = 0;
    181			for (var i2 = 0; i2 < 9; i2++) {
    182				for (var i3 = 0; i3 < 3; i3++) {
    183					for (var i4 = 0; i4 < 3; i4++) {
    184						if (mp.pr[i2][i3][i4] == k1)
    185							n++;
    186					}
    187				}
    188			}
    189			var id = "ct" + i1;
    190			eval("document.getElementById('" + id + "').value = " + n);
    191			if (n == 9)
    192				eval("document.getElementById('" + id + "').style.backgroundColor = '#ff0000'");
    193			else
    194				eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    195		}
    196	}
    			
    035 行目~ 036 行目( GamePanel 関数)

      「数字」ボタンとカウンターフィールドを表示しています.

    044 行目( GamePanel 関数)

      数字をカウントするためのメソッド count を呼んでいます.

    092 行目~ 093 行目( onInput メソッド)

      ルール違反を犯していない場合,数字をカウントするためのメソッド count を呼んでいます.

    176 行目~ 196 行目( count メソッド)

      数字をカウントし,カウンタフィールドに表示するためのメソッドです.カウンタフィールドに数字を設定( 190 行目)した後,数字の出現回数が 9 の場合はカウンタフィールドの背景色を赤に,そうでない場合は白に設定しています(191 行目~ 194 行目).

  4. ステップ4: 数字入力可能位置の表示

      「数字」ボタンをクリックすると,その数字を入力できる場所を表示する機能を追加します.修正するプログラムは,MainPanel と GamePanel です.MainPanel における修正は,「数字」ボタンに対して,ボタンをクリックした時 GamePanel オブジェクトのメソッド onSafe を実行するように,onClick 属性を付加するだけです( 44 行目).

    01	mp = null;   // MainPanel オブジェクト
    02	
    03				//
    04				// MainPanel の開始
    05				//
    06	function mp_start()
    07	{
    08						// MainPanel オブジェクト
    09		mp = new MainPanel();
    10	}
    11				//
    12				// MainPanel オブジェクト(プロパティ)
    13				//
    14	function MainPanel()
    15	{
    16						// 問題領域と作業域の確保 : pr[i][j][k], wk[i][j][k]
    17						//    [i] : ブロック番号.左から右,上から下へ番号付け(i=0~9)
    18						//    [j][k] : 各ブロックのj行k列(j,k=0~2)
    19		this.pr = new Array();
    20		this.wk = new Array();
    21		for (var i1 = 0; i1 < 9; i1++) {
    22			this.pr[i1] = new Array();
    23			this.wk[i1] = new Array();
    24			for (var i2 = 0; i2 < 3; i2++) {
    25				this.pr[i1][i2] = new Array(0, 0, 0);
    26				this.wk[i1][i2] = new Array(0, 0, 0);
    27			}
    28		}
    29						// ゲームボード
    30		document.write('<DIV ID="bord" STYLE="width: 505px; height: 535px; margin-left: auto; margin-right: auto;">\n');
    31		for (var i1 = 0; i1 < 9; i1++) {
    32			document.write('<DIV STYLE="float: left; border-width: 3px; border-style: solid; border-color: #00ff00;">\n');
    33			this.bord(i1);
    34			if (i1%3 == 2)
    35				document.write('</DIV><BR>\n');
    36			else
    37				document.write('</DIV>\n');
    38		}
    39		document.write('</DIV>\n');
    40						// 「数字」ボタン
    41		document.write('<DIV ID="num" STYLE="width: 550px; height: 30px; margin-left: auto; margin-right: auto;">\n');
    42		for (var i1 = 0; i1 < 9; i1++) {
    43			var id1 = "num_b" + i1;
    44			document.write('<BUTTON ID="' + id1 + '" onClick="gp.onSafe(' + i1 + ')" STYLE="width: 49px; height: 25px; font-size: 20px; text-align: center;">' + (i1+1) + '</BUTTON>\n');
    45		}
    46		document.write('</DIV>\n');
    47						// カウンタ
    48		document.write('<DIV ID="count" STYLE="width: 550px; height: 30px; margin-left: auto; margin-right: auto;">\n');
    49		for (var i1 = 0; i1 < 9; i1++) {
    50			var id1 = "ct" + i1;
    51			document.write('<INPUT ID="' + id1 + '" TYPE="text" STYLE="width: 45px; height: 25px; font-size: 20px; text-align: center;">\n');
    52		}
    53		document.write('</DIV>\n');
    54	
    55		return this;
    56	}
    57				//
    58				// MainPanel オブジェクト(メソッド)
    59				//
    60						// ボード(ブロック)
    61	MainPanel.prototype.bord = function(k)
    62	{
    63		for (var i1 = 0; i1 < 3; i1++) {
    64			var id1 = "bd" + k + i1;
    65			for (var i2 = 0; i2 < 3; i2++) {
    66				var id2 = id1 + i2;
    67				document.write('<INPUT ID="' + id2 + '" TYPE="text" STYLE="width: 50px; height: 50px; font-size: 50px; text-align: center;"></INPUT>');
    68			}
    69			document.write('<BR>');
    70		}
    71	}
    72						// 終了
    73	MainPanel.prototype.finish = function()
    74	{
    75		document.getElementById('method').style.display = "none";
    76		document.getElementById('start').style.display = "none";
    77		document.getElementById('finish').style.display = "none";
    78		document.getElementById('bord').style.display = "none";
    79		document.getElementById('num').style.display = "none";
    80		document.getElementById('count').style.display = "none";
    81		document.getElementById('problem').style.display = "none";
    82	}
    			

      GamePanel における修正箇所は以下に示すとおりです.

    001	gp = null;   // GamePanel オブジェクト
    002	
    003				//
    004				// GamePanel の開始
    005				//
    006	function gp_start()
    007	{
    008						// GamePanel オブジェクト
    009		gp = new GamePanel();
    010	}
    011				//
    012				// GamePanel オブジェクト(プロパティ)
    013				//
    014	function GamePanel()
    015	{
    016						// ボードの制御
    017		for (var i1 = 0; i1 < 9; i1++) {
    018			for (var i2 = 0; i2 < 3; i2++) {
    019				for (var i3 = 0; i3 < 3; i3++) {
    020					var id = "bd" + i1 + i2 + i3;
    021					if (mp.wk[i1][i2][i3] == 1) {
    022						eval("document.getElementById('" + id + "').style.backgroundColor = '#c8ffc8'");
    023						eval("document.getElementById('" + id + "').readOnly = 'true'");
    024					}
    025					else {
    026						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    027						eval("document.getElementById('" + id + "').readOnly = ''");
    028						eval("document.getElementById('" + id + "').addEventListener('input', this.onInput)");
    029					}
    030				}
    031			}
    032		}
    033						// ボタンの表示制御
    034		document.getElementById('start').removeEventListener("click", sp.onPlay);
    035		document.getElementById('num').style.display = "";
    036		document.getElementById('count').style.display = "";
    037		document.getElementById('method').style.display = "none";
    038		document.getElementById('start').style.display = "";
    039		document.getElementById('finish').style.display = "";
    040		document.getElementById('problem').style.display = "none";
    041		document.getElementById('start').innerHTML = "次の問題";
    042		document.getElementById('start').addEventListener("click", this.onNext);
    043		for (var i1 = 0; i1 < 9; i1++) {
    044			var id = "num_b" + i1;
    045			eval("document.getElementById('" + id + "').style.color = '#000000'");
    046		}
    047						// 数字の数を数える
    048		this.count();
    049	
    050		return this;
    051	}
    052				//
    053				// GamePanel オブジェクト(メソッド)
    054				//
    055						// 「次の問題」ボタンがクリックされたときの処理
    056	GamePanel.prototype.onNext = function()
    057	{
    058		for (var i1 = 0; i1 < 9; i1++) {
    059			for (var i2 = 0; i2 < 3; i2++) {
    060				for (var i3 = 0; i3 < 3; i3++) {
    061					var id = "bd" + i1 + i2 + i3;
    062					if (mp.wk[i1][i2][i3] != 1)
    063						eval("document.getElementById('" + id + "').removeEventListener('input', gp.onInput)");
    064					else
    065						eval("document.getElementById('" + id + "').readOnly = ''");
    066				}
    067			}
    068		}
    069		document.getElementById('start').removeEventListener("click", gp.onNext);
    070		sp_start();
    071	}
    072						// ゲーム版のテキストフィールドに入力された時の処理
    073	GamePanel.prototype.onInput = function()
    074	{
    075		for (var i1 = 0; i1 < 9; i1++) {
    076			for (var i2 = 0; i2 < 3; i2++) {
    077				for (var i3 = 0; i3 < 3; i3++) {
    078					var id = "bd" + i1 + i2 + i3;
    079					var str = eval("document.getElementById('" + id + "').value");
    080					if (str.length <= 0)
    081						k = 0;
    082					else {
    083						if (isNaN(str))
    084							var k = -1;
    085						else {
    086							var k = parseInt(str);
    087							if (k == 0)
    088								k = -1;
    089						}
    090					}
    091					mp.pr[i1][i2][i3] = k;
    092				}
    093			}
    094		}
    095		var sw = gp.check();
    096		if (sw > 0)
    097			gp.count();
    098	}
    099						// データは適切か?
    100	GamePanel.prototype.check = function()
    101	{
    102		var sw = 1;
    103								// 背景色のクリア
    104		for (var i1 = 0; i1 < 9; i1++) {
    105			for (var i2 = 0; i2 < 3; i2++) {
    106				for (var i3 = 0; i3 < 3; i3++) {
    107					if (mp.wk[i1][i2][i3] == 0) {
    108						var id = "bd" + i1 + i2 + i3;
    109						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    110					}
    111				}
    112			}
    113		}
    114								// 数字の適切性
    115		for (var i1 = 0; i1 < 9; i1++) {
    116			for (var i2 = 0; i2 < 3; i2++) {
    117				for (var i3 = 0; i3 < 3; i3++) {
    118					var id = "bd" + i1 + i2 + i3;
    119					eval("document.getElementById('" + id + "').style.color = '#000000'");
    120					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    121						sw = 0;
    122						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    123					}
    124				}
    125			}
    126		}
    127								// ブロック内
    128		if (sw > 0) {
    129			for (var i1 = 0; i1 < 9; i1++) {
    130				for (var i2 = 0; i2 < 8; i2++) {
    131					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    132					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    133					if (k1 > 0) {
    134						for (var i3 = i2+1; i3 < 9; i3++) {
    135							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    136							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    137							if (k1 == k2) {
    138								sw = 0;
    139								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    140								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    141							}
    142						}
    143					}
    144				}
    145			}
    146		}
    147								// 行内
    148		if (sw > 0) {
    149			for (var i1 = 0; i1 < 9; i1++) {
    150				for (var i2 = 0; i2 < 8; i2++) {
    151					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    152					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    153					if (k1 > 0) {
    154						for (var i3 = i2+1; i3 < 9; i3++) {
    155							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    156							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    157							if (k1 == k2) {
    158								sw = 0;
    159								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    160								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    161							}
    162						}
    163					}
    164				}
    165			}
    166		}
    167								// 列内
    168		if (sw > 0) {
    169			for (var i1 = 0; i1 < 9; i1++) {
    170				for (var i2 = 0; i2 < 8; i2++) {
    171					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    172					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    173					if (k1 > 0) {
    174						for (var i3 = i2+1; i3 < 9; i3++) {
    175							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    176							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    177							if (k1 == k2) {
    178								sw = 0;
    179								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    180								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    181							}
    182						}
    183					}
    184				}
    185			}
    186		}
    187	
    188		return sw;
    189	}
    190						// 数字の数を数える
    191	GamePanel.prototype.count = function()
    192	{
    193		for (var i1 = 0; i1 < 9; i1++) {
    194			var k1 = i1 + 1;
    195			var n = 0;
    196			for (var i2 = 0; i2 < 9; i2++) {
    197				for (var i3 = 0; i3 < 3; i3++) {
    198					for (var i4 = 0; i4 < 3; i4++) {
    199						if (mp.pr[i2][i3][i4] == k1)
    200							n++;
    201					}
    202				}
    203			}
    204			var id = "ct" + i1;
    205			eval("document.getElementById('" + id + "').value = " + n);
    206			if (n == 9)
    207				eval("document.getElementById('" + id + "').style.backgroundColor = '#ff0000'");
    208			else
    209				eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    210		}
    211	}
    212					// 「数字」ボタンがクリックされたときの処理(数字がおける場所)
    213	GamePanel.prototype.onSafe = function(k)
    214	{
    215		k++;
    216							// ブロック内
    217		for (var i1 = 0; i1 < 9; i1++) {
    218			var sw = 0;
    219			for (var i2 = 0; i2 < 3; i2++) {
    220				for (var i3 = 0; i3 < 3; i3++) {
    221					if (mp.wk[i1][i2][i3] == 2)
    222						mp.wk[i1][i2][i3] = 0;
    223					else if (mp.wk[i1][i2][i3] == 0) {
    224						var id = "bd" + i1 + i2 + i3;
    225						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    226					}
    227					if (mp.pr[i1][i2][i3] == k)
    228						sw = 1;
    229				}
    230			}
    231			for (i2 = 0; i2 < 3; i2++) {
    232				for (i3 = 0; i3 < 3; i3++) {
    233					if (mp.wk[i1][i2][i3] == 0 && (mp.pr[i1][i2][i3] > 0 || sw > 0))
    234						mp.wk[i1][i2][i3] = 2;
    235				}
    236			}
    237		}
    238							// 行内
    239		for (i1 = 0; i1 < 9; i1++) {
    240			for (i2 = 0; i2 < 9; i2++) {
    241				var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    242				if (k1 == k) {
    243					for (i3 = 0; i3 < 9; i3++) {
    244						if (mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] == 0)
    245							mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] = 2;
    246					}
    247					break;
    248				}
    249			}
    250		}
    251							// 列内
    252		for (i1 = 0; i1 < 9; i1++) {
    253			for (i2 = 0; i2 < 9; i2++) {
    254				k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    255				if (k1 == k) {
    256					for (i3 = 0; i3 < 9; i3++) {
    257						if (mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] == 0)
    258							mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] = 2;
    259					}
    260					break;
    261				}
    262			}
    263		}
    264							// 背景色の変更
    265		for (i1 = 0; i1 < 9; i1++) {
    266			var id = "num_b" + i1;
    267			eval("document.getElementById('" + id + "').style.color = '#000000'");
    268		}
    269		var id = "num_b" + (k - 1);
    270		eval("document.getElementById('" + id + "').style.color = '#00ff00'");
    271	
    272		for (i1 = 0; i1 < 9; i1++) {
    273			for (i2 = 0; i2 < 3; i2++) {
    274				for (i3 = 0; i3 < 3; i3++) {
    275					if (mp.wk[i1][i2][i3] == 0) {
    276						var id = "bd" + i1 + i2 + i3;
    277						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffd700'");
    278					}
    279				}
    280			}
    281		}
    282	}
    			
    043 行目~ 046 行目( GamePanel 関数)

      「数字」ボタンの文字色を黒に初期設定しています.

    104 行目~ 113 行目( check メソッド)

      「数字」ボタンによって変化したテキストフィールドの背景色を白に戻しています.

    213 行目~ 282 行目( onSafe メソッド)

      「数字」ボタンをクリックした時の処理です.指定された数字を設定できる場所の背景色をピンク( RGB : 0xFFD700 )に変更すると共に,クリックされた「数字」ボタンの文字色を緑に設定します.まず,217 行目~ 237 行目においては,各ブロックに同じ数字がないかを調べ,同じ数字があった場合は,そのブロックの数字が設定されていないテキストフィールドに対応する配列変数 wk の値を 2 に設定し,無かった場合は 0 に設定します.以下,239 行目~ 250 行目においては同じ行内,また,252 行目~ 263 行目においては同じ列内に同じ数字があった場合は,その行または列内の数字が設定されていないテキストフィールドに対応する配列変数 wk の値を 2 に設定します.

      その後,クリックされた「数字」ボタンの文字色を緑に設定し( 265 行目~ 270 行目),配列変数 wk の値が 0 である場所に対応するテキストフィールドの背景色をピンクに設定します( 272 行目~ 281 行目).

  5. ステップ5: 状態の記憶と復元

      ゲーム実行中,任意の状態を記憶し,再びその状態に戻ることが出来る機能を追加します.修正するプログラムは,HTML ファイル,MainPanel,StartPanel,及び,GamePanel です.まず,HTML ファイルでは,状態を記憶するためのボタン「記憶」と,記憶された状態に戻るためのボタン「戻る」を追加します( 34,35 行目).

    01	<!DOCTYPE HTML>
    02	<HTML>
    03	<HEAD>
    04		<TITLE>ナンプレ:ステップ5</TITLE>
    05		<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
    06		<LINK REL="stylesheet" TYPE="text/css" HREF="../../../master.css">
    07		<SCRIPT TYPE="text/javascript" SRC="main/MainPanel.js"></SCRIPT>
    08		<SCRIPT TYPE="text/javascript" SRC="start/StartPanel.js"></SCRIPT>
    09		<SCRIPT TYPE="text/javascript" SRC="game/GamePanel.js"></SCRIPT>
    10		<SCRIPT TYPE="text/javascript" SRC="problem/Problem.js"></SCRIPT>
    11	</HEAD>
    12	<BODY CLASS="eeffee">
    13		<DIV ID="problem_set_sheet" STYLE="display: none">
    14			<BUTTON CLASS="std" onClick="problem_set(1)">解</BUTTON> 
    15			空白<INPUT ID="sp" TYPE="text" SIZE="2" STYLE="font-size: 90%">
    16			<BUTTON CLASS="std" onClick="problem_set(2)">Go</BUTTON> 
    17			<BUTTON CLASS="std" onClick="problem_set(3)">設定</BUTTON>
    18			<P CLASS="center"><SCRIPT TYPE="text/javascript">
    19				for (var i1 = 0; i1 < 9; i1++) {
    20					for (var i2 = 0; i2 < 9; i2++) {
    21						var str = '<INPUT ID="tx' + i1 + i2 + '" TYPE="text" SIZE="2" STYLE="width: 30px; height: 30px; font-size: 30px; text-align: center;">'
    22						document.write(str + "\n");
    23					}
    24					document.write("<BR>\n");
    25				}
    26			</SCRIPT></P>
    27		</DIV>
    28	
    29		<H1>ナンプレ:ステップ5(記憶)</H1>
    30		<SCRIPT TYPE="text/javascript">
    31			mp_start();
    32		</SCRIPT>
    33		<A HREF="method.htm" TARGET="method"><BUTTON ID="method" CLASS="std">遊び方</BUTTON></A>
    34		<BUTTON ID="memory" CLASS="std" onClick="gp.memory()">記憶</BUTTON>
    35		<BUTTON ID="back" CLASS="std" onClick="gp.back()">戻る</BUTTON>
    36		<BUTTON ID="start" CLASS="std">実行</BUTTON>
    37		<BUTTON ID="finish" CLASS="std" onClick="mp.finish()">ゲーム終了</BUTTON>
    38		<BUTTON ID="problem" CLASS="std" onClick="problem_set(0)">問題</BUTTON>
    39		<SCRIPT TYPE="text/javascript">
    40			sp_start();
    41		</SCRIPT>
    42	</BODY>
    43	</HTML>
    			

      MainPanel では,「記憶」ボタンと「戻る」ボタンの表示設定を追加しています( 76,77 行目).

    01	mp = null;   // MainPanel オブジェクト
    02	
    03				//
    04				// MainPanel の開始
    05				//
    06	function mp_start()
    07	{
    08						// MainPanel オブジェクト
    09		mp = new MainPanel();
    10	}
    11				//
    12				// MainPanel オブジェクト(プロパティ)
    13				//
    14	function MainPanel()
    15	{
    16						// 問題領域と作業域の確保 : pr[i][j][k], wk[i][j][k]
    17						//    [i] : ブロック番号.左から右,上から下へ番号付け(i=0~9)
    18						//    [j][k] : 各ブロックのj行k列(j,k=0~2)
    19		this.pr = new Array();
    20		this.wk = new Array();
    21		for (var i1 = 0; i1 < 9; i1++) {
    22			this.pr[i1] = new Array();
    23			this.wk[i1] = new Array();
    24			for (var i2 = 0; i2 < 3; i2++) {
    25				this.pr[i1][i2] = new Array(0, 0, 0);
    26				this.wk[i1][i2] = new Array(0, 0, 0);
    27			}
    28		}
    29						// ゲームボード
    30		document.write('<DIV ID="bord" STYLE="width: 505px; height: 535px; margin-left: auto; margin-right: auto;">\n');
    31		for (var i1 = 0; i1 < 9; i1++) {
    32			document.write('<DIV STYLE="float: left; border-width: 3px; border-style: solid; border-color: #00ff00;">\n');
    33			this.bord(i1);
    34			if (i1%3 == 2)
    35				document.write('</DIV><BR>\n');
    36			else
    37				document.write('</DIV>\n');
    38		}
    39		document.write('</DIV>\n');
    40						// 「数字」ボタン
    41		document.write('<DIV ID="num" STYLE="width: 550px; height: 30px; margin-left: auto; margin-right: auto;">\n');
    42		for (var i1 = 0; i1 < 9; i1++) {
    43			var id1 = "num_b" + i1;
    44			document.write('<BUTTON ID="' + id1 + '" onClick="gp.onSafe(' + i1 + ')" STYLE="width: 49px; height: 25px; font-size: 20px; text-align: center;">' + (i1+1) + '</BUTTON>\n');
    45		}
    46		document.write('</DIV>\n');
    47						// カウンタ
    48		document.write('<DIV ID="count" STYLE="width: 550px; height: 30px; margin-left: auto; margin-right: auto;">\n');
    49		for (var i1 = 0; i1 < 9; i1++) {
    50			var id1 = "ct" + i1;
    51			document.write('<INPUT ID="' + id1 + '" TYPE="text" STYLE="width: 45px; height: 25px; font-size: 20px; text-align: center;">\n');
    52		}
    53		document.write('</DIV>\n');
    54	
    55		return this;
    56	}
    57				//
    58				// MainPanel オブジェクト(メソッド)
    59				//
    60						// ボード(ブロック)
    61	MainPanel.prototype.bord = function(k)
    62	{
    63		for (var i1 = 0; i1 < 3; i1++) {
    64			var id1 = "bd" + k + i1;
    65			for (var i2 = 0; i2 < 3; i2++) {
    66				var id2 = id1 + i2;
    67				document.write('<INPUT ID="' + id2 + '" TYPE="text" STYLE="width: 50px; height: 50px; font-size: 50px; text-align: center;"></INPUT>');
    68			}
    69			document.write('<BR>');
    70		}
    71	}
    72						// 終了
    73	MainPanel.prototype.finish = function()
    74	{
    75		document.getElementById('method').style.display = "none";
    76		document.getElementById('memory').style.display = "none";
    77		document.getElementById('back').style.display = "none";
    78		document.getElementById('start').style.display = "none";
    79		document.getElementById('finish').style.display = "none";
    80		document.getElementById('bord').style.display = "none";
    81		document.getElementById('num').style.display = "none";
    82		document.getElementById('count').style.display = "none";
    83		document.getElementById('problem').style.display = "none";
    84	}
    			

      StartPanel でも,「記憶」ボタンと「戻る」ボタンの表示設定を追加しています( 29,30 行目).

    001	sp = null;   // StartPanel オブジェクト
    002				//
    003				// StartPanel の開始
    004				//
    005	function sp_start()
    006	{
    007						// StartPanel オブジェクト
    008		sp = new StartPanel();
    009	}
    010				//
    011				// StartPanel オブジェクト(プロパティ)
    012				//
    013	function StartPanel()
    014	{
    015						// 初期設定
    016		for (var i1 = 0; i1 < 9; i1++) {
    017			for (var i2 = 0; i2 < 3; i2++) {
    018				for (var i3 = 0; i3 < 3; i3++) {
    019					mp.pr[i1][i2][i3] = 0;
    020					mp.wk[i1][i2][i3] = 0;
    021					id = "bd" + i1 + i2 + i3;
    022					eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    023					eval("document.getElementById('" + id + "').value = ''");
    024				}
    025			}
    026		}
    027						// ボタンの表示制御
    028		document.getElementById('method').style.display = "";
    029		document.getElementById('memory').style.display = "none";
    030		document.getElementById('back').style.display = "none";
    031		document.getElementById('start').style.display = "";
    032		document.getElementById('start').addEventListener("click", this.onPlay);
    033		document.getElementById('finish').style.display = "none";
    034		document.getElementById('num').style.display = "none";
    035		document.getElementById('count').style.display = "none";
    036		document.getElementById('problem').style.display = "";
    037		document.getElementById('start').innerHTML = "実行";
    038	}
    039				//
    040				// StartPanel オブジェクト(メソッド)
    041				//
    042						// 「実行」ボタンがクリックされたときの処理
    043	StartPanel.prototype.onPlay = function()
    044	{
    045		for (var i1 = 0; i1 < 9; i1++) {
    046			for (var i2 = 0; i2 < 3; i2++) {
    047				for (var i3 = 0; i3 < 3; i3++) {
    048					var id = "bd" + i1 + i2 + i3;
    049					var str = eval("document.getElementById('" + id + "').value");
    050					if (str.length > 0) {
    051						if (isNaN(str))
    052							var k = -1;
    053						else {
    054							var k = parseInt(str);
    055							if (k == 0)
    056								k = -1;
    057						}
    058						mp.pr[i1][i2][i3] = k;
    059						mp.wk[i1][i2][i3] = 1;
    060					}
    061					else {
    062						mp.pr[i1][i2][i3] = 0;
    063						mp.wk[i1][i2][i3] = 0;
    064					}
    065				}
    066			}
    067		}
    068		var sw = sp.check();
    069		if (sw > 0)
    070			gp_start();
    071	}
    072						// データは適切か?
    073	StartPanel.prototype.check = function()
    074	{
    075		var sw = 1;
    076								// 数字の適切性
    077		for (var i1 = 0; i1 < 9; i1++) {
    078			for (var i2 = 0; i2 < 3; i2++) {
    079				for (var i3 = 0; i3 < 3; i3++) {
    080					var id = "bd" + i1 + i2 + i3;
    081					eval("document.getElementById('" + id + "').style.color = '#000000'");
    082					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    083						sw = 0;
    084						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    085					}
    086				}
    087			}
    088		}
    089								// ブロック内
    090		if (sw > 0) {
    091			for (var i1 = 0; i1 < 9; i1++) {
    092				for (var i2 = 0; i2 < 8; i2++) {
    093					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    094					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    095					if (k1 > 0) {
    096						for (var i3 = i2+1; i3 < 9; i3++) {
    097							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    098							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    099							if (k1 == k2) {
    100								sw = 0;
    101								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    102								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    103							}
    104						}
    105					}
    106				}
    107			}
    108		}
    109								// 行内
    110		if (sw > 0) {
    111			for (var i1 = 0; i1 < 9; i1++) {
    112				for (var i2 = 0; i2 < 8; i2++) {
    113					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    114					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    115					if (k1 > 0) {
    116						for (var i3 = i2+1; i3 < 9; i3++) {
    117							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    118							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    119							if (k1 == k2) {
    120								sw = 0;
    121								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    122								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    123							}
    124						}
    125					}
    126				}
    127			}
    128		}
    129								// 列内
    130		if (sw > 0) {
    131			for (var i1 = 0; i1 < 9; i1++) {
    132				for (var i2 = 0; i2 < 8; i2++) {
    133					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    134					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    135					if (k1 > 0) {
    136						for (var i3 = i2+1; i3 < 9; i3++) {
    137							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    138							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    139							if (k1 == k2) {
    140								sw = 0;
    141								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    142								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    143							}
    144						}
    145					}
    146				}
    147			}
    148		}
    149	
    150		return sw;
    151	}
    			

      GamePanel における修正箇所は以下に示すとおりです.

    001	gp = null;   // GamePanel オブジェクト
    002	
    003				//
    004				// GamePanel の開始
    005				//
    006	function gp_start()
    007	{
    008						// GamePanel オブジェクト
    009		gp = new GamePanel();
    010	}
    011				//
    012				// GamePanel オブジェクト(プロパティ)
    013				//
    014	function GamePanel()
    015	{
    016						// 記憶領域の初期設定
    017		this.ct = 0;   // 記憶している状態の数
    018		this.kp = new Array();   // 状態の記憶場所
    019						// ボードの制御
    020		for (var i1 = 0; i1 < 9; i1++) {
    021			for (var i2 = 0; i2 < 3; i2++) {
    022				for (var i3 = 0; i3 < 3; i3++) {
    023					var id = "bd" + i1 + i2 + i3;
    024					if (mp.wk[i1][i2][i3] == 1) {
    025						eval("document.getElementById('" + id + "').style.backgroundColor = '#c8ffc8'");
    026						eval("document.getElementById('" + id + "').readOnly = 'true'");
    027					}
    028					else {
    029						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    030						eval("document.getElementById('" + id + "').readOnly = ''");
    031						eval("document.getElementById('" + id + "').addEventListener('input', this.onInput)");
    032					}
    033				}
    034			}
    035		}
    036						// ボタンの表示制御
    037		document.getElementById('start').removeEventListener("click", sp.onPlay);
    038		document.getElementById('num').style.display = "";
    039		document.getElementById('count').style.display = "";
    040		document.getElementById('method').style.display = "none";
    041		document.getElementById('memory').style.display = "";
    042		document.getElementById('back').style.display = "none";
    043		document.getElementById('start').style.display = "";
    044		document.getElementById('finish').style.display = "";
    045		document.getElementById('problem').style.display = "none";
    046		document.getElementById('start').innerHTML = "次の問題";
    047		document.getElementById('start').addEventListener("click", this.onNext);
    048		for (var i1 = 0; i1 < 9; i1++) {
    049			var id = "num_b" + i1;
    050			eval("document.getElementById('" + id + "').style.color = '#000000'");
    051		}
    052						// 数字の数を数える
    053		this.count();
    054	
    055		return this;
    056	}
    057				//
    058				// GamePanel オブジェクト(メソッド)
    059				//
    060						// 「次の問題」ボタンがクリックされたときの処理
    061	GamePanel.prototype.onNext = function()
    062	{
    063		for (var i1 = 0; i1 < 9; i1++) {
    064			for (var i2 = 0; i2 < 3; i2++) {
    065				for (var i3 = 0; i3 < 3; i3++) {
    066					var id = "bd" + i1 + i2 + i3;
    067					if (mp.wk[i1][i2][i3] != 1)
    068						eval("document.getElementById('" + id + "').removeEventListener('input', gp.onInput)");
    069					else
    070						eval("document.getElementById('" + id + "').readOnly = ''");
    071				}
    072			}
    073		}
    074		document.getElementById('start').removeEventListener("click", gp.onNext);
    075		sp_start();
    076	}
    077						// ゲーム版のテキストフィールドに入力された時の処理
    078	GamePanel.prototype.onInput = function()
    079	{
    080		for (var i1 = 0; i1 < 9; i1++) {
    081			for (var i2 = 0; i2 < 3; i2++) {
    082				for (var i3 = 0; i3 < 3; i3++) {
    083					var id = "bd" + i1 + i2 + i3;
    084					var str = eval("document.getElementById('" + id + "').value");
    085					if (str.length <= 0)
    086						k = 0;
    087					else {
    088						if (isNaN(str))
    089							var k = -1;
    090						else {
    091							var k = parseInt(str);
    092							if (k == 0)
    093								k = -1;
    094						}
    095					}
    096					mp.pr[i1][i2][i3] = k;
    097				}
    098			}
    099		}
    100		var sw = gp.check();
    101		if (sw > 0)
    102			gp.count();
    103	}
    104						// データは適切か?
    105	GamePanel.prototype.check = function()
    106	{
    107		var sw = 1;
    108								// 背景色のクリア
    109		for (var i1 = 0; i1 < 9; i1++) {
    110			for (var i2 = 0; i2 < 3; i2++) {
    111				for (var i3 = 0; i3 < 3; i3++) {
    112					if (mp.wk[i1][i2][i3] == 0) {
    113						var id = "bd" + i1 + i2 + i3;
    114						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    115					}
    116				}
    117			}
    118		}
    119								// 数字の適切性
    120		for (var i1 = 0; i1 < 9; i1++) {
    121			for (var i2 = 0; i2 < 3; i2++) {
    122				for (var i3 = 0; i3 < 3; i3++) {
    123					var id = "bd" + i1 + i2 + i3;
    124					eval("document.getElementById('" + id + "').style.color = '#000000'");
    125					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    126						sw = 0;
    127						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    128					}
    129				}
    130			}
    131		}
    132								// ブロック内
    133		if (sw > 0) {
    134			for (var i1 = 0; i1 < 9; i1++) {
    135				for (var i2 = 0; i2 < 8; i2++) {
    136					var k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    137					var id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    138					if (k1 > 0) {
    139						for (var i3 = i2+1; i3 < 9; i3++) {
    140							var k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    141							var id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    142							if (k1 == k2) {
    143								sw = 0;
    144								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    145								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    146							}
    147						}
    148					}
    149				}
    150			}
    151		}
    152								// 行内
    153		if (sw > 0) {
    154			for (var i1 = 0; i1 < 9; i1++) {
    155				for (var i2 = 0; i2 < 8; i2++) {
    156					var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    157					var id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    158					if (k1 > 0) {
    159						for (var i3 = i2+1; i3 < 9; i3++) {
    160							var k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    161							var id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    162							if (k1 == k2) {
    163								sw = 0;
    164								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    165								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    166							}
    167						}
    168					}
    169				}
    170			}
    171		}
    172								// 列内
    173		if (sw > 0) {
    174			for (var i1 = 0; i1 < 9; i1++) {
    175				for (var i2 = 0; i2 < 8; i2++) {
    176					var k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    177					var id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    178					if (k1 > 0) {
    179						for (var i3 = i2+1; i3 < 9; i3++) {
    180							var k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    181							var id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    182							if (k1 == k2) {
    183								sw = 0;
    184								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    185								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    186							}
    187						}
    188					}
    189				}
    190			}
    191		}
    192	
    193		return sw;
    194	}
    195						// 数字の数を数える
    196	GamePanel.prototype.count = function()
    197	{
    198		for (var i1 = 0; i1 < 9; i1++) {
    199			var k1 = i1 + 1;
    200			var n = 0;
    201			for (var i2 = 0; i2 < 9; i2++) {
    202				for (var i3 = 0; i3 < 3; i3++) {
    203					for (var i4 = 0; i4 < 3; i4++) {
    204						if (mp.pr[i2][i3][i4] == k1)
    205							n++;
    206					}
    207				}
    208			}
    209			var id = "ct" + i1;
    210			eval("document.getElementById('" + id + "').value = " + n);
    211			if (n == 9)
    212				eval("document.getElementById('" + id + "').style.backgroundColor = '#ff0000'");
    213			else
    214				eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    215		}
    216	}
    217					// 「数字」ボタンがクリックされたときの処理(数字がおける場所)
    218	GamePanel.prototype.onSafe = function(k)
    219	{
    220		k++;
    221							// ブロック内
    222		for (var i1 = 0; i1 < 9; i1++) {
    223			var sw = 0;
    224			for (var i2 = 0; i2 < 3; i2++) {
    225				for (var i3 = 0; i3 < 3; i3++) {
    226					if (mp.wk[i1][i2][i3] == 2)
    227						mp.wk[i1][i2][i3] = 0;
    228					else if (mp.wk[i1][i2][i3] == 0) {
    229						var id = "bd" + i1 + i2 + i3;
    230						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    231					}
    232					if (mp.pr[i1][i2][i3] == k)
    233						sw = 1;
    234				}
    235			}
    236			for (i2 = 0; i2 < 3; i2++) {
    237				for (i3 = 0; i3 < 3; i3++) {
    238					if (mp.wk[i1][i2][i3] == 0 && (mp.pr[i1][i2][i3] > 0 || sw > 0))
    239						mp.wk[i1][i2][i3] = 2;
    240				}
    241			}
    242		}
    243							// 行内
    244		for (i1 = 0; i1 < 9; i1++) {
    245			for (i2 = 0; i2 < 9; i2++) {
    246				var k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    247				if (k1 == k) {
    248					for (i3 = 0; i3 < 9; i3++) {
    249						if (mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] == 0)
    250							mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] = 2;
    251					}
    252					break;
    253				}
    254			}
    255		}
    256							// 列内
    257		for (i1 = 0; i1 < 9; i1++) {
    258			for (i2 = 0; i2 < 9; i2++) {
    259				k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    260				if (k1 == k) {
    261					for (i3 = 0; i3 < 9; i3++) {
    262						if (mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] == 0)
    263							mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] = 2;
    264					}
    265					break;
    266				}
    267			}
    268		}
    269							// 背景色の変更
    270		for (i1 = 0; i1 < 9; i1++) {
    271			var id = "num_b" + i1;
    272			eval("document.getElementById('" + id + "').style.color = '#000000'");
    273		}
    274		var id = "num_b" + (k - 1);
    275		eval("document.getElementById('" + id + "').style.color = '#00ff00'");
    276	
    277		for (i1 = 0; i1 < 9; i1++) {
    278			for (i2 = 0; i2 < 3; i2++) {
    279				for (i3 = 0; i3 < 3; i3++) {
    280					if (mp.wk[i1][i2][i3] == 0) {
    281						var id = "bd" + i1 + i2 + i3;
    282						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffd700'");
    283					}
    284				}
    285			}
    286		}
    287	}
    288						// 状態の保存
    289	GamePanel.prototype.memory = function()
    290	{
    291		document.getElementById('back').style.display = "";
    292		var state = new Array();
    293		for (var i1 = 0; i1 < 9; i1++) {
    294			state[i1] = new Array();
    295			for (var i2 = 0; i2 < 3; i2++) {
    296				state[i1][i2] = new Array();
    297				for (var i3 = 0; i3 < 3; i3++)
    298					state[i1][i2][i3] = mp.pr[i1][i2][i3];
    299			}
    300		}
    301		gp.kp[gp.ct] = state;
    302		gp.ct++;
    303		alert("状態を記憶しました\n 記憶状態数 = " + gp.ct);
    304	}
    305	
    306						// 一つ前の状態に戻す
    307	GamePanel.prototype.back = function()
    308	{
    309		gp.ct--;
    310		if (gp.ct == 0)
    311			document.getElementById('back').style.display = "none";
    312		mp.pr = gp.kp[gp.ct];
    313		for (var i1 = 0; i1 < 9; i1++) {
    314			for (var i2 = 0; i2 < 3; i2++) {
    315				for (var i3 = 0; i3 < 3; i3++) {
    316					var id = "bd" + i1 + i2 + i3;
    317					if (mp.pr[i1][i2][i3] != 0)
    318						eval("document.getElementById('" + id + "')").value = mp.pr[i1][i2][i3];
    319					else
    320						eval("document.getElementById('" + id + "')").value = "";
    321				}
    322			}
    323		}
    324		var sw = gp.check();
    325		if (sw > 0)
    326			gp.count();
    327	}
    			
    017,018 行目( GamePanel 関数)

      記憶している数を表す変数 ct と,記憶場所 kp (配列)を定義しています.

    041 行目~ 042 行目( GamePanel 関数)

      「記憶」ボタンを表示し,「戻る」ボタンを非表示にしています.

    289 行目~ 304 行目( memory メソッド)

      「記憶」ボタンがクリックされたときの処理です.「戻る」ボタンを表示( 291 行目),現在の状態を表す配列 mp.pr を配列 state にコピーして,その state を配列 gp.kp に記憶( 292 行目~ 301 行目),記憶している状態の数を増加( 302 行目)という処理を行っています.303 行目では,記憶した状態の数と共に,「状態を記憶しました」というメッセージを表示しています.301 行目の代わりに,gp.kp[gp.ct] = mp.pr のように,配列 pr を直接保存する訳にはいかない点に注意してください.なぜなら,pr は配列を指すポインタ(配列のアドレス)ですので,同じものを配列 kp に保存することになってしまいます.

    307 行目~ 327 行目( back メソッド)

      「戻る」ボタンをクリックした時の処理であり,最後に記憶された状態に戻します.記憶されている状態の数を減らし,その数が 0 の場合は,「戻る」ボタンを非表示にしています( 309 行目~ 311 行目).現在の状態を表す配列 mp.pr に最後に記憶された状態を設定し( 312 行目),mp.pr の値に基づき,画面の表示を再設定しています( 313 行目~ 323 行目).その後,ルール違反のチェックと数字のカウントを行っています.

静岡理工科大学 菅沼ホーム JavaScript 目次 索引