オセロゲーム
jQueryを使って、オセロゲームを作ってみましょう。人対人で、二人が交互に操作するという想定です。
jQueryの初心者~中級者向けの課題です。(所要時間:2~3時間)
ソースコードは画面の都合、全ソースは記述していません。全ソースが必要な方は、各ソースコード説明のリンクからダウンロードしてください。
図0.オセロゲームの完成図
前提条件
オセロゲームのゲームボードは下記のHTML,CSS,HTMLを使います。
初期段階で中央の4駒はセットしておきます。(図1参照)
図1.オセロゲームのゲームボード
以下に、このHTMLとCSS、そしてjavascriptを示します。
サンプル1 | CSS+HTML+jQuery |
1
2
3
4
5
6
7
8
9
10
11
12
|
<style type="text/css"> * {margin:0; padding:0;} body {width:100%; height:100%;} table#othero { background:#090; border:solid 2px #000; border-collapse:collapse; border-spacing:0px; table-layout:fixed; } table#othero tr td { width:54px; height:54px; border:solid 1px #fff; text-align:center; vertical-align:middle; font-size:300%; } </style> |
1
2
3
4
5
6
7
8
9
10
11
|
$(function() { "use strict"; var _o = {black:"●", white:"○"}; // 盤生成 と、初期駒セット $("#othero td").addClass("empty"); $("td", $("#othero tr").eq(3)).eq(3).removeClass("empty").addClass("white").text(_o.white); $("td", $("#othero tr").eq(4)).eq(4).removeClass("empty").addClass("white").text(_o.white); $("td", $("#othero tr").eq(3)).eq(4).removeClass("empty").addClass("black").text(_o.black); $("td", $("#othero tr").eq(4)).eq(3).removeClass("empty").addClass("black").text(_o.black); }); |
1
2
3
4
5
6
7
8
9
10
11
12
|
<div> <table id="othero"><tbody> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> </tbody></table> </div> |
HTMLは、単に8×8マスのテーブルを作っているだけです。
それでは、javascriptの説明をしましょう。
オセロの全マス目には、マス目が空(カラ)であることを識別する目的でemptyクラスを設定しています。(6行目)
オセロゲームはスタート時は白黒2マスをづつ中央にセットしてます。
この中央4マスは、空でないので、先ほどセットしたemtyクラスを取り除いています。
さらに、この4マスそれぞれに、駒色にあうwhite, blackクラス付加しています。(7-10行目)
この先、マス目の駒判定は、この white, black、そして enpty クラスを使います。
このjavascriptは、初期画面表示時に、1回流れるだけです。
では、このHTML+javascriptをもとに、オセロゲームを作ってみましょう。
オセロ 空いているマス目に交互に駒を打つ
問 題 (1) オセロの駒打ち 空いているマス目に白黒の駒を交互に駒打ちせよ。
サンプル1にjavascript(jQuery)を記述して、白黒駒を交互に、空いているマス目に駒打するようにしてください。
操作はマス目をマウスクリックで行うことにします。
制御は、emptyクラスは利用します。
このemptyクラスが残るマス目は、まだ何も駒打ちされていない空マス目で、駒打ちができる判断になります。
駒打ちされたマス目は、その色にあう、white, black クラスを付加してください。
この段階は、単に順番に駒打ちをすることに専念してください。
オセロ本来のロジック、同色で挟まれた相手駒をひっくり返す処理、また、駒を置くことができる判断処理は、この段階では無視して構いません。
図2.オセロゲーム駒打ち第一ステップ
解 答 (1) jQueryを使ったオセロの駒打ちロジック第一ステップ
jQueryの部分は以下のようになります。
サンプル2 | jQuery |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
$(function() { "use strict"; // 現在の駒色 _bw, 次の色 _bw2, 表示駒文字 _o var _bw = "white", _bw2= "black", _o = {black:"●", white:"○"}, x; // 盤生成 と、初期駒セット $("#othero td").addClass("empty"); $("td", $("#othero tr").eq(3)).eq(3).removeClass("empty").addClass("white").text(_o.white); $("td", $("#othero tr").eq(4)).eq(4).removeClass("empty").addClass("white").text(_o.white); $("td", $("#othero tr").eq(3)).eq(4).removeClass("empty").addClass("black").text(_o.black); $("td", $("#othero tr").eq(4)).eq(3).removeClass("empty").addClass("black").text(_o.black); // 駒打ちロジック $("#othero td").click(function() { // 空地チェック if(!$(this).hasClass("empty")){ alert("すでに駒が打たれています。"); return; } // 駒打ち $(this).removeClass("empty").addClass(_bw).text(_o[_bw]); // 駒チェンジ x = _bw2; _bw2 = _bw; _bw = x; }); }); |
追加されたjavascriptは、以下の十数行です。
駒をマウスクリックされたときの処理を記述します。(17-29行目)
まず、クリックされたマス目が空でなければ、メッセージを表示して終わります。(19-22行目)
次に、実際に駒打ちします。(25行目)
emptyクラスを取りのぞき、whilte, black のいずれかのクラスを付加します。
そして、tdに駒となるテキストを追加します。
現在の、白か黒の順番を管理しているのが、_bw変数です。
自分の駒打ちが終わると、次の駒打ちの順番になるようにします。(28行目)
オセロでは、置くところがないとパスとなるルールで、必ずしも、白黒が交互に打ちとは限りません。
よって、奇数回目が白、偶数回目が黒の順番とは限らないため、単純に、白が駒打ちしたら黒、黒が駒打ちしたら白の手番になるように記述します。
実際のパスロジックは、問題4で取り組みます。
いかがですか、実際にオセロ板に、駒打ちされましたか。
オセロの基礎になるので、クラスの使い方を、しっかりと押さえててください。
次に、オセロの駒で挟んで相手駒をひっくり返すロジックに取り組みます。
問 題 (2) 問題1のjavascriptを改良して、オセロの駒打ち 挟んで相手駒をひっくり返すを実現せよ。
サンプル1を改良して、相手駒を挟んでひっくり返す処理を行ってください。
また、どこに自分の駒が置けるかを正しく判断してください。
この段階では、勝敗判定はまだ行いません。また、パスルールのこともないものとしてください。
次のステップで、この部分を完成させます。
図3.オセロの駒打ちロジック第二ステップ
解 答 (2) javascriptを改良して、オセロの駒打ち 挟んでひっくり返す
サンプル3 | jQuery |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// 駒打ちロジック $("#othero td").click(function() { b = false; // 空地チェック if(!$(this).hasClass("empty")){ alert("すでに駒が打たれています。"); return; } // 押下セル位置取得 row = $("#othero tr").index($(this).parents("tr")); col = $("td", $("#othero tr").eq(row)).index($(this)); // 上方向チェック if(row>=2){ if($("td", $("#othero tr").eq(row-1)).eq(col).hasClass(_bw2)){ for(r=row-2; r>=0; r--){ x = $("td", $("#othero tr").eq(r)).eq(col); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(r++; r<row; r++){ $("td", $("#othero tr").eq(r)).eq(col).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 下方向チェック if(row<=5){ if($("td", $("#othero tr").eq(row+1)).eq(col).hasClass(_bw2)){ for(r=row+2; r<=7; r++){ x = $("td", $("#othero tr").eq(r)).eq(col); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(r--; r>row; r--){ $("td", $("#othero tr").eq(r)).eq(col).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 左方向チェック if(col>=2){ if($("td", $("#othero tr").eq(row)).eq(col-1).hasClass(_bw2)){ for(c=col-2; c>=0; c--){ x = $("td", $("#othero tr").eq(row)).eq(c); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(c++; c<col; c++){ $("td", $("#othero tr").eq(row)).eq(c).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 右方向チェック if(col<=5){ if($("td", $("#othero tr").eq(row)).eq(col+1).hasClass(_bw2)){ for(c=col+2; c<=7; c++){ x = $("td", $("#othero tr").eq(row)).eq(c); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(c--; c>col; c--){ $("td", $("#othero tr").eq(row)).eq(c).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 左上方向チェック if(row>=2 && col>=2){ if($("td", $("#othero tr").eq(row-1)).eq(col-1).hasClass(_bw2)){ for(r=row-2, c=col-2; r>=0 && c>=0; r--, c--){ x = $("td", $("#othero tr").eq(r)).eq(c); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(r++, c++; r<row && c<col; r++, c++){ $("td", $("#othero tr").eq(r)).eq(c).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 右上方向チェック if(row>=2 && col<=7){ if($("td", $("#othero tr").eq(row-1)).eq(col+1).hasClass(_bw2)){ for(r=row-2, c=col+2; r>=0 && c<=7; r--, c++){ x = $("td", $("#othero tr").eq(r)).eq(c); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(r++, c--; r<row && c>col; r++, c--){ $("td", $("#othero tr").eq(r)).eq(c).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 右下方向チェック if(row<=7 && col<=7){ if($("td", $("#othero tr").eq(row+1)).eq(col+1).hasClass(_bw2)){ for(r=row+2, c=col+2; r<=7 && c<=7; r++, c++){ x = $("td", $("#othero tr").eq(r)).eq(c); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(r--, c--; r>row && c>col; r--, c--){ $("td", $("#othero tr").eq(r)).eq(c).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } // 左下方向チェック if(row<=7 && col>=0){ if($("td", $("#othero tr").eq(row+1)).eq(col-1).hasClass(_bw2)){ for(r=row+2, c=col-2; r<=7 && c>=0; r++, c--){ x = $("td", $("#othero tr").eq(r)).eq(c); if(x.hasClass("empty")) break; if(x.hasClass(_bw)){ b = true; for(r--, c++; r>row && c<col; r--, c++){ $("td", $("#othero tr").eq(r)).eq(c).toggleClass(_bw).toggleClass(_bw2).text(_o[_bw]); } break; } } } } if(!b){ alert("ここへは駒が置けません。"); return; } // 駒打ち $(this).removeClass("empty").addClass(_bw).text(_o[_bw]); // 駒チェンジ x = _bw2; _bw2 = _bw; _bw = x; }); |
まず、クリックされたマス目の行(row)と、カラム(col)を変数に保存しています。(12-13行目)
次に、打たれたマス目を基準に上下左右、斜めの駒の状況をみて、駒が打てるか判断します。
このロジックは上下左右、ななめとも同じなので、上方向のチェックロジックで説明します。(16-30行目)
<上方向判定ロジックの説明>
駒打ち可/不可の判断として、変数bを使います。この変数は、上下左右斜め判定を通して使います。(3行目)
説明をわかりやすくするため、現在、白が手番だとします。
上方向駒打ち判定の最初は、
駒を置こうとするマス目が2段目(先頭を0行目とする)より下でなければ、
相手駒を挟むことができないので、上方向判定をしません。(16行目)
次に、これから駒を置こうとするマス目の1つ上のマス目にが相手の黒駒でなくてはなりません。(17行目)
上段が黒であれば、黒駒の連続状況をチェックします。(18行目ループ)
連続状況チェック中に、空マスが現れれば、白で挟むことができないため、駒打ち不可となりループを抜けます。(17行目)
連続チェックするマス目に白駒が置かれていれば、黒駒を挟めるいうことで、駒打ちが可能であると判断されます。(21行目)
駒打ち可の場合、変数bをtrueとします。そして、挟まれた相手の黒駒を白駒ににします。(23-25行目)
相手駒の連続チェックで、最後まで黒駒の連続であれば、白駒で挟めないということで、変数bはfalseのままリープを抜けます。
つまり、この場合も駒打ちできないとなります。
この駒打ち判定を、上下左右・斜めを終えて、変数b=false のままの場合、駒打ち不可のマス目であった判断されたことになります。(144-147行目)
少しアルゴリズム解説になりましたが、わかりましたか。
問 題 (3) 問題2のjavascriptに、勝敗判定を記述せよ。
メッセージを指定領域(div#message)を使い、
試合中は、駒打ちの順番を表示し、最後に、白黒の勝敗判定のメッセージを表示します。
ただし、この段階では、パスのロジックは無視してよい。
図4.オセロのメッセージ表示
1
|
<div id="message">白の順番</div> |
解 答 (3) オセロの勝敗判定表示
問題2から追加があった部分を抜粋して説明します。
サンプル4 | HTML |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
$(function() { (途中略) // 駒打ちロジック $("#othero td").click(function() { (途中略) // 駒チェンジ x = _bw2; _bw2 = _bw; _bw = x; $("#message").text(_o[_bw] + "の順番"); x = $("#othero .empty").length; if(x===0){ x = $("#othero .white").length - $("#othero .black").length; if(x>0){ $("#message").text("白の勝ちです。"); }else if(x<0){ $("#message").text("黒の勝ちです。"); }else{ $("#message").text("引き分けです。"); } } }); }); |
次の手番を促すメッセージを画面表示します。(8行目)
空マスがなくなった時点で勝敗判定を行います。(10-11行目)
白と黒のマス目の数の差を求めて、勝敗判定を行います。(12-19行目)
問 題 (4) オセロのパスルール
パスの判断は人が行うことにして、駒を置くところがなくなった場合、パスをして相手の順番になるようにしてください。
パスは、メッセージの下にボタンをつくり、ボタンを押されたら、パス処理を行なってください。(図5参照)
両社ともパスした場合は、試合終了として、その時点の勝敗判定をしてください。
図5.オセロのパス処理をつくって完成
解 答 (4) オセロのパスの制御
HTLMは、パス用のボタンを追加するだけです。
javascriptは、追加されたパス用ボタンを押した処理を記述します。
サンプル5 | HTML+jQuery |
1
2
3
4
5
6
7
8
9
10
11
|
<table id="othero"><tbody> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> <tr><td/><td/><td/><td/><td/><td/><td/><td/></tr> </tbody></table> <div id="message">白の先手</div><button id="pass">Pass</button> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
$(function() { "use strict"; // 現在の駒色 _bw, 次の色 _bw2, 表示駒文字 _o var _bw = "white", _bw2= "black", _o = {black:"●", white:"○"}, _nPass=0, // パスカウンタ x, b, row, col, r, c; (途中略) $("#pass").click(function() { _nPass++; // 二人が連続パスしたら終了 if(_nPass>=2){ $("#othero .empty").removeClass("empty"); x = $("#othero .white").length - $("#othero .black").length; if(x>0){ $("#message").text("白の勝ちです。"); }else if(x<0){ $("#message").text("黒の勝ちです。"); }else{ $("#message").text("引き分けです。"); } return; } // 駒チェンジ x = _bw2; _bw2 = _bw; _bw = x; $("#message").text(_o[_bw] + "の順番"); }); // 駒打ちロジック $("#othero td").click(function() { (途中略) // パスフラグクリア _nPass=0; (途中略) } }); }); </script> |
パス用ボタンは、HTMLの、オセロのゲームボードtableの後、メッセージ領域の後に、buttonタグを追加します。
パスカウンターを、グローバル変数として定義します。(7行目)
これは、二人が連続してパスをしたという判定に使います。
パスボタンが押されたら(12行目)
相手に順番を譲ります。(27-29行目)
二人が連続してパスしたら(15行目)
ゲーム終了判定を行います。(16-25行目)
なお、パスせずに普通に駒打ちした場合には、パスカウンターをクリアしておきます。(36行目)
ちなみに、二人が連続してパスするケースとは、全マス駒打ちしない段階で、全色同色になる場合です。
今回の問題は、jQueryを使ってオセロゲームを作ってみました。
余裕がある方は、人対コンピュータに挑戦してみませんか?
では、今回は、ここまでとします。
更新日付:2017/07/08 20:35:47