Dr.GUI .NET #8
ASP.NET アプリケーションとしての Conway のライフ ゲーム
September 20, 2002 日本語版最終更新日 2002 年 10 月 17 日
概要 :
名医は、先月から Conway のライフ ゲームでの作業を続けています。
今月は、戦略的なクライアント側 Microsoft JScript のメソッドを使用して、
Microsoft ASP.NET アプリケーションとしてのゲームを開発します。
目次
はじめに
前回のコラムと今回のコラム
古いもの...
新しいもの...
借りたもの、そして...
青ざめたもの!
デザイン上の選択肢
試してみましょう!
まとめと次回予告
「Dr. GUI .NET Message Boards」 (英語) で、
この記事の感想を私たち、そして世界に向けて書き込んでください。
「http://drgui.coldrooster.com/DrGUIdotNet/8/」 (英語) で、
Microsoft® ASP.NET アプリケーションとして実行するサンプルとソース コードを確認してください。
はじめに
Dr. GUI がお届けする .NET Framework プログラミングの 9 作目の記事にようこそ。
(これまでの 8 作は、
.NET Framework 配列要素の番号付けの手法を使用して名前を付けてきました。
そのため、1 ではなく 0 から始まり、
「Dr. GUI .NET #0」 から 「Dr. GUI .NET #7」 になっています。)
過去の記事を探している方は、
「Dr. GUI .NET home page」
(英語) を調べてください。
Dr. GUI は、
あなたが Message Boards
に書き込んでくれることを望んでいます。
そこでは既に活発な議論が交わされていますが、
あなたの参加でいっそう盛り上がるでしょう!
誰でもメッセージを読むことができますが、
投稿するには、あなたを認証するための Passport が必要です。
心配しないでください。
Passport の設定は簡単です。
Passport はどんな電子メール アカウントにも関連付けることができます。
前回のコラムと今回のコラム
前回は、
Conway のライフ ゲームを Microsoft® Windows® フォーム アプリケーションとして作成しました。
ライフ ゲームとは何か、
そしてその由来を知るためには、
「前回の記事」 を参照してください。
また、前回のコードの大部分を再利用しているので、
そのためにも前回の記事を参照してください。
今回、このゲームを主にサーバー側の Microsoft® ASP.NET アプリケーションに変換するつもりです。
ただし、今回は Dr. GUI が初めてクライアント側の JScript に本格的に進出したので、
サーバー側のコードに依存する場合よりもすばやく盤面を編集したり、ゲームを行うことができるようになったことがわかります。
それでは、Dr. GUI が前回のこのサーバー側のコードを新しい Web クライアントとどのように結び付けたのか見てみましょう。
Web アプリケーションは次のようになります。
古いもの...
Windows フォーム アプリケーションから多くのクライアント コードを再利用できました。
最も多く再利用したのは、
あるライフ ゲームの盤面を別の盤面から計算するメソッドです。
LifeCalc.vb ファイル全体をまったく変更しないで、
以前のプロジェクトから新しいプロジェクトにコピーしました。
ただし、CalcNext メソッドは異なるオーバーロードを呼び出しました。
Windows フォーム版では、
2 つの配列 (古い配列と新しい配列) を受け取るオーバーロードを呼び出し、
その後、配列への参照を交換していました。
つまり、アプリケーション起動時に 2 つの配列を作成して、
アプリケーションが終了するまで同じ組み合わせの配列を使用していました。
新しい世代を計算して、配列を交換するコードは次のとおりです。
Dim newBoard As Integer(,) = _
LifeCalc.CalcNext(currBoard, tempBoard)
tempBoard = currBoard
currBoard = newBoard
ASP.NET 版では、アプリケーションのライフ サイクルが異なります。
すべての変数は、「HTTP 要求のたびに」初期化されます。
これは、"状態なし" の操作と呼ばれています。
(2 つの配列、描画用のビットマップ、Graphics オブジェクトなど、
あらゆる状態をアプリケーションの起動から終了まで保持している Windows フォーム版とは対照的です。)
HTTP 要求間で状態を保持する場合は、
何らかのストレージ メカニズムを使用して特殊なことを行う必要があります。
ストレージ メカニズムとしては、
Cookie、フォーム上の (おそらく非表示の)入力フィールド、
または、最も柔軟なセッション状態かビュー ステートなどがあります。
つまり、各 HTTP 要求が配列が新しく作成することになります。
各要求間で配列を維持しておく優れた方法はありません。
(この状態なしの操作は、Web アプリケーションでは典型的なもので、スケーラビリティを拡張します。)
いずれにしても、ASP.NET 版では毎回両方の配列を作成する必要があるので、
Page_Load で現在の盤面を作成し、
CalcNext のオーバーロードで新しい盤面を作成しました。
CalcNext のオーバーロードは、
自動的に結果配列を作成し、
その配列への参照を返します。
これを行うコードは ([Single Step] のボタンのハンドラも) 次のとおりです。
Private Sub RunSingle_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles RunSingle.Click
currBoard = CalcNext(currBoard)
End Sub
このコードは、新しく作成した更新済みの盤面を参照するように currBoard を設定します。
その結果、(古い配列を参照するものが既に存在しないので) 古い配列を破棄します。
ただし、いずれにしてもすぐに終了するので、
少し早く古い配列を破棄しても問題はありません。
(最終的にはガベージ コレクタがクリーンアップを行います。)
また、FillRandom メソッドも変更せずに使用しました。
名医は、Windows フォーム アプリケーションから ASP.NET アプリケーションにこのメソッドをただコピーしただけです!
「前回の記事」 で、
コードを確認できます。
新しいもの...
クライアント側のスクリプトや ASP.NET のイベント ハンドラの両方など、
すべての新しい要素を理解するには、
Web ページのライフ サイクルと ASP.NET 要求のライフ サイクルについて説明する必要があります。
その結果、各コードを実行するときに動作が明確になります。
(Dr. GUI は、多くの読者にとってこの作業は復習にすぎないことはわかっています。
それでも、Dr. GUI は Web 開発の経験が比較的浅いので、
彼がこのプログラムを記述しているときにこのことを理解することが役に立つと考えました。
したがって、一部の読者にとってはこのことを理解することが役に立つだろうと思いました。)
Web ページのライフ サイクル
Web ページのライフ サイクルについて知っておく必要のあることは、
このアプリケーションでは非常に単純です。
まず、ユーザーはそのページの URL を入力またはクリックします。
ブラウザがページを読み込みます。
そのとき window_onload 関数内にスクリプトが存在する場合は、
そのスクリプトを実行します。
(アプリケーションを実行する場合、
Web ページを読み込むたびに、
すべての変数を新しく設定し、タイマを再起動するので、
アプリケーションにはタイマを処理するスクリプトが必要です。)
次に、Web ページがユーザーのブラウザに表示され、
ユーザーの操作を待機します。
ユーザーの操作は、入力イベントまたはマウス イベントの形式になります。
ほとんどのイベントはある既定の動作を行います
(たとえば、入力により現在選択されているテキスト ボックスにそのテキストが表示され、
ボタンによりサーバーにフォームが送信されます)。
ただし、任意のイベントにスクリプトを関連付けることもできます。
このアプリケーションには、
ライフ ゲームの盤面上でのマウス クリック イベント用のスクリプト、
およびタイマを有効または無効にするボタン用のスクリプトがあります。
その他に、タイマの Tick イベントを処理するイベント ハンドラもあります。
スクリプト内でクリックまたは呼び出しのいずれかを行うことにより、
サーバーに新しい Web ページの要求を生成できます。
タイマを有効または無効にするボタンを除くすべてのボタンは、
サーバー要求を生成するので、
タイマの Tick ハンドラはそのボタンのいずれかの click メソッドを呼び出すことにより、
要求を生成します。
基本的には、ボタンのクリックはプログラムで行われます。
タイマの Tick イベントは、間接的にサーバー要求を生成し、
その結果新しい Web ページを生成し、それをブラウザに返すので、
そのページの特定のインスタンスでは、
複数のタイマ Tick イベントを取得しないことに注意してください。
それに対して、複数の Tick イベントに対してタイマを保持し続けようとする場合、
タイマが有効であるという情報を新しい Web ページに渡す方法が必要になります。
Web ページのコードについて説明するときに、
この方法を説明します。
また、ASP.NET がこのようなサーバー要求を処理する方法についても、後で説明しましょう。
(読者のご想像どおり、これを説明するセクションのタイトルは「ASP.NET 要求のライフ サイクル」です。)
最終的に、ページがアンロードされます。
おそらく、ユーザーがリンクまたはボタンをクリックするか、ブラウザを閉じます。
必要に応じて、アンロード イベントを捕捉できます。
ただし、このアプリケーションではその必要がないので、
このイベントを捕捉しません。
その後、ユーザーが新しいページを要求した場合、
新しいページが読み込まれ (ライフ ゲームでは、別のフォーム データを持つ同じページになります)、
サイクルが再開します。
ASP.NET 要求のライフ サイクル
要求がサーバーに到着すると、
一連のイベントが発生します。
このアプリケーションでは、これらのイベントの一部しか取り上げません。
ただし、注目すべき例外的なイベントを 1 つ取り上げます。
まず注目するのは、
Page Load イベントです。
Page Load イベント ハンドラでは、配列の初期化だけを行います。
つまり、Web ページに配列が存在しない場合 (たとえば、初めてページが表示されるとき)、
オブジェクト内の非表示のテキスト フィールドから配列を読み取るか、
または新しい配列を作成して、その配列をランダム パターンで初期化します。
次に、ボタン クリック イベントにより要求が発生した場合、
([Single Step] ボタンをクリックするときの RunSingle_Click のような) 適切なイベント ハンドラ メソッドが実行されます。
これにより配列を変更できます。
最後に、そのページの pre-render イベントを使用して、配列の値をテーブルに書き込みます。
このテーブルは値をクライアント側で操作できるように、
Web ページ上や非表示の INPUT 要素内にレンダリングされます。
その後 ASP.NET が、
すべてのコントロール (DIV 要素に格納されたテーブル形式の HTML テキストなど) から Web ページをレンダリングします。
Web フォームの基本的なライフ サイクルの詳細については、
「Web フォーム ページの処理」を参照してください。
(pre-render イベントなど) さらに詳しく知りたい場合は、
「Control Execution Lifecycle」 (英語) を参照してください。
クライアント側で実行すること、サーバー側で実行すること
既に説明したように、
クライアントとサーバーに処理を分割します。
実行する順にコードを参照して、それぞれの処理が実行される場所を確認しましょう。
手順 1 : ブラウザの要求に応じて Web ページを作成する
アプリケーションの実行中に一番最初に行われることは、
ユーザーが アプリケーション (英語) の URL に移動することです。
この操作が行われると、
ASP.NET がページ オブジェクトを作成し、
最終的に Page_Load メソッドなど、
そのオブジェクトのメソッドを呼び出しを開始します。
Page_Load メソッドが実行される最初のコードです。
コードは次のとおりです。
Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' クリックしたボタンが [Clear] の場合、配列を読み込みません。
If (Request.Form.Get("Clear") = Nothing) Then
' クリックしたボタンが [Clear] ではない場合、配列を取得します。
'currBoard = Session("currBoard")
currBoard = GetBoardFromString(LifeData.Value)
If currBoard Is Nothing Then
currBoard = New Integer(rows - 1, cols - 1) {}
FillRandom(200, False)
End If
End If
End Sub
ここでは、[Clear] ボタンをクリックしたことにより要求が発生したかどうかを確認します。
[Clear] ボタンをクリックした場合、配列をクリアするだけなので、
配列を作成することには意味がありません。
[Clear] ボタンをクリックしたことにより要求が発生したのではない場合
(および、ページを初めて参照したのでボタンをクリックしていない場合)、
GetBoardFromString を呼び出して、
非表示の入力要素から配列を作成します。
非表示の入力要素からの作成に失敗すると、
新しい配列を作成して、その配列にランダムに生命体を配置します。
(非表示の入力要素にはまだ何も存在しないので、
初めて実行される場合にこの動作が行われます。)
初めて実行されるときは、
ボタン クリック イベント ハンドラが呼び出されません。
ページが表示されていないので、
ユーザーはボタンをクリックする機会がありません!
ただし、pre-render イベントが発生すると、
テーブル表現の HTML に配列をレンダリングして、
この HTML をテーブルの適切な場所にある DIV 要素に挿入できます。
また、この機会を利用して、配列を文字列にレンダリングし、
非表示の要素としてページに渡します。
pre-render イベントを処理し、
テーブルや文字列にレンダリングするコードは次のとおりです。
Private Sub Page_PreRender(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.PreRender
LifeBoard.InnerHtml = _
RenderLifeToTable(currBoard)
LifeData.Value = RenderLifeToString(currBoard)
'Session("currBoard") = currBoard
End Sub
このコードは、テーブルの HTML レンダリングを保持するために、
LifeBoard DIV 要素の InnerHtml プロパティを設定します
(その文字列の構築方法はこの後すぐに説明します)。
同様に、配列を文字列にレンダリングします。
配列を HTML テーブルとしてレンダリングする方法は次のとおりです。
Private Function RenderLifeToTable(ByVal board As Integer(,)) _
As String
Dim ret As New StringBuilder( _
"<table cellspacing=0 cellpadding=0>")
Dim i, j As Integer
For i = 0 To rows - 1
ret.Append("<tr>")
For j = 0 To cols - 1
ret.AppendFormat( _
"<td><img id = ""IMG_{0}_{1}"" src={2}.gif></td>", _
i, j, IIf(currBoard(i, j) = 0, "empty", "alive"))
Next
ret.Append("</tr>")
Next
ret.Append("</table>")
Return ret.ToString()
End Function
最初に、String ではなく、StringBuilder を作成して、
結果の HTML を保持します。
テーブルの開始タグになるようにこれを初期化します。
一連の <TD> (テーブル データ) 要素 を保持する <TR> (テーブル行) 要素を追加する二重ループに入ります。
<TD> はそれぞれ 1 つのセルを表し、
そのセルには <IMG> タグが含まれることに注意してください。
<IMG> タグはセルに生命体を表示する必要があるかどうか指定します。
また、各 <IMG> タグは一意 ID を持つことに注意してください。
その ID は行番号と列番号を保持します。
後でこの ID を使用して、
Microsoft® JScript コード内でどのセルをクリックしたかを識別します。
(これらの ID を使用すると、
ピクセルのオフセットから位置計算を行う必要がなくなります。
代わりに、ブラウザがクリックした項目の ID を報告するようにし、
その ID からインデックスを解析します。)
最後に、それぞれのタグを閉じ、
文字列ビルダを文字列として返します。
非表示の入力要素に格納できるようにライフ ゲームの盤面の配列を文字列にレンダリングすることはさらに簡単です。
これは上記のコードに似ていますが、
生命体が存在するかしないかを示すために "0" および "1" という文字だけをレンダリングします。
Private Function RenderLifeToString(ByVal board As Integer(,)) _
As String
Dim ret As New StringBuilder(rows * cols)
ret.Length = rows * cols
Dim i, j As Integer
Dim charCount As Integer = 0
For i = 0 To rows - 1
For j = 0 To cols - 1
ret(charCount) = _
IIf(board(i, j) = 0, "0", "1")
charCount += 1
Next
Next
Return ret.ToString()
End Function
この場合、適切なコンストラクタを呼び出し、
その長さの最大値を設定して、
StringBuilder に適切な長さを割り当てます。
長さを正しく設定すると、StringBuilder が適切なサイズになります。
その後、各文字を正しく設定します。
この操作は Append や AppendFormat を使用するよりも迅速に行われますが、
テーブルのレンダリングでは要素のサイズが変数になるので、
Append メソッドを使用せずに文字列を構築するのは困難です。
ASP.NET は、DIV および非表示の INPUT など、
ページ全体をレンダリングし、
それをブラウザに送信します。
手順 2 : ブラウザで起こることを確認する
ブラウザは OnLoad 関数を実行した後に、
ページを読み込んで表示します。
この関数については、(後で) タイマのセクションで説明します。
初めて実行される場合は、
タイマが設定されていないので、
OnLoad 関数は何も実行しないで戻ります。
ユーザーは自由にページと対話できます。
ユーザーがタイマの切り替えボタン以外の任意のボタンをクリックすると、
サーバーに対して要求が生成されるので、
サーバーで Page Load イベントが発生し、そのイベントが処理され、
新しい値を使用してページをレンダリングするというサイクルが再開します。
ユーザーがタイマの切り替えボタンをクリックすると、
JScript を実行して、タイマをセットアップします。
これは、正しく機能するために 3 つの関数を必要とします。
このボタンについては別に説明します。
その他に、ユーザーはライフ ゲームの盤面のテーブル自体をクリックすることがあります。
ライフ ゲームの盤面を編集するためのクリック ハンドラは次のとおりです。
function LifeBoard_onclick() {
imgID = event.srcElement.id;
sa = imgID.split("_"); // 形式は "IMG__" です。
// sa[0] は、"IMG" を保持する必要があります。
if (sa[0] == "IMG") {
row = sa[1]; col = sa[2];
// 32 は列数です。サーバー側でこの数を変更する場合は、
// 次の行を必ず変更するようにしてください。
linearPos = row * 32 + new Number(col);
with (document.Form1) {
firstPart = LifeData.value.substr(0, linearPos);
lastPart = LifeData.value.substr(linearPos + 1);
if (LifeData.value.charAt(linearPos) == "0") {
middlePart = "1";
document.getElementById(imgID).src = "alive.gif"; }
else {
middlePart = "0";
document.getElementById(imgID).src = "empty.gif";
}
LifeData.value = firstPart + middlePart + lastPart;
}
}
}
このクライアント側のコードは、
最初にクリックされた画像の ID を検出して、
その ID をサブ文字列に分割します。
このコードでは、ID が "IMG" で始まることを確認し、
次に適切な IMG タグの URL を変更するので、
正しく表示されます。
また、データをサーバー側に正しく返すことができるように、
適切な場所で非表示の入力要素の内容を変更します。
この関数がサーバー側の処理を生成しないことに注意してください。
ここでは、すべての処理がクライアント側で行われます!
JScript には、文字列内の個別の文字を変更する方法がありません。
そのため、変更した文字の直前までの古い文字列、変更した文字、そして残りの古い文字列を連結して、
新しい文字列を作成する必要があります。
つまり、"000X111" 内の文字 ("X") を "Y" に変更する場合、
変更しない最初の 3 文字 "000"、
変更した文字 "Y"、
そして変更しない最後の 3 文字 "111" を連結します。
タイマの動作方法
タイマは完全にクライアント側で実行されます
(もちろん、サーバーに要求して、
新しい盤面を計算する場合は例外です)。
サーバーへの要求を行うことは、
ページが戻ってくるときに JScript のすべての変数がクリアされることを意味します。
そのため、タイマの状態を保持する場合、
要求から要求までの間安定性のある方法でこれを行う必要があります。
このような理由により、
非表示の入力要素を選択します。
非表示の入力要素を切り替えるコード (とその時のタイマの状態) は以下のとおりです。
function ToggleAuto_onclick() {
with (document.Form1) {
if (TimerEnabled.value != "true") { // タイマは実行されていません。
// ポストバック用の非表示フォーム値。
TimerEnabled.value = "true";
// サーバーの処理が遅い場合、ボタンを変更するのが適切です。
ToggleAuto.value = "Stop!!!";
ToggleAuto.style.backgroundColor = "red";
// 最初の世代を実行して、onload で残りの部分を処理します。
RunSingle.click();
}
else {
// 既に更新している場合は変更しません。
if (!TickHappening) {
// タイマをオフにして、参照をクリアにします。
clearTimeout(TimerID);
// ポストバック用の非表示フォーム値。
TimerEnabled.value = "false";
// UI のボタンを変更します。
ToggleAuto.value = "Start repeating";
ToggleAuto.style.backgroundColor = "Lime";
}
}
}
}
ここでは、タイマを実行していない場合 (最初の状態)、
非表示の入力要素に "true" が入るように変更します。
その後、ボタンを停止の状態に変更して、
最初の新しい世代を実行します。
それが終わると、(下記の) OnLoad 関数で、
それ以後の世代を計算するためにタイマを設定します。
OnLoad 関数は次のとおりです。
function window_onload() {
with (document.Form1) {
// ページを読み込みます。タイマがオフになっているので、設定する必要があるか確認します。
if (TimerEnabled.value == "true") {
msec = RepSecs.value * 1000;
if (msec < 2000) { // あまり早くありません。
msec = 2000;
RepSecs.value = "2";
}
// ページの既定値をオーバーライドする必要があります。
ToggleAuto.value = "Stop!!!";
ToggleAuto.style.backgroundColor = "red";
// タイマを設定します。間隔だけには注意してください。
TimerID = setTimeout(TickHandler, msec);
}
}
}
(非表示入力要素により) タイマがオンになっている場合、
タイマに設定する INPUT 要素の値 (最小値は 2000 ミリ秒、つまり 2 秒) に基づいて、
ミリ秒単位で時間を計算します。
(まだ変更していない場合は) ボタンの色およびテキストを変更して、
カウントダウン タイマを設定します。
タイマが起動すると、Tick イベント ハンドラ メソッドが呼び出されます。
function TickHandler() {
with (document.Form1) {
if (TimerEnabled.value == "true") { // 意味のない Tick イベントを除去します。
TickHappening = true;
RunSingle.click();
}
}
}
この関数は主に [Single Step] ボタンをクリックしますが、
その前にタイマがオンであることを確認します。
この確認を行わないと、
タイマをオフにした直後の Tick イベントを実行する可能性があります。
また、ボタン切り替えメソッドで確認できるように、
TickHappening と呼ばれる変数を設定します。
この場合も、レース状態を防ぎます。
その他機能はすべてサーバー上で処理されます。
そのコードのほとんどは、前回の Windows フォーム アプリケーションによく似ていますが、
まったく同じではありません。
借りたもの、そして...
さて、サーバー側のイベントのボタンのいずれかをクリックしたときに何が起こるか見てみましょう。
まず、既に説明した Page_Load イベント ハンドラが呼び出されます。
今回は、取得する配列があるので、
非表示の入力要素に格納された文字列から配列を構築します。
これを解析するコードは次のとおりです。
Private Function GetBoardFromString(ByVal s As String) As Integer(,)
If (s.Length <> rows * cols) Then ' 文字列が存在しません。
Return Nothing
Else
Dim board = New Integer(rows - 1, cols - 1) {}
Dim i, j As Integer
For i = 0 To rows - 1
Dim rowStart As Integer = i * cols
For j = 0 To cols - 1
board(i, j) = _
IIf(s.Chars(rowStart + j) = "0", 0, 1)
Next
Next
Return board
End If
End Function
文字列が存在しない場合、
または文字列の長さが正しくない場合は、
単純に Nothing をエラーとして返します。
それ以外の場合は、配列を作成してから、
文字列を解析して配列の要素を設定します。
次に、ボタンをクリックした場合、
以下のイベント ハンドラのいずれかが呼び出されます。
Private Sub AddRandom_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles AddRandom.Click
FillRandom(NumRnd.Text, False)
End Sub
Private Sub Clear_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Clear.Click
' 配列は OnLoad で作成されません。ここで作成する必要があります!
currBoard = New Integer(rows - 1, cols - 1) {}
End Sub
Private Sub RunSingle_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles RunSingle.Click
currBoard = CalcNext(currBoard)
End Sub
Private Sub RunMult_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles RunMult.Click
Dim num As Integer = NumOfGen.Text
Dim i As Integer
For i = 1 To num
currBoard = CalcNext(currBoard)
Next
End Sub
上記のイベント ハンドラは、
前回実行したほとんどのことを実行します。
ただし、ページは自動的にレンダリングされるので、
イベント ハンドラがルーチンを呼び出して、
レンダリングおよび更新を行う必要はありません。
また、Clear ハンドラは異なる動作を行います。
Load ハンドラで配列を作成し、
その配列をクリアするのではなく、
単に Load ハンドラでの配列の作成を省略して (if ステートメントのテキストを思い出してください) 、
Clear ハンドラで新しく配列を作成します。
最後に、RunMulti ハンドラは新しく作成したもので、
単純にループ内で CalcNext を呼び出します。
青ざめたもの!
実際、ここでは青ざめるようなことはありませんでした。
ただし、ASP.NET とライフ ゲームをもっとすばらしく結び付けるために、
残念ながら HTML ベースのスライダ コントロールが存在しないという事実を認めざるを得ないので、
これにより青ざめることになるでしょう。
デザイン上の選択肢
すべてのアプリケーションでは、
さまざまなデザイン上でいくつか選択を行う必要が生じます。
このアプリケーションの最も明確な選択肢は、
サーバー上で行うことと、クライアント上で行うことを決断することでした。
世代の計算をはじめ、
すべての処理をクライアント上で実行できることは明らかです。
JScript はかなり時間がかかります。
そのため、名医は JScript で生命体の計算すると、
サーバー上でその計算を行うよりも時間がかかると推測しています。
(ただし、Dr. GUI は自分でこれを検証していないことを認めます。
実際に処理の速いデスクトップ上では、
スクリプトを使用する場合でも、クライアント側でこの計算を行う方が速くなることがあります。)
その一方で、盤面の編集をはじめ、
すべてのことをサーバー側で実行できます。
ただし、サーバー側で盤面を編集すると、
変更するたびにサーバーとのやり取りが必要になるので、
"非常に時間がかかります"。
なんということでしょう!
実際には、Clear イベントをサーバーとクライアントのどちらで実行するかは議論の余地があります。
クライアント上でこのイベントを実行することは簡単です。
ただし、既にサーバー側でコードを実行しているので、
ユーザーはその処理が非常に速くなることを期待できません。
そういう訳で、名医はこれをそのままにしておきました。
(壊れていないものを修理してはいけません!)
試してみましょう!

.NET をお持ちの皆さん、
.NET を習得するには実際に試してみることです。
お持ちでない方は、購入を検討してください。
週に 1 時間ほど Dr. GUI .NET と過ごせば、
すぐに .NET Framework の "専門家" になれます!
一番乗りになって、友人を誘いましょう!
いつでも新しいテクノロジを一番乗りで学習するのはすばらしいことです。
しかし、友人と共に学習すればいっそう楽しくなります!
ぜひ友人とグループを作って、一緒に .NET を学習してはいかがでしょうか!
ちょっと試してみましょう...
- まず、ここで示したコードを試してみてください。このコードで遊んでみましょう。
- さまざまなボタンいくつかを追加すると、
面白いことが起こります。
おそらく、盤面に追加できるいくつかのストック パターンのメニューを持つことが考えられます。
- さまざまなデータ構造でこのコードを試して、
その結果を確認します。
まとめと次回予告
今回は、やや戦略的なクライアント側 JScript メソッドを使用して、
ASP.NET アプリケーションとしての Conway のライフ ゲームを作成しました。
次回は、.NET Framework でフォーマッティングと解析の方法について説明します。
|