情報学部 菅沼ホーム 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 関数の実行( 34 行目)により,ゲームの実行に必要な要素をすべて配置し,StartPanel に相当する sp_start 関数,及び,GamePanel に相当する gp_start 関数では,各要素の属性や値を変更することによってゲームを実行していきます.従って,48 行目に示すように,何らかの操作によって StartPanel に移動するといった処理を行わず,要素の配置が終了すると即座に StartPanel の状態に移動します.

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

        このプログラムでは,問題を保存し,保存した問題を呼び出して実行することも可能です( 40 行目~ 46 行目).それらの処理は,localStorage で行っています.その内容に関する説明は,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		<META NAME=viewport CONTENT="width=device-width, initial-scale=1">
      07		<LINK REL="stylesheet" TYPE="text/css" HREF="../../../master.css">
      08		<SCRIPT TYPE="text/javascript" SRC="main/MainPanel.js"></SCRIPT>
      09		<SCRIPT TYPE="text/javascript" SRC="start/StartPanel.js"></SCRIPT>
      10		<SCRIPT TYPE="text/javascript" SRC="game/GamePanel.js"></SCRIPT>
      11		<SCRIPT TYPE="text/javascript" SRC="problem/Problem.js"></SCRIPT>
      12		<SCRIPT TYPE="text/javascript" SRC="storage/storage.js"></SCRIPT>
      13	</HEAD>
      14	<BODY CLASS="eeffee">
      15		<DIV ID="problem_set_sheet" STYLE="display: none">
      16			<BUTTON CLASS="std" onClick="problem_set(1)">解</BUTTON> 
      17			空白<INPUT ID="sp" TYPE="text" SIZE="2" STYLE="font-size: 90%">
      18			<BUTTON CLASS="std" onClick="problem_set(2)">Go</BUTTON> 
      19			<BUTTON CLASS="std" onClick="problem_set(3)">設定</BUTTON>
      20			<P CLASS="center"><SCRIPT TYPE="text/javascript">
      21				for (let i1 = 0; i1 < 9; i1++) {
      22					for (let i2 = 0; i2 < 9; i2++) {
      23						let str = '<INPUT ID="tx' + i1 + i2 + '" TYPE="text" SIZE="2" STYLE="width: 30px; height: 30px; font-size: 30px; text-align: center;">'
      24						document.write(str + "\n");
      25					}
      26					document.write("<BR>\n");
      27				}
      28			</SCRIPT></P>
      29			<INPUT ID="msg" TYPE="text" SIZE="5" STYLE="font-size: 90%">
      30		</DIV>
      31	
      32		<H1>ナンプレ:ステップ1(基本)</H1>
      33		<SCRIPT TYPE="text/javascript">
      34			mp_start();
      35		</SCRIPT>
      36		<A HREF="method.htm" TARGET="method"><BUTTON ID="method" CLASS="std">遊び方</BUTTON></A>
      37		<BUTTON ID="start" CLASS="std">実行</BUTTON>
      38		<BUTTON ID="finish" CLASS="std" onClick="mp.finish()">ゲーム終了</BUTTON>
      39		<BUTTON ID="problem" CLASS="std" onClick="problem_set(0)">問題</BUTTON><BR>
      40		<BUTTON ID="storage" CLASS="std" onClick="storage()">過去の問題</BUTTON>
      41		<DIV ID="str" STYLE="display: none"><INPUT TYPE="button" VALUE="件数" onClick="str_num()" STYLE="font-size:90%"> 
      42		<INPUT TYPE="button" VALUE="問題リスト出力" onClick="str_out()" STYLE="font-size:90%"> 
      43		<INPUT TYPE="button" VALUE="全削除" onClick="str_del_all()" STYLE="font-size:90%"><BR>
      44		問題名 : <INPUT TYPE="text" ID="s_name" SIZE="10" STYLE="font-size:120%"> <INPUT TYPE="button" VALUE="設定" onClick="str_set()" STYLE="font-size:90%"><BR>
      45		問題名 : <INPUT TYPE="text" ID="d_name" SIZE="10" STYLE="font-size:120%"> <INPUT TYPE="button" VALUE="削除" onClick="str_del()" STYLE="font-size:90%"><BR></DIV>
      46		<TEXTAREA TYPE="text" ID="str_tx" COLS="30" ROWS="5" STYLE="font-size: 120%; display: none"></TEXTAREA>
      47		<SCRIPT TYPE="text/javascript">
      48			sp_start();
      49		</SCRIPT>
      50	</BODY>
      51	</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 (let i1 = 0; i1 < 9; i1++) {
      22			this.pr[i1] = new Array();
      23			this.wk[i1] = new Array();
      24			for (let 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 (let 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 (let i1 = 0; i1 < 3; i1++) {
      50			let id1 = "bd" + k + i1;
      51			for (let i2 = 0; i2 < 3; i2++) {
      52				let 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 次元配列の例です.
      	let a = new Array(2);   // let a = new Array(); でも可
      	for (let i1 = 0; i1 < 2; i1++)
      		a[i1] = new Array(3);					
      初期設定も同時に行いたい場合は,例えば,以下のようにして行います.
      	let a = new Array(2);   // let 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 (let i1 = 0; i1 < 9; i1++) {
      17			for (let i2 = 0; i2 < 3; i2++) {
      18				for (let 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('storage').style.display = "";
      34		document.getElementById('start').innerHTML = "実行";
      35	}
      36				//
      37				// StartPanel オブジェクト(メソッド)
      38				//
      39						// 「実行」ボタンがクリックされたときの処理
      40	StartPanel.prototype.onPlay = function()
      41	{
      42		let sw  = 1;
      43		let prb = "";
      44		for (let i1 = 0; i1 < 9; i1++) {
      45			for (let i2 = 0; i2 < 3; i2++) {
      46				for (let i3 = 0; i3 < 3; i3++) {
      47					let id = "bd" + i1 + i2 + i3;
      48					let str = eval("document.getElementById('" + id + "').value");
      49					if (str.length > 0) {   // 文字列の長さ > 0 ?
      50						let k;
      51						if (isNaN(str))   // 非数字か ?
      52							k = -1;
      53						else {
      54							k = parseInt(str);   // 整数への変換
      55							if (k <= 0 || k >= 10)
      56								k = -1;
      57						}
      58						mp.pr[i1][i2][i3] = k;
      59						mp.wk[i1][i2][i3] = 1;
      60						if (k > 0) {
      61							if (prb.length > 0)
      62								prb += "," + id + "," + str;
      63							else
      64								prb = id + "," + str;
      65						}
      66						else {
      67							eval("document.getElementById('" + id + "').style.color = '#ff0000'");
      68							sw = 0;
      69						}
      70					}
      71					else {
      72						mp.pr[i1][i2][i3] = 0;
      73						mp.wk[i1][i2][i3] = 0;
      74					}
      75				}
      76			}
      77		}
      78	
      79		if (sw > 0) {
      80			document.getElementById('storage').style.display = "none";
      81			document.getElementById('str').style.display = "none";
      82			document.getElementById('str_tx').style.display = "none";
      83			let key = prompt("この問題を保存する場合は,問題名を入力してください", "");
      84			if (key != null && key.length > 0)
      85				str_keep(key, prb);
      86			gp_start();
      87		}
      88	}
      				
      08 行目( sp_start 関数)

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

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

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

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

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

      42 行目~ 77 行目( onPlay メソッド)

        onPlay は,「実行」ボタンがクリックされた時の処理を行うメソッドです( 40 行目~ 88 行目).各ブロックのテキストフィールドに入力された数字を配列変数 pr の対応する場所に設定し,かつ,その場所の wk の値を 1 (問題として設定された部分であり,ゲーム実行中にこの値を編集不可能にするため)に設定しています.ただし,全角の数字,数字以外の文字,1 ~ 9 以外の数字などが入力されていた場合は pr に -1 を設定しています.何も入力されていない場合は,pr 及び wk の値を 0 に設定しています.適切な数字が入力された場合は,この問題を記憶する際に使用するために,変数 prb に,その位置と数字をカンマで区切って記憶しています( 60 行目~ 65 行目).なお,length は,String オブジェクトのプロパティであり,isNaNparseInt は,トップレベルの関数です.

      79 行目~ 87 行目( onPlay メソッド)

        適切な数字が入力された場合の処理です.保存してある問題を利用するために使用するボタン等を非表示にし( 80 行目~ 82 行目),この問題を保存するか否かの問いかけを行った後,保存する場合は保存処理を行っています( 83 行目~ 85 行目).最後に,ゲームの実行画面に移動しています( 86 行目).

        基本的に,スタート画面において,キーボードから問題を入力し,「実行」ボタンをクリックすればナンプレを実行可能です.しかし,過去に行った問題をもう一度行いたいような場合もあるかと思います.「過去の問題」ボタンをクリックすると,その下に,ボタンやテキストエリア等が表示されます.「件数」ボタンをクリックすると保存してある問題数が,「問題リスト出力」ボタンをクリックすると保存してある全ての問題が,また,「全削除」ボタンをクリックすると保存してある全ての問題が削除されます.問題名を入力して「削除」ボタンをクリックすると,指定した問題だけが削除されます.問題名を入力して「設定」ボタンをクリックすると,指定した問題が,上に表示されている問題パネルに設定されます.これらの処理を行う JavaScript の関数 storage.js の内容は,以下に示すとおりです.

      /****************************/
      /* 問題の管理               */
      /*      coded by Y.Suganuma */
      /****************************/
      		// ボタン等の表示
      function storage() {
      	document.getElementById("str").style.display = "";
      	document.getElementById("str_tx").style.display = "";
      }
      		// データ件数
      function str_num() {
      	document.getElementById("str_tx").value = localStorage.length;
      }
      		// データの保存
      function str_keep(key, value) {
      	localStorage.setItem(key, value)
      }
      		// データの設定
      function str_set() {
      	let key   = document.getElementById("s_name").value;
      	let value = localStorage.getItem(key);
      	if (value != null) {
      		let str   = value.split(",");
      		for (let i1 = 0; i1 < str.length; i1 += 2)
      			document.getElementById(str[i1]).value = str[i1+1];
      	}
      	else
      		document.getElementById("str_tx").value = key + " が存在しません!";
      }
      		// データの削除
      function str_del() {
      	let key = document.getElementById("d_name").value;
      	if (key.length > 0) {
      		localStorage.removeItem(key);
      		document.getElementById("str_tx").value = key + " を削除しました";
      	}
      	else
      		document.getElementById("str_tx").value = "問題名を入力してください!";
      }
      		// 全てのデータの削除
      function str_del_all() {
      	localStorage.clear();
      	document.getElementById("str_tx").value = "全てのデータを削除しました";
      }
      		// 全てのデータの出力
      function str_out() {
      	let str = "";
      	for (let i1 = 0; i1 < localStorage.length; i1++) {
      		let key   = localStorage.key(i1);
      		let value = localStorage.getItem(key);
      		str      += key + "\n";
      	}
      	document.getElementById("str_tx").value = str;
      }
      				

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

      • 最初の 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		let pr = new Array();
      004		let p  = new Array();
      005		for (let 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			let ct1 = 0;   // バックトラック回数
      019			let ct2 = 0;   // バックトラック回数の合計
      020			let n1 = 1;   // 配置する数字
      021			let n2 = 1;   // その数字の何個目かを示す
      022			let ft = 1;   // 一番最初の数字( 1 )か否か
      023			let mp = new Array();
      024			for (let i1 = 0; i1 < 9; i1++) {
      025				for (let 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						let k = Math.floor(Math.random() * 81);
      035						if (k >= 81)
      036							k = 80;
      037						let k1     = Math.floor(k / 9);
      038						let k2     = k % 9;
      039						pr[k1][k2] = 1;
      040						let row1   = new Array();
      041						let col1   = new Array();
      042						for (let i1 = 0; i1 < 9; i1++) {
      043							for (let i2 = 0; i2 < 9; i2++) {
      044								if (i1 != k1 || i2 != k2) {
      045									row1.push(i1);
      046									col1.push(i2);
      047								}
      048							}
      049						}
      050						let 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						let row1 = new Array();
      058						let col1 = new Array();
      059						for (let i1 = 0; i1 < 9; i1++) {
      060							for (let 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							let k = Math.floor(Math.random() * row1.length);
      070							if (k >= row1.length)
      071								k = row1.length - 1;
      072							let k1     = row1[k];
      073							let k2     = col1[k];
      074							pr[k1][k2] = n1;
      075							row1.splice(k, 1);
      076							col1.splice(k, 1);
      077							let ps = new Pos(k1, k2, n1, n2, row1, col1);
      078							mp.push(ps);
      079							n2++;
      080						}
      081										// 設定する場所がない場合
      082						else {
      083							let sw = 0;
      084							while (sw == 0 && ct1 < 1000) {
      085								ct1++;
      086								let n = mp.length - 1;
      087								if (n < 0)
      088									break;
      089								else {
      090									let ps     = mp[n];
      091									let k1     = ps.r;
      092									let 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										let 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 (let i1 = 0; i1 < 9; i1++) {
      123									for (let 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 (let i1 = 0; i1 < 9; i1++) {
      137				for (let i2 = 0; i2 < 9; i2++) {
      138					let id = "tx" + i1 + i2;
      139					document.getElementById(id).value = pr[i1][i2];
      140				}
      141			}
      142		}
      143				// 空白の生成
      144		else if (ssw == 2) {
      145			for (let i1 = 0; i1 < 9; i1++) {
      146				for (let i2 = 0; i2 < 9; i2++)
      147					pr[i1][i2] = 1;
      148			}
      149			let n = parseInt(document.getElementById("sp").value);
      150			if (n > 0) {
      151				let k = 0;
      152				while (k < n) {
      153					let k1 = Math.floor(9 * Math.random());
      154					if (k1 > 8)
      155						k1 = 8;
      156					let k2 = Math.floor(9 * Math.random());
      157					if (k2 > 8)
      158						k2 = 8;
      159					if (pr[k1][k2] != 0) {
      160						let 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 (let i1 = 0; i1 < 9; i1++) {
      171				for (let i2 = 0; i2 < 3; i2++) {
      172					for (let i3 = 0; i3 < 3; i3++) {
      173						let k1 = Math.floor(i1 / 3) * 3 + i2;
      174						let k2 = (i1 % 3) * 3 + i3;
      175						let bd = "bd" + i1 + i2 + i3;
      176						let 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 (let i1 = 0; i1 < 9; i1++) {
      194			for (let 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 (let i1 = 0; i1 < 9; i1++) {
      203			let k1 = Math.floor(i1 / 3) * 3;
      204			let k2 = (i1 % 3) * 3;
      205			let sw = 0;
      206			for (let i2 = k1; i2 < k1+3 && sw == 0; i2++) {
      207				for (let 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 (let i2 = k1; i2 < k1+3; i2++) {
      214					for (let i3 = k2; i3 < k2+3; i3++)
      215						p[i2][i3] = 0;
      216				}
      217			}
      218		}
      219					// 行内
      220		for (let i1 = 0; i1 < 9; i1++) {
      221			let sw = 0;
      222			for (let i2 = 0; i2 < 9 && sw == 0; i2++) {
      223				if (pr[i1][i2] == n)
      224					sw = 1;
      225			}
      226			if (sw > 0) {
      227				for (let i2 = 0; i2 < 9; i2++)
      228					p[i1][i2] = 0;
      229			}
      230		}
      231					// 列内
      232		for (let i1 = 0; i1 < 9; i1++) {
      233			let sw = 0;
      234			for (let i2 = 0; i2 < 9 && sw == 0; i2++) {
      235				if (pr[i2][i1] == n)
      236					sw = 1;
      237			}
      238			if (sw > 0) {
      239				for (let 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 (let i1 = 0; i1 < 9; i1++) {
      18			for (let i2 = 0; i2 < 3; i2++) {
      19				for (let i3 = 0; i3 < 3; i3++) {
      20					let 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 (let i1 = 0; i1 < 9; i1++) {
      50			for (let i2 = 0; i2 < 3; i2++) {
      51				for (let i3 = 0; i3 < 3; i3++) {
      52					let 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 (let i1 = 0; i1 < 9; i1++) {
    017			for (let i2 = 0; i2 < 3; i2++) {
    018				for (let 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('storage').style.display = "";
    034		document.getElementById('start').innerHTML = "実行";
    035	}
    036				//
    037				// StartPanel オブジェクト(メソッド)
    038				//
    039						// 「実行」ボタンがクリックされたときの処理
    040	StartPanel.prototype.onPlay = function()
    041	{
    042	//	let sw  = 1;   削除
    043		let prb = "";
    044		for (let i1 = 0; i1 < 9; i1++) {
    045			for (let i2 = 0; i2 < 3; i2++) {
    046				for (let i3 = 0; i3 < 3; i3++) {
    047					let id = "bd" + i1 + i2 + i3;
    048					let str = eval("document.getElementById('" + id + "').value");
    049					if (str.length > 0) {
    050						let k;
    051						if (isNaN(str))   // 非数字か ?
    052							k = -1;
    053						else {
    054							k = parseInt(str);   // 整数への変換
    055							if (k <= 0 || k >= 10)
    056								k = -1;
    057						}
    058						mp.pr[i1][i2][i3] = k;
    059						mp.wk[i1][i2][i3] = 1;
    060						if (k > 0) {
    061							if (prb.length > 0)
    062								prb += "," + id + "," + str;
    063							else
    064								prb = id + "," + str;
    065						}
    066	//					else {   以下,削除
    067	//						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    068	//						sw = 0;
    069	//					}
    070					}
    071					else {
    072						mp.pr[i1][i2][i3] = 0;
    073						mp.wk[i1][i2][i3] = 0;
    074					}
    075				}
    076			}
    077		}
    078	
    079		let sw = sp.check();
    080		if (sw > 0) {
    081			document.getElementById('storage').style.display = "none";
    082			document.getElementById('str').style.display = "none";
    083			document.getElementById('str_tx').style.display = "none";
    084			let key = prompt("この問題を保存する場合は,問題名を入力してください", "");
    085			if (key != null && key.length > 0)
    086				str_keep(key, prb);
    087			gp_start();
    088		}
    089	}
    090						// データは適切か?
    091	StartPanel.prototype.check = function()
    092	{
    093		let sw = 1;
    094								// 数字の適切性
    095		for (let i1 = 0; i1 < 9; i1++) {
    096			for (let i2 = 0; i2 < 3; i2++) {
    097				for (let i3 = 0; i3 < 3; i3++) {
    098					let id = "bd" + i1 + i2 + i3;
    099					eval("document.getElementById('" + id + "').style.color = '#000000'");
    100					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    101						sw = 0;
    102						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    103					}
    104				}
    105			}
    106		}
    107								// ブロック内
    108		if (sw > 0) {
    109			for (let i1 = 0; i1 < 9; i1++) {
    110				for (let i2 = 0; i2 < 8; i2++) {
    111					let k1  = mp.pr[i1][Math.floor(i2/3)][i2%3];
    112					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    113					if (k1 > 0) {
    114						for (let i3 = i2+1; i3 < 9; i3++) {
    115							let k2  = mp.pr[i1][Math.floor(i3/3)][i3%3];
    116							let id2 = "bd" + i1 + Math.floor(i3 / 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 (let i1 = 0; i1 < 9; i1++) {
    130				for (let i2 = 0; i2 < 8; i2++) {
    131					let k1  = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    132					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    133					if (k1 > 0) {
    134						for (let i3 = i2+1; i3 < 9; i3++) {
    135							let k2  = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    136							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 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 (let i1 = 0; i1 < 9; i1++) {
    150				for (let i2 = 0; i2 < 8; i2++) {
    151					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    152					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    153					if (k1 > 0) {
    154						for (let i3 = i2+1; i3 < 9; i3++) {
    155							let k2  = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    156							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 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		return sw;
    169	}
    			
    079 行目~ 088 行目( onPlay メソッド)

      ルール違反を起こしていないか否かのチェックをメソッド check で行い,問題が無ければ GamePanel に移動します.なお,ステップ1における 042 行目,及び,066 行目~ 069 行目を削除しています.

    091 行目~ 169 行目( check メソッド)

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

      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 (let i1 = 0; i1 < 9; i1++) {
    018			for (let i2 = 0; i2 < 3; i2++) {
    019				for (let i3 = 0; i3 < 3; i3++) {
    020					let 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 (let i1 = 0; i1 < 9; i1++) {
    051			for (let i2 = 0; i2 < 3; i2++) {
    052				for (let i3 = 0; i3 < 3; i3++) {
    053					let 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 (let i1 = 0; i1 < 9; i1++) {
    068			for (let i2 = 0; i2 < 3; i2++) {
    069				for (let i3 = 0; i3 < 3; i3++) {
    070					let id = "bd" + i1 + i2 + i3;
    071					let str = eval("document.getElementById('" + id + "').value");
    072					let k;
    073					if (str.length <= 0)
    074						k = 0;
    075					else {
    076						if (isNaN(str))
    077							k = -1;
    078						else {
    079							k = parseInt(str);
    080							if (k == 0)
    081								k = -1;
    082						}
    083					}
    084					mp.pr[i1][i2][i3] = k;
    085				}
    086			}
    087		}
    088		let sw = gp.check();
    089	}
    090						// データは適切か?
    091	GamePanel.prototype.check = function()
    092	{
    093		let sw = 1;
    094								// 数字の適切性
    095		for (let i1 = 0; i1 < 9; i1++) {
    096			for (let i2 = 0; i2 < 3; i2++) {
    097				for (let i3 = 0; i3 < 3; i3++) {
    098					let id = "bd" + i1 + i2 + i3;
    099					eval("document.getElementById('" + id + "').style.color = '#000000'");
    100					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    101						sw = 0;
    102						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    103					}
    104				}
    105			}
    106		}
    107								// ブロック内
    108		if (sw > 0) {
    109			for (let i1 = 0; i1 < 9; i1++) {
    110				for (let i2 = 0; i2 < 8; i2++) {
    111					let k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    112					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    113					if (k1 > 0) {
    114						for (let i3 = i2+1; i3 < 9; i3++) {
    115							let k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    116							let id2 = "bd" + i1 + Math.floor(i3 / 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 (let i1 = 0; i1 < 9; i1++) {
    130				for (let i2 = 0; i2 < 8; i2++) {
    131					let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    132					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    133					if (k1 > 0) {
    134						for (let i3 = i2+1; i3 < 9; i3++) {
    135							let k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    136							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 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 (let i1 = 0; i1 < 9; i1++) {
    150				for (let i2 = 0; i2 < 8; i2++) {
    151					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    152					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    153					if (k1 > 0) {
    154						for (let i3 = i2+1; i3 < 9; i3++) {
    155							let k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    156							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 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		return sw;
    169	}
    			
    028 行目( GamePanel 関数)

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

    065 行目~ 089 行目( 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 (let i1 = 0; i1 < 9; i1++) {
    22			this.pr[i1] = new Array();
    23			this.wk[i1] = new Array();
    24			for (let 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 (let 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 (let i1 = 0; i1 < 9; i1++) {
    43			let 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 (let i1 = 0; i1 < 9; i1++) {
    50			let 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 (let i1 = 0; i1 < 3; i1++) {
    64			let id1 = "bd" + k + i1;
    65			for (let i2 = 0; i2 < 3; i2++) {
    66				let 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 (let i1 = 0; i1 < 9; i1++) {
    017			for (let i2 = 0; i2 < 3; i2++) {
    018				for (let 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('storage').style.display = "";
    036		document.getElementById('start').innerHTML = "実行";
    037	}
    038				//
    039				// StartPanel オブジェクト(メソッド)
    040				//
    041						// 「実行」ボタンがクリックされたときの処理
    042	StartPanel.prototype.onPlay = function()
    043	{
    044		let prb = "";
    045	
    046		for (let i1 = 0; i1 < 9; i1++) {
    047			for (let i2 = 0; i2 < 3; i2++) {
    048				for (let i3 = 0; i3 < 3; i3++) {
    049					let id = "bd" + i1 + i2 + i3;
    050					let str = eval("document.getElementById('" + id + "').value");
    051					if (str.length > 0) {
    052						let k;
    053						if (isNaN(str))   // 非数字か ?
    054							k = -1;
    055						else {
    056							k = parseInt(str);   // 整数への変換
    057							if (k <= 0 || k >= 10)
    058								k = -1;
    059						}
    060						mp.pr[i1][i2][i3] = k;
    061						mp.wk[i1][i2][i3] = 1;
    062						if (k > 0) {
    063							if (prb.length > 0)
    064								prb += "," + id + "," + str;
    065							else
    066								prb = id + "," + str;
    067						}
    068					}
    069					else {
    070						mp.pr[i1][i2][i3] = 0;
    071						mp.wk[i1][i2][i3] = 0;
    072					}
    073				}
    074			}
    075		}
    076	
    077		let sw = sp.check();
    078		if (sw > 0) {
    079			document.getElementById('storage').style.display = "none";
    080			document.getElementById('str').style.display = "none";
    081			document.getElementById('str_tx').style.display = "none";
    082			let key = prompt("この問題を保存する場合は,問題名を入力してください", "");
    083			if (key != null && key.length > 0)
    084				str_keep(key, prb);
    085			gp_start();
    086		}
    087	}
    088						// データは適切か?
    089	StartPanel.prototype.check = function()
    090	{
    091		let sw = 1;
    092								// 数字の適切性
    093		for (let i1 = 0; i1 < 9; i1++) {
    094			for (let i2 = 0; i2 < 3; i2++) {
    095				for (let i3 = 0; i3 < 3; i3++) {
    096					let id = "bd" + i1 + i2 + i3;
    097					eval("document.getElementById('" + id + "').style.color = '#000000'");
    098					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    099						sw = 0;
    100						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    101					}
    102				}
    103			}
    104		}
    105								// ブロック内
    106		if (sw > 0) {
    107			for (let i1 = 0; i1 < 9; i1++) {
    108				for (let i2 = 0; i2 < 8; i2++) {
    109					let k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    110					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    111					if (k1 > 0) {
    112						for (let i3 = i2+1; i3 < 9; i3++) {
    113							let k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    114							let id2 = "bd" + i1 + Math.floor(i3 / 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 (let i1 = 0; i1 < 9; i1++) {
    128				for (let i2 = 0; i2 < 8; i2++) {
    129					let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    130					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    131					if (k1 > 0) {
    132						for (let i3 = i2+1; i3 < 9; i3++) {
    133							let k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    134							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 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		if (sw > 0) {
    147			for (let i1 = 0; i1 < 9; i1++) {
    148				for (let i2 = 0; i2 < 8; i2++) {
    149					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    150					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    151					if (k1 > 0) {
    152						for (let i3 = i2+1; i3 < 9; i3++) {
    153							let k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    154							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    155							if (k1 == k2) {
    156								sw = 0;
    157								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    158								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    159							}
    160						}
    161					}
    162				}
    163			}
    164		}
    165	
    166		return sw;
    167	}
    			

      最後は,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 (let i1 = 0; i1 < 9; i1++) {
    018			for (let i2 = 0; i2 < 3; i2++) {
    019				for (let i3 = 0; i3 < 3; i3++) {
    020					let 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 (let i1 = 0; i1 < 9; i1++) {
    055			for (let i2 = 0; i2 < 3; i2++) {
    056				for (let i3 = 0; i3 < 3; i3++) {
    057					let 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 (let i1 = 0; i1 < 9; i1++) {
    072			for (let i2 = 0; i2 < 3; i2++) {
    073				for (let i3 = 0; i3 < 3; i3++) {
    074					let id = "bd" + i1 + i2 + i3;
    075					let str = eval("document.getElementById('" + id + "').value");
    076					let k;
    077					if (str.length <= 0)
    078						k = 0;
    079					else {
    080						if (isNaN(str))
    081							k = -1;
    082						else {
    083							k = parseInt(str);
    084							if (k == 0)
    085								k = -1;
    086						}
    087					}
    088					mp.pr[i1][i2][i3] = k;
    089				}
    090			}
    091		}
    092		let sw = gp.check();
    093		if (sw > 0)
    094			gp.count();
    095	}
    096						// データは適切か?
    097	GamePanel.prototype.check = function()
    098	{
    099		let sw = 1;
    100								// 数字の適切性
    101		for (let i1 = 0; i1 < 9; i1++) {
    102			for (let i2 = 0; i2 < 3; i2++) {
    103				for (let i3 = 0; i3 < 3; i3++) {
    104					let id = "bd" + i1 + i2 + i3;
    105					eval("document.getElementById('" + id + "').style.color = '#000000'");
    106					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    107						sw = 0;
    108						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    109					}
    110				}
    111			}
    112		}
    113								// ブロック内
    114		if (sw > 0) {
    115			for (let i1 = 0; i1 < 9; i1++) {
    116				for (let i2 = 0; i2 < 8; i2++) {
    117					let k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    118					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    119					if (k1 > 0) {
    120						for (let i3 = i2+1; i3 < 9; i3++) {
    121							let k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    122							let id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    123							if (k1 == k2) {
    124								sw = 0;
    125								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    126								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    127							}
    128						}
    129					}
    130				}
    131			}
    132		}
    133								// 行内
    134		if (sw > 0) {
    135			for (let i1 = 0; i1 < 9; i1++) {
    136				for (let i2 = 0; i2 < 8; i2++) {
    137					let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    138					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    139					if (k1 > 0) {
    140						for (let i3 = i2+1; i3 < 9; i3++) {
    141							let k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    142							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    143							if (k1 == k2) {
    144								sw = 0;
    145								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    146								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    147							}
    148						}
    149					}
    150				}
    151			}
    152		}
    153								// 列内
    154		if (sw > 0) {
    155			for (let i1 = 0; i1 < 9; i1++) {
    156				for (let i2 = 0; i2 < 8; i2++) {
    157					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    158					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    159					if (k1 > 0) {
    160						for (let i3 = i2+1; i3 < 9; i3++) {
    161							let k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    162							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    163							if (k1 == k2) {
    164								sw = 0;
    165								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    166								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    167							}
    168						}
    169					}
    170				}
    171			}
    172		}
    173	
    174		return sw;
    175	}
    176						// 数字の数を数える
    177	GamePanel.prototype.count = function()
    178	{
    179		for (let i1 = 0; i1 < 9; i1++) {
    180			let k1 = i1 + 1;
    181			let n = 0;
    182			for (let i2 = 0; i2 < 9; i2++) {
    183				for (let i3 = 0; i3 < 3; i3++) {
    184					for (let i4 = 0; i4 < 3; i4++) {
    185						if (mp.pr[i2][i3][i4] == k1)
    186							n++;
    187					}
    188				}
    189			}
    190			let id = "ct" + i1;
    191			eval("document.getElementById('" + id + "').value = " + n);
    192			if (n == 9)
    193				eval("document.getElementById('" + id + "').style.backgroundColor = '#ff0000'");
    194			else
    195				eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    196		}
    197	}
    			
    035 行目~ 036 行目( GamePanel 関数)

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

    044 行目( GamePanel 関数)

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

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

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

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

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

  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 (let i1 = 0; i1 < 9; i1++) {
    22			this.pr[i1] = new Array();
    23			this.wk[i1] = new Array();
    24			for (let 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 (let 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 (let i1 = 0; i1 < 9; i1++) {
    43			let 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 (let i1 = 0; i1 < 9; i1++) {
    50			let 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 (let i1 = 0; i1 < 3; i1++) {
    64			let id1 = "bd" + k + i1;
    65			for (let i2 = 0; i2 < 3; i2++) {
    66				let 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 (let i1 = 0; i1 < 9; i1++) {
    018			for (let i2 = 0; i2 < 3; i2++) {
    019				for (let i3 = 0; i3 < 3; i3++) {
    020					let 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 (let i1 = 0; i1 < 9; i1++) {
    044			let 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 (let i1 = 0; i1 < 9; i1++) {
    059			for (let i2 = 0; i2 < 3; i2++) {
    060				for (let i3 = 0; i3 < 3; i3++) {
    061					let 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 (let i1 = 0; i1 < 9; i1++) {
    076			for (let i2 = 0; i2 < 3; i2++) {
    077				for (let i3 = 0; i3 < 3; i3++) {
    078					let id = "bd" + i1 + i2 + i3;
    079					let str = eval("document.getElementById('" + id + "').value");
    080					let k;
    081					if (str.length <= 0)
    082						k = 0;
    083					else {
    084						if (isNaN(str))
    085							k = -1;
    086						else {
    087							k = parseInt(str);
    088							if (k == 0)
    089								k = -1;
    090						}
    091					}
    092					mp.pr[i1][i2][i3] = k;
    093				}
    094			}
    095		}
    096		let sw = gp.check();
    097		if (sw > 0)
    098			gp.count();
    099	}
    100						// データは適切か?
    101	GamePanel.prototype.check = function()
    102	{
    103		let sw = 1;
    104								// 背景色のクリア
    105		for (let i1 = 0; i1 < 9; i1++) {
    106			for (let i2 = 0; i2 < 3; i2++) {
    107				for (let i3 = 0; i3 < 3; i3++) {
    108					if (mp.wk[i1][i2][i3] == 0) {
    109						let id = "bd" + i1 + i2 + i3;
    110						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    111					}
    112				}
    113			}
    114		}
    115								// 数字の適切性
    116		for (let i1 = 0; i1 < 9; i1++) {
    117			for (let i2 = 0; i2 < 3; i2++) {
    118				for (let i3 = 0; i3 < 3; i3++) {
    119					let id = "bd" + i1 + i2 + i3;
    120					eval("document.getElementById('" + id + "').style.color = '#000000'");
    121					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    122						sw = 0;
    123						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    124					}
    125				}
    126			}
    127		}
    128								// ブロック内
    129		if (sw > 0) {
    130			for (let i1 = 0; i1 < 9; i1++) {
    131				for (let i2 = 0; i2 < 8; i2++) {
    132					let k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    133					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    134					if (k1 > 0) {
    135						for (let i3 = i2+1; i3 < 9; i3++) {
    136							let k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    137							let id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    138							if (k1 == k2) {
    139								sw = 0;
    140								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    141								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    142							}
    143						}
    144					}
    145				}
    146			}
    147		}
    148								// 行内
    149		if (sw > 0) {
    150			for (let i1 = 0; i1 < 9; i1++) {
    151				for (let i2 = 0; i2 < 8; i2++) {
    152					let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    153					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    154					if (k1 > 0) {
    155						for (let i3 = i2+1; i3 < 9; i3++) {
    156							let k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    157							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    158							if (k1 == k2) {
    159								sw = 0;
    160								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    161								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    162							}
    163						}
    164					}
    165				}
    166			}
    167		}
    168								// 列内
    169		if (sw > 0) {
    170			for (let i1 = 0; i1 < 9; i1++) {
    171				for (let i2 = 0; i2 < 8; i2++) {
    172					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    173					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    174					if (k1 > 0) {
    175						for (let i3 = i2+1; i3 < 9; i3++) {
    176							let k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    177							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    178							if (k1 == k2) {
    179								sw = 0;
    180								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    181								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    182							}
    183						}
    184					}
    185				}
    186			}
    187		}
    188	
    189		return sw;
    190	}
    191						// 数字の数を数える
    192	GamePanel.prototype.count = function()
    193	{
    194		for (let i1 = 0; i1 < 9; i1++) {
    195			let k1 = i1 + 1;
    196			let n = 0;
    197			for (let i2 = 0; i2 < 9; i2++) {
    198				for (let i3 = 0; i3 < 3; i3++) {
    199					for (let i4 = 0; i4 < 3; i4++) {
    200						if (mp.pr[i2][i3][i4] == k1)
    201							n++;
    202					}
    203				}
    204			}
    205			let id = "ct" + i1;
    206			eval("document.getElementById('" + id + "').value = " + n);
    207			if (n == 9)
    208				eval("document.getElementById('" + id + "').style.backgroundColor = '#ff0000'");
    209			else
    210				eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    211		}
    212	}
    213					// 「数字」ボタンがクリックされたときの処理(数字がおける場所)
    214	GamePanel.prototype.onSafe = function(k)
    215	{
    216		k++;
    217							// ブロック内
    218		for (let i1 = 0; i1 < 9; i1++) {
    219			let sw = 0;
    220			for (let i2 = 0; i2 < 3; i2++) {
    221				for (let i3 = 0; i3 < 3; i3++) {
    222					if (mp.wk[i1][i2][i3] == 2)
    223						mp.wk[i1][i2][i3] = 0;
    224					else if (mp.wk[i1][i2][i3] == 0) {
    225						let id = "bd" + i1 + i2 + i3;
    226						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    227					}
    228					if (mp.pr[i1][i2][i3] == k)
    229						sw = 1;
    230				}
    231			}
    232			for (i2 = 0; i2 < 3; i2++) {
    233				for (i3 = 0; i3 < 3; i3++) {
    234					if (mp.wk[i1][i2][i3] == 0 && (mp.pr[i1][i2][i3] > 0 || sw > 0))
    235						mp.wk[i1][i2][i3] = 2;
    236				}
    237			}
    238		}
    239							// 行内
    240		for (i1 = 0; i1 < 9; i1++) {
    241			for (i2 = 0; i2 < 9; i2++) {
    242				let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    243				if (k1 == k) {
    244					for (i3 = 0; i3 < 9; i3++) {
    245						if (mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] == 0)
    246							mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] = 2;
    247					}
    248					break;
    249				}
    250			}
    251		}
    252							// 列内
    253		for (i1 = 0; i1 < 9; i1++) {
    254			for (i2 = 0; i2 < 9; i2++) {
    255				k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    256				if (k1 == k) {
    257					for (i3 = 0; i3 < 9; i3++) {
    258						if (mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] == 0)
    259							mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] = 2;
    260					}
    261					break;
    262				}
    263			}
    264		}
    265							// 背景色の変更
    266		for (i1 = 0; i1 < 9; i1++) {
    267			let id = "num_b" + i1;
    268			eval("document.getElementById('" + id + "').style.color = '#000000'");
    269		}
    270		let id = "num_b" + (k - 1);
    271		eval("document.getElementById('" + id + "').style.color = '#00ff00'");
    272	
    273		for (i1 = 0; i1 < 9; i1++) {
    274			for (i2 = 0; i2 < 3; i2++) {
    275				for (i3 = 0; i3 < 3; i3++) {
    276					if (mp.wk[i1][i2][i3] == 0) {
    277						let id = "bd" + i1 + i2 + i3;
    278						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffd700'");
    279					}
    280				}
    281			}
    282		}
    283	}
    			
    043 行目~ 046 行目( GamePanel 関数)

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

    105 行目~ 114 行目( check メソッド)

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

    214 行目~ 283 行目( onSafe メソッド)

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

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

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

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

    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		<SCRIPT TYPE="text/javascript" SRC="storage/storage.js"></SCRIPT>
    12	</HEAD>
    13	<BODY CLASS="eeffee">
    14		<DIV ID="problem_set_sheet" STYLE="display: none">
    15			<BUTTON CLASS="std" onClick="problem_set(1)">解</BUTTON> 
    16			空白<INPUT ID="sp" TYPE="text" SIZE="2" STYLE="font-size: 90%">
    17			<BUTTON CLASS="std" onClick="problem_set(2)">Go</BUTTON> 
    18			<BUTTON CLASS="std" onClick="problem_set(3)">設定</BUTTON>
    19			<P CLASS="center"><SCRIPT TYPE="text/javascript">
    20				for (let i1 = 0; i1 < 9; i1++) {
    21					for (let i2 = 0; i2 < 9; i2++) {
    22						let str = '<INPUT ID="tx' + i1 + i2 + '" TYPE="text" SIZE="2" STYLE="width: 30px; height: 30px; font-size: 30px; text-align: center;">'
    23						document.write(str + "\n");
    24					}
    25					document.write("<BR>\n");
    26				}
    27			</SCRIPT></P>
    28			<INPUT ID="msg" TYPE="text" SIZE="5" STYLE="font-size: 90%">
    29		</DIV>
    30	
    31		<H1>ナンプレ:ステップ5(記憶)</H1>
    32		<SCRIPT TYPE="text/javascript">
    33			mp_start();
    34		</SCRIPT>
    35		<A HREF="method.htm" TARGET="method"><BUTTON ID="method" CLASS="std">遊び方</BUTTON></A>
    36		<BUTTON ID="memory" CLASS="std" onClick="gp.memory()">記憶</BUTTON>
    37		<BUTTON ID="back" CLASS="std" onClick="gp.back()">戻る</BUTTON>
    38		<BUTTON ID="start" CLASS="std">実行</BUTTON>
    39		<BUTTON ID="finish" CLASS="std" onClick="mp.finish()">ゲーム終了</BUTTON>
    40		<BUTTON ID="problem" CLASS="std" onClick="problem_set(0)">問題</BUTTON><BR>
    41		<BUTTON ID="storage" CLASS="std" onClick="storage()">過去の問題</BUTTON>
    42		<DIV ID="str" STYLE="display: none"><INPUT TYPE="button" VALUE="件数" onClick="str_num()" STYLE="font-size:90%"> 
    43		<INPUT TYPE="button" VALUE="問題リスト出力" onClick="str_out()" STYLE="font-size:90%"> 
    44		<INPUT TYPE="button" VALUE="全削除" onClick="str_del_all()" STYLE="font-size:90%"><BR>
    45		問題名 : <INPUT TYPE="text" ID="s_name" SIZE="10" STYLE="font-size:120%"> <INPUT TYPE="button" VALUE="設定" onClick="str_set()" STYLE="font-size:90%"><BR>
    46		問題名 : <INPUT TYPE="text" ID="d_name" SIZE="10" STYLE="font-size:120%"> <INPUT TYPE="button" VALUE="削除" onClick="str_del()" STYLE="font-size:90%"><BR></DIV>
    47		<TEXTAREA TYPE="text" ID="str_tx" COLS="30" ROWS="5" STYLE="font-size: 120%; display: none"></TEXTAREA>
    48		<SCRIPT TYPE="text/javascript">
    49			sp_start();
    50		</SCRIPT>
    51	</BODY>
    52	</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 (let i1 = 0; i1 < 9; i1++) {
    22			this.pr[i1] = new Array();
    23			this.wk[i1] = new Array();
    24			for (let 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 (let 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 (let i1 = 0; i1 < 9; i1++) {
    43			let 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 (let i1 = 0; i1 < 9; i1++) {
    50			let 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 (let i1 = 0; i1 < 3; i1++) {
    64			let id1 = "bd" + k + i1;
    65			for (let i2 = 0; i2 < 3; i2++) {
    66				let 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 (let i1 = 0; i1 < 9; i1++) {
    017			for (let i2 = 0; i2 < 3; i2++) {
    018				for (let 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('storage').style.display = "";
    038		document.getElementById('start').innerHTML = "実行";
    039	}
    040				//
    041				// StartPanel オブジェクト(メソッド)
    042				//
    043						// 「実行」ボタンがクリックされたときの処理
    044	StartPanel.prototype.onPlay = function()
    045	{
    046		let prb = "";
    047	
    048		for (let i1 = 0; i1 < 9; i1++) {
    049			for (let i2 = 0; i2 < 3; i2++) {
    050				for (let i3 = 0; i3 < 3; i3++) {
    051					let id = "bd" + i1 + i2 + i3;
    052					let str = eval("document.getElementById('" + id + "').value");
    053					if (str.length > 0) {
    054						let k;
    055						if (isNaN(str))   // 非数字か ?
    056							k = -1;
    057						else {
    058							k = parseInt(str);   // 整数への変換
    059							if (k <= 0 || k >= 10)
    060								k = -1;
    061						}
    062						mp.pr[i1][i2][i3] = k;
    063						mp.wk[i1][i2][i3] = 1;
    064						if (k > 0) {
    065							if (prb.length > 0)
    066								prb += "," + id + "," + str;
    067							else
    068								prb = id + "," + str;
    069						}
    070					}
    071					else {
    072						mp.pr[i1][i2][i3] = 0;
    073						mp.wk[i1][i2][i3] = 0;
    074					}
    075				}
    076			}
    077		}
    078	
    079		let sw = sp.check();
    080		if (sw > 0) {
    081			document.getElementById('storage').style.display = "none";
    082			document.getElementById('str').style.display = "none";
    083			document.getElementById('str_tx').style.display = "none";
    084			let key = prompt("この問題を保存する場合は,問題名を入力してください", "");
    085			if (key != null && key.length > 0)
    086				str_keep(key, prb);
    087			gp_start();
    088		}
    089	}
    090						// データは適切か?
    091	StartPanel.prototype.check = function()
    092	{
    093		let sw = 1;
    094								// 数字の適切性
    095		for (let i1 = 0; i1 < 9; i1++) {
    096			for (let i2 = 0; i2 < 3; i2++) {
    097				for (let i3 = 0; i3 < 3; i3++) {
    098					let id = "bd" + i1 + i2 + i3;
    099					eval("document.getElementById('" + id + "').style.color = '#000000'");
    100					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    101						sw = 0;
    102						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    103					}
    104				}
    105			}
    106		}
    107								// ブロック内
    108		if (sw > 0) {
    109			for (let i1 = 0; i1 < 9; i1++) {
    110				for (let i2 = 0; i2 < 8; i2++) {
    111					let k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    112					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    113					if (k1 > 0) {
    114						for (let i3 = i2+1; i3 < 9; i3++) {
    115							let k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    116							let id2 = "bd" + i1 + Math.floor(i3 / 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 (let i1 = 0; i1 < 9; i1++) {
    130				for (let i2 = 0; i2 < 8; i2++) {
    131					let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    132					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    133					if (k1 > 0) {
    134						for (let i3 = i2+1; i3 < 9; i3++) {
    135							let k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    136							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 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 (let i1 = 0; i1 < 9; i1++) {
    150				for (let i2 = 0; i2 < 8; i2++) {
    151					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    152					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    153					if (k1 > 0) {
    154						for (let i3 = i2+1; i3 < 9; i3++) {
    155							let k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    156							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 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		return sw;
    169	}
    			

      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 (let i1 = 0; i1 < 9; i1++) {
    021			for (let i2 = 0; i2 < 3; i2++) {
    022				for (let i3 = 0; i3 < 3; i3++) {
    023					let 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 (let i1 = 0; i1 < 9; i1++) {
    049			let 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 (let i1 = 0; i1 < 9; i1++) {
    064			for (let i2 = 0; i2 < 3; i2++) {
    065				for (let i3 = 0; i3 < 3; i3++) {
    066					let 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 (let i1 = 0; i1 < 9; i1++) {
    081			for (let i2 = 0; i2 < 3; i2++) {
    082				for (let i3 = 0; i3 < 3; i3++) {
    083					let id = "bd" + i1 + i2 + i3;
    084					let str = eval("document.getElementById('" + id + "').value");
    085					let k;
    086					if (str.length <= 0)
    087						k = 0;
    088					else {
    089						if (isNaN(str))
    090							k = -1;
    091						else {
    092							k = parseInt(str);
    093							if (k == 0)
    094								k = -1;
    095						}
    096					}
    097					mp.pr[i1][i2][i3] = k;
    098				}
    099			}
    100		}
    101		let sw = gp.check();
    102		if (sw > 0)
    103			gp.count();
    104	}
    105						// データは適切か?
    106	GamePanel.prototype.check = function()
    107	{
    108		let sw = 1;
    109								// 背景色のクリア
    110		for (let i1 = 0; i1 < 9; i1++) {
    111			for (let i2 = 0; i2 < 3; i2++) {
    112				for (let i3 = 0; i3 < 3; i3++) {
    113					if (mp.wk[i1][i2][i3] == 0) {
    114						let id = "bd" + i1 + i2 + i3;
    115						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    116					}
    117				}
    118			}
    119		}
    120								// 数字の適切性
    121		for (let i1 = 0; i1 < 9; i1++) {
    122			for (let i2 = 0; i2 < 3; i2++) {
    123				for (let i3 = 0; i3 < 3; i3++) {
    124					let id = "bd" + i1 + i2 + i3;
    125					eval("document.getElementById('" + id + "').style.color = '#000000'");
    126					if (mp.pr[i1][i2][i3] < 0 || mp.pr[i1][i2][i3] > 9) {
    127						sw = 0;
    128						eval("document.getElementById('" + id + "').style.color = '#ff0000'");
    129					}
    130				}
    131			}
    132		}
    133								// ブロック内
    134		if (sw > 0) {
    135			for (let i1 = 0; i1 < 9; i1++) {
    136				for (let i2 = 0; i2 < 8; i2++) {
    137					let k1 = mp.pr[i1][Math.floor(i2/3)][i2%3];
    138					let id1 = "bd" + i1 + Math.floor(i2 / 3) + (i2 % 3);
    139					if (k1 > 0) {
    140						for (let i3 = i2+1; i3 < 9; i3++) {
    141							let k2 = mp.pr[i1][Math.floor(i3/3)][i3%3];
    142							let id2 = "bd" + i1 + Math.floor(i3 / 3) + (i3 % 3);
    143							if (k1 == k2) {
    144								sw = 0;
    145								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    146								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    147							}
    148						}
    149					}
    150				}
    151			}
    152		}
    153								// 行内
    154		if (sw > 0) {
    155			for (let i1 = 0; i1 < 9; i1++) {
    156				for (let i2 = 0; i2 < 8; i2++) {
    157					let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    158					let id1 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i2 / 3)) + (i1 % 3) + (i2 % 3);
    159					if (k1 > 0) {
    160						for (let i3 = i2+1; i3 < 9; i3++) {
    161							let k2 = mp.pr[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3];
    162							let id2 = "bd" + (Math.floor(i1 / 3) * 3 + Math.floor(i3 / 3)) + (i1 % 3) + (i3 % 3);
    163							if (k1 == k2) {
    164								sw = 0;
    165								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    166								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    167							}
    168						}
    169					}
    170				}
    171			}
    172		}
    173								// 列内
    174		if (sw > 0) {
    175			for (let i1 = 0; i1 < 9; i1++) {
    176				for (let i2 = 0; i2 < 8; i2++) {
    177					let k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    178					let id1 = "bd" + (Math.floor(i1 / 3) + Math.floor(i2 / 3) * 3) + (i2 % 3) + (i1 % 3);
    179					if (k1 > 0) {
    180						for (let i3 = i2+1; i3 < 9; i3++) {
    181							let k2 = mp.pr[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3];
    182							let id2 = "bd" + (Math.floor(i1 / 3) + Math.floor(i3 / 3) * 3) + (i3 % 3) + (i1 % 3);
    183							if (k1 == k2) {
    184								sw = 0;
    185								eval("document.getElementById('" + id1 + "').style.color = '#ff0000'");
    186								eval("document.getElementById('" + id2 + "').style.color = '#ff0000'");
    187							}
    188						}
    189					}
    190				}
    191			}
    192		}
    193	
    194		return sw;
    195	}
    196						// 数字の数を数える
    197	GamePanel.prototype.count = function()
    198	{
    199		for (let i1 = 0; i1 < 9; i1++) {
    200			let k1 = i1 + 1;
    201			let n = 0;
    202			for (let i2 = 0; i2 < 9; i2++) {
    203				for (let i3 = 0; i3 < 3; i3++) {
    204					for (let i4 = 0; i4 < 3; i4++) {
    205						if (mp.pr[i2][i3][i4] == k1)
    206							n++;
    207					}
    208				}
    209			}
    210			let id = "ct" + i1;
    211			eval("document.getElementById('" + id + "').value = " + n);
    212			if (n == 9)
    213				eval("document.getElementById('" + id + "').style.backgroundColor = '#ff0000'");
    214			else
    215				eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    216		}
    217	}
    218					// 「数字」ボタンがクリックされたときの処理(数字がおける場所)
    219	GamePanel.prototype.onSafe = function(k)
    220	{
    221		k++;
    222							// ブロック内
    223		for (let i1 = 0; i1 < 9; i1++) {
    224			let sw = 0;
    225			for (let i2 = 0; i2 < 3; i2++) {
    226				for (let i3 = 0; i3 < 3; i3++) {
    227					if (mp.wk[i1][i2][i3] == 2)
    228						mp.wk[i1][i2][i3] = 0;
    229					else if (mp.wk[i1][i2][i3] == 0) {
    230						let id = "bd" + i1 + i2 + i3;
    231						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffffff'");
    232					}
    233					if (mp.pr[i1][i2][i3] == k)
    234						sw = 1;
    235				}
    236			}
    237			for (i2 = 0; i2 < 3; i2++) {
    238				for (i3 = 0; i3 < 3; i3++) {
    239					if (mp.wk[i1][i2][i3] == 0 && (mp.pr[i1][i2][i3] > 0 || sw > 0))
    240						mp.wk[i1][i2][i3] = 2;
    241				}
    242			}
    243		}
    244							// 行内
    245		for (i1 = 0; i1 < 9; i1++) {
    246			for (i2 = 0; i2 < 9; i2++) {
    247				let k1 = mp.pr[Math.floor(i1/3)*3+Math.floor(i2/3)][i1%3][i2%3];
    248				if (k1 == k) {
    249					for (i3 = 0; i3 < 9; i3++) {
    250						if (mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] == 0)
    251							mp.wk[Math.floor(i1/3)*3+Math.floor(i3/3)][i1%3][i3%3] = 2;
    252					}
    253					break;
    254				}
    255			}
    256		}
    257							// 列内
    258		for (i1 = 0; i1 < 9; i1++) {
    259			for (i2 = 0; i2 < 9; i2++) {
    260				k1 = mp.pr[Math.floor(i1/3)+Math.floor(i2/3)*3][i2%3][i1%3];
    261				if (k1 == k) {
    262					for (i3 = 0; i3 < 9; i3++) {
    263						if (mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] == 0)
    264							mp.wk[Math.floor(i1/3)+Math.floor(i3/3)*3][i3%3][i1%3] = 2;
    265					}
    266					break;
    267				}
    268			}
    269		}
    270							// 背景色の変更
    271		for (i1 = 0; i1 < 9; i1++) {
    272			let id = "num_b" + i1;
    273			eval("document.getElementById('" + id + "').style.color = '#000000'");
    274		}
    275		let id = "num_b" + (k - 1);
    276		eval("document.getElementById('" + id + "').style.color = '#00ff00'");
    277	
    278		for (i1 = 0; i1 < 9; i1++) {
    279			for (i2 = 0; i2 < 3; i2++) {
    280				for (i3 = 0; i3 < 3; i3++) {
    281					if (mp.wk[i1][i2][i3] == 0) {
    282						let id = "bd" + i1 + i2 + i3;
    283						eval("document.getElementById('" + id + "').style.backgroundColor = '#ffd700'");
    284					}
    285				}
    286			}
    287		}
    288	}
    289						// 状態の保存
    290	GamePanel.prototype.memory = function()
    291	{
    292		document.getElementById('back').style.display = "";
    293		let state = new Array();
    294		for (let i1 = 0; i1 < 9; i1++) {
    295			state[i1] = new Array();
    296			for (let i2 = 0; i2 < 3; i2++) {
    297				state[i1][i2] = new Array();
    298				for (let i3 = 0; i3 < 3; i3++)
    299					state[i1][i2][i3] = mp.pr[i1][i2][i3];
    300			}
    301		}
    302		gp.kp[gp.ct] = state;
    303		gp.ct++;
    304		alert("状態を記憶しました\n 記憶状態数 = " + gp.ct);
    305	}
    306	
    307						// 一つ前の状態に戻す
    308	GamePanel.prototype.back = function()
    309	{
    310		gp.ct--;
    311		if (gp.ct == 0)
    312			document.getElementById('back').style.display = "none";
    313		mp.pr = gp.kp[gp.ct];
    314		for (let i1 = 0; i1 < 9; i1++) {
    315			for (let i2 = 0; i2 < 3; i2++) {
    316				for (let i3 = 0; i3 < 3; i3++) {
    317					let id = "bd" + i1 + i2 + i3;
    318					if (mp.pr[i1][i2][i3] != 0)
    319						eval("document.getElementById('" + id + "')").value = mp.pr[i1][i2][i3];
    320					else
    321						eval("document.getElementById('" + id + "')").value = "";
    322				}
    323			}
    324		}
    325		let sw = gp.check();
    326		if (sw > 0)
    327			gp.count();
    328	}
    			
    017,018 行目( GamePanel 関数)

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

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

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

    290 行目~ 305 行目( memory メソッド)

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

    308 行目~ 328 行目( back メソッド)

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

情報学部 菅沼ホーム JavaScript 目次 索引