ブログが続かないわけ

この日記のはてなブックマーク数
Webエンジニアが思うこと by junichiro on Facebook

[Perl][JavaScript]JavaScript とPerl で○×ゲームを作った

このエントリーを含むはてなブックマーク hateb
OpenSocial とかで遊ぶためにもJavaScript は必須で、いままで避けて通ろうとしていたのが、そろそろそうは行かなくなってきた。そのため、今月はmixi hackathon もあることだし、それまでにしっかりとJavaScript を身につけるため、js 特訓月とした。

今日は、そのトレーニングの一環として、○×ゲーム(英名: TicTacToe)を作った。
【こちらで遊べます→】TicTacToe

tictactoe.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="css/tictactoe.css">
<script src="lib/jquery/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="lib/tictactoe.js" type="text/javascript"></script>
<title>JavaScript Training (TicTacToe)</title>
</head>
<body>
<h1>TicTacToe</h1>
<div id="field"></div>
<div id="result"></div>
<div id="retry"></div>
</body>
</html>

HTML にはプレースホルダーとなるdiv をいくつか書いただけで、あとはjs で書くことにした。

lib/tictactoe.js
if( typeof( window.TicTacToe ) == "undefined") {

// TicTacToe コンストラクタ
var TicTacToe = function() { return this; };

// Version
TicTacToe.VERSION = '0.01';

// TicTacToe オブジェクトのプロパティ
TicTacToe.prototype.cgiurl = 'tictactoe.cgi';
TicTacToe.prototype.result = '#result';
TicTacToe.prototype.level = '1000';
TicTacToe.prototype.data = [
0, 0, 0,
0, 0, 0,
0, 0, 0,
];
TicTacToe.prototype.lock = '0';

TicTacToe.prototype.init = function ( arg ) {
var __this = this;
__this.data = [0, 0, 0, 0, 0, 0, 0, 0, 0]
$('#result').empty();
$('#retry').empty();
__this.display();
}

TicTacToe.prototype.event_init = function( arg ) {
var __this = this;
$("table.game-field tr td").mouseover(function(e) {
__this.mouseover(e);
});
$("table.game-field tr td").mouseout(function(e) {
__this.clear_color(e);
});
$("table.game-field tr td").click(function(e) {
if( __this.lock == '0' ) {
__this.hit(e);
__this.lock = '1';
}
});
}

// 着手計算結果
TicTacToe.prototype.expr = function ( arg ) {
// CGI に渡すパラメータオブジェクトを生成
var param = {
q: arg,
l: this.level,
// debug: '1'
};
// コールバック関数無いではthis が使えないのでコピー
var __this = this;
// JSON データを受取るコールバック関数
var func = function ( data ) {
// alert( data.debug );
__this.data = data.field;
__this.display( data.field );
$(__this.result).text(data.result);
if( data.status != '0' ) __this.finish( data.status );
__this.lock = '0';
};
// CGI を呼び出して、JSON データを受取る
jQuery.getJSON( this.cgiurl, param, func );
}

TicTacToe.prototype.mouseover = function ( arg ) {
var obj = jQuery(arg.currentTarget);
var txt = obj.text();
if(txt == '') {
obj.css('cursor', 'pointer');
obj.css('background-color', '#FFE7A1');
}
else {
obj.css('cursor', 'no-drop');
}
}

TicTacToe.prototype.clear_color = function ( arg ) {
var obj = jQuery(arg.currentTarget);
obj.css('background-color', '#FFFFFF');
}

TicTacToe.prototype.hit = function ( arg ) {
var td = arg.currentTarget;
if(td.textContent != '') return;
var data = this.data;
var sub = td.id.split('-')[1];
data[sub] = 1;
this.data = data;
this.display( data );
this.expr( data );
}

TicTacToe.prototype.display = function ( arg ) {
$('#field').empty();

var data;
if( arg && arg.length == 9 ) {
data = arg;
}
else {
data = this.data;
}

var field = document.getElementById('field');
var div = document.createElement('div');
var tab = document.createElement('table');
tab.setAttribute('class', 'game-field');

var tr = new Array();
var td = new Array();
for ( var i=0; i<3; i++) {
tr[i] = document.createElement('tr');
for ( var j=0; j<3; j++) {
var sub = i*3+j;
var text;
if(data[sub] == '1') {
text = document.createTextNode('○');
}
else if(data[sub] == '2') {
text = document.createTextNode('×');
}
else {
text = document.createTextNode('');
}
td[j] = document.createElement('td');
td[j].id = 'id-'+sub;
td[j].appendChild(text);
tr[i].appendChild(td[j]);
}
tab.appendChild(tr[i]);
}

div.appendChild(tab);
field.appendChild(div);
this.event_init();
}

TicTacToe.prototype.finish = function( status ) {
var __this = this;
var color = {1: '#DDDDFF', 2: '#FFDDDD', 3: '#DDDDDD'};
$("table.game-field tr td").unbind();
$("table.game-field tr td").css('background-color', color[status]);

var btn = document.createElement('input');
btn.type = 'button';
btn.value = 'リトライ';
jQuery(btn).click(function(e) {
__this.init();
});
var retry = document.getElementById('retry');
retry.appendChild(btn);
}

}

// main
$(document).ready(function(){
var ttt = new TicTacToe();
ttt.init();
});

サーバサイドは、以前モンテカルロ法の調査をしているときに作ったAI で○×ゲームのcomp 側を実装した。
tictactoe.cgi
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use CGI;
use JSON;
use Games::TicTacToe;

my $out = {};
my $cgi = CGI->new();
my @data = $cgi->param('q');
my $level = $cgi->param('l') || '200';

# debug
$cgi->param('debug') and $out->{debug} = Dumper(¥@data);

# initialize
scalar(@data) != 9 and @data = qw/0 0 0 0 0 0 0 0 0/;

# output header
print $cgi->header( -type => 'text/plain', -charset => 'utf-8' );

# calculate
my $ttt = Games::TicTacToe->new(@data);
&is_finished($ttt, $out);
if ( $out->{status} eq '0' ) {
my $result = $ttt->is_finished();
$ttt->level($level);
my $score = $ttt->calc_next_matrix();
my $next_location = $ttt->next_location($score);
$data[$next_location] = $ttt->player;
$ttt->hit($next_location);
&is_finished( $ttt, $out );
}

# response
$out->{field} = ¥@data;

# output json
print JSON::objToJson($out), "¥n";

sub is_finished {
my ($ttt, $out) = @_;
my $result;
if ( $result = $ttt->is_finished() ) {
my %res = ( '1' => 'You win!', '2' => 'You lose...', '3' => 'Draw' );
$out->{result} = $res{$result};
$out->{status} = $result;
}
else {
$out->{result} = '';
$out->{status} = '0';
}
return $out;
}

Games::TicTacToe の中身はgithub にあげてある。
junichiro's games-tictactoe at master - GitHub
この記事のトラックバックURL
http://en.yummy.stripper.jp/trackback/1255043
トラックバック
mixiのアプリカテゴリが変わりましたね
スパイスラボ神部です。 mixi アプリのカテゴリが一気に整理されて、7個になりましたね。 この変更については、mixi Developer Center (ミ...
| ラボブログ | 2009/07/10 12:30 PM |
コメント









関連情報